Can you copy one number that doesn't work here (edit it a bit for privacy), maybe it uses some special character that is not matched?
One of the numbers that doesn't work is shown in the example ![]()
7 917 809‑97‑58
I see. That seems to contain an invisible unicode character 0x202c at the end (you will notice when trying to select or delete only the 8).
You could modify the script to remove that character:
let phoneNumber = clipboardContentString.replace(/[\s\-\(\)\u200B-\u200D\u202C\uFEFF]/g, "");
Nice spot Andreas!
@T-N-T Here is the full updated script. You'll notice that I added:
// set to true to show dialog, set to false to not show the dialog
let debug = true;
at the beginning of the script.
If you want to turn off the dialog to display the summary of the script, set debug to false.
I hope this helps!
async (clipboardContentString) => {
// set to true to show dialog, set to false to not show the dialog
let debug = true;
// Function to display dialog with input, escaping any double quotes
function displayDialog(message) {
let safeMessage = message.replace(/"/g, '\\"'); // Escape double quotes
let debugScript = `
display dialog "${safeMessage}"
`;
runAppleScript(debugScript);
}
// Error handling block
try {
// Remove whitespace, dashes, parentheses, and invisible unicode characters
let phoneNumber = clipboardContentString.replace(/[\s\-\(\)\u200B-\u200D\u202C\uFEFF]/g, "");
// Regex check for valid phone number format (example: international format)
let isValidPhoneNumber = /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
// Stop execution if phone number is not valid
if (!isValidPhoneNumber) {
displayDialog(`Invalid phone number format: ${phoneNumber}. Execution stopped.`);
return phoneNumber;
}
// Escape any special characters to prevent command injection
let escapedPhoneNumber = phoneNumber.replace(/[^a-zA-Z0-9+]/g, '');
// Construct the shell command with the escaped phone number
let script = `/usr/bin/open whatsapp://send\?phone=${escapedPhoneNumber}`;
if (debug == true) {
// Use AppleScript with better readability and extra new lines between key-value pairs
let appleScript = `
set dialogText to "Clipboard Content:\n" & "${clipboardContentString}" & "\n\n" & \
"\nFormatted Phone Number:\n" & "${phoneNumber}" & "\n\n" & \
"\nIs Valid Phone Number:\n" & "${isValidPhoneNumber}" & "\n\n" & \
"\nShell Script:\n" & "${script}"
display dialog dialogText
`;
// Execute the AppleScript
runAppleScript(appleScript);
};
// Execute the shell command
runShellScript({ script });
return phoneNumber;
} catch (error) {
// Display dialog with the last variable that caused an error
displayDialog(`Error encountered: ${error.message}`);
throw error; // Re-throw the error for any higher-level handling
}
};
Guys, would it be too much trouble for you to send me the entire script so I can paste it into the program?
I'm not very good with programming at all...
I copied what you sent above, pasted it into the program, and now it crashes )
@T-N-T Here's a new and improved version of the script that should work:
async (clipboardContentString) => {
// Set to true to show dialog, set to false to not show the dialog
let debug = false;
// Function to display dialog with input, escaping any double quotes
function displayDialog(message) {
let safeMessage = message.replace(/"/g, '\\"'); // Escape double quotes
let debugScript = `
display dialog "${safeMessage}"
`;
runAppleScript(debugScript);
}
// Error handling block
try {
// Remove all characters except '+' and digits
let phoneNumber = clipboardContentString.replace(/[^+\d]/g, "");
// Regex check for valid phone number format (E.164 international format)
let isValidPhoneNumber = /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
// Stop execution if phone number is not valid
if (!isValidPhoneNumber) {
displayDialog(`Invalid phone number format: ${phoneNumber}. Execution stopped.`);
return phoneNumber;
}
// Construct the shell command with the cleaned phone number
let script = `/usr/bin/open whatsapp://send?phone=${phoneNumber}`;
if (debug) {
// Use AppleScript with better readability and extra new lines between key-value pairs
let appleScript = `
set dialogText to "Clipboard Content:\n" & "${clipboardContentString}" & "\n\n" & \
"Formatted Phone Number:\n" & "${phoneNumber}" & "\n\n" & \
"Is Valid Phone Number:\n" & "${isValidPhoneNumber}" & "\n\n" & \
"Shell Script:\n" & "${script}"
display dialog dialogText
`;
// Execute the AppleScript
runAppleScript(appleScript);
}
// Execute the shell command
runShellScript({ script });
return phoneNumber;
} catch (error) {
// Display dialog with the error message
displayDialog(`Error encountered: ${error.message}`);
throw error; // Re-throw the error for any higher-level handling
}
};
This one works ![]()
And in Google Sheets and everywhere, thank you very much ![]()
Guys, tell me, is it difficult to add to this script the ability to perform an action also from the selected text?
That is, everything is the same, only not from the clipboard, but from the selected text.
Previously (before WhatsApp was updated) both options worked.
When I used the "%@" function
Are you using the "Transform & Copy Selection With Java Script" action like described here Paste from clipboard with a specific variable - #2 by Andreas_Hegenberg ?
That should work with the selected text
You are right! There was an option, not like in your example.
Now everything works and with selection!
You are very cool guys! Thank you very much ![]()
This saves me a lot of time and nerves )))
Found where the script doesn't work.
Let's say we right-click on the phone number from this site:
https://bamper.by/zapchast_dvigatel/50315-B462580995/
And select "copy phone number" (FireFox browser)
Run the script and get: Invalid phone number format: . Execution stopped.
At the same time, if after "copy phone number" we paste this text into some editor, then from there this number will work according to the script.
It's probably easier to shoot a video, but is it possible to post it somewhere here?
And another thing.
Previously, I could drop something into the clipboard, then select the phone number, run the script and paste the text from the clipboard into the opened WhatsApp chat.
Now, if I copy some text into the clipboard, then select the phone number, run the script, then the phone number is pasted into the clipboard.
@T-N-T I updated the script to make it works like this:
If there is text selected, check if it's a valid phone number. If yes, then open it in WhatsApp. If no, then check if the clipboard content is a valid phone number. If yes, then open it in WhatsApp. If no, then display an error.
To make this script work, the Action type must be:
Run Real JavaScript
It will not work if the Action type is:
Transform & Copy Selection With Java Script, or;
Transform Clipboard Contents with JavaScript
(async function () {
// Enable or disable debug mode (set to false to disable debug dialogs)
const DEBUG = true;
/**
* Function to display a dialog box using AppleScript.
* This is used to show messages to the user.
* @param {string} title - The title of the dialog box.
* @param {string} message - The message content to display.
*/
async function showDialog(title, message) {
// Prepare the AppleScript command to display a dialog
const appleScript = `display dialog ${JSON.stringify(
message
)} with title ${JSON.stringify(
title
)} buttons {"OK"} default button "OK"`;
// Execute the AppleScript command to show the dialog
await runAppleScript(appleScript);
}
/**
* Function to validate a phone number against the E.164 international format.
* E.164 is an international standard for phone numbers.
* @param {string} phoneNumber - The phone number to validate.
* @returns {boolean} - Returns true if the phone number is valid, false otherwise.
*/
function isValidE164(phoneNumber) {
// Regular expression to match E.164 phone numbers
return /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
}
/**
* Function to extract a valid phone number from input text.
* It sanitizes the input by removing unwanted characters and checks if it's a valid phone number.
* @param {string} input - The input text that may contain a phone number.
* @returns {string|null} - Returns the sanitized phone number if valid, or null if invalid.
*/
function extractValidPhoneNumber(input) {
// If input is null or empty, return null
if (!input) return null;
// Remove all characters except '+' and digits
const sanitized = input.replace(/[^+\d]/g, "");
// Check if the sanitized number is a valid E.164 phone number
return isValidE164(sanitized) ? sanitized : null;
}
/**
* Function to get a valid phone number from content.
* @param {string} content - The content to extract the phone number from.
* @param {string} sourceLabel - A label indicating the source ("Selection" or "Clipboard").
* @returns {{phoneNumber: string, source: string, content: string} | null} - Returns an object with the phone number, source, and content, or null if not found.
*/
function getPhoneNumberFromContent(content, sourceLabel) {
// Attempt to extract a valid phone number from the content
const phoneNumber = extractValidPhoneNumber(content);
if (phoneNumber) {
// Return the phone number, source label, and the original content
return { phoneNumber, source: sourceLabel, content: content };
} else {
// Return null if no valid phone number is found
return null;
}
}
try {
// Step 1: Retrieve the clipboard content first
// This ensures that BTT updates any internal states related to the clipboard and selection
const clipboardContent = await callBTT("get_clipboard_content", {});
// Step 2: Retrieve the selected text from BTT's variables
const selectionContent = await get_string_variable({
variable_name: "selected_text",
});
// Step 3: Attempt to get the phone number from the selection
const selectionResult = getPhoneNumberFromContent(
selectionContent,
"Selection"
);
// Step 4: If not found in selection, attempt to get from clipboard
const finalResult =
selectionResult ||
getPhoneNumberFromContent(clipboardContent, "Clipboard");
// Step 5: If no valid phone number is found, show an error dialog
if (!finalResult) {
await showDialog(
"Invalid Input",
`No valid phone number found.
Selection Content:
${selectionContent || "[No Selection]"}
Clipboard Content:
${clipboardContent || "[Clipboard is Empty]"}`
);
// Exit the script since there's no valid phone number to proceed with
return;
}
// Step 6: If debug mode is enabled, show debug information to the user
if (DEBUG) {
await showDialog(
"Debug Information",
`Source: ${finalResult.source}
Phone Number: ${finalResult.phoneNumber}`
);
}
// Step 7: Open WhatsApp with the valid phone number
// This constructs a shell command to open WhatsApp using the 'whatsapp://' URL scheme
const shellScriptOpenWhatsApp = `/usr/bin/open "whatsapp://send?phone=${finalResult.phoneNumber}"`;
// Execute the shell command to open WhatsApp
await runShellScript({ script: shellScriptOpenWhatsApp });
// Step 8: Return the valid phone number to BTT (optional)
// This can be used if you need to pass the phone number back to BTT for further actions
returnToBTT(finalResult.phoneNumber);
} catch (error) {
// If any error occurs during the execution of the script, show an error dialog to the user
await showDialog("Script Error", `An error occurred: ${error.message}`);
}
})();
Вроде бы пока работает, нужно тестировать.
Подскажите, как то можно убрать это окно?

English reply
I made some further modifications to the code to simplify it. I hope you'll be able to understand how it works.
👈 Click to see updated code (English version):
/**
* Script to Open WhatsApp with a Selected or Copied Phone Number
*
* **Description:**
* This script extracts a phone number from either the selected text or the clipboard,
* validates it according to the E.164 international format, and then opens WhatsApp
* to send a message to that number.
*
* **How to Use:**
* - Set up this script in BetterTouchTool (BTT) as a **"Run Real JavaScript"** action.
* - **Important:** This script will **not** work with other action types like
* "Transform & Copy Selection With JavaScript" or "Transform Clipboard Contents with JavaScript".
* - To use the script:
* 1. Select a phone number in any application, or copy it to the clipboard.
* 2. Trigger the BTT action that runs this script.
*
* **Turning Debug Mode On/Off:**
* - The `DEBUG` variable controls whether debug dialogs are shown.
* - Set `DEBUG = true` to enable debug mode (shows additional dialogs with information).
* - Set `DEBUG = false` to disable debug mode (no debug dialogs will appear).
*
* **Requirements:**
* - BetterTouchTool (BTT) version **4.867** or later.
* - WhatsApp MacOS app.
*
* **Author:** https://community.folivora.ai/u/fortred2
* **Date:** 2024-11-14
*/
(async function () {
// Enable or disable debug mode.
// Set to 'true' to show debug dialogs, or 'false' to hide them.
const DEBUG = false;
/**
* Displays a dialog box using AppleScript to show messages to the user.
* @param {string} title - The title of the dialog box.
* @param {string} message - The message content to display.
*/
async function showDialog(title, message) {
// Prepare the AppleScript command to display a dialog
const appleScript = `display dialog ${JSON.stringify(
message
)} with title ${JSON.stringify(
title
)} buttons {"OK"} default button "OK"`;
// Execute the AppleScript command to show the dialog
await runAppleScript(appleScript);
}
/**
* Validates a phone number against the E.164 international format.
* @param {string} phoneNumber - The phone number to validate.
* @returns {boolean} True if the phone number is valid, false otherwise.
*/
function isValidE164(phoneNumber) {
// Regular expression to match E.164 phone numbers
return /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
}
/**
* Extracts a valid phone number from input text.
* Removes unwanted characters and checks if the number is valid.
* @param {string} input - The input text that may contain a phone number.
* @returns {string|null} The sanitized phone number if valid, or null if not.
*/
function extractValidPhoneNumber(input) {
// If input is null or empty, return null
if (!input) return null;
// Remove all characters except '+' and digits
const sanitized = input.replace(/[^+\d]/g, "");
// Check if the sanitized number is valid according to E.164 format
return isValidE164(sanitized) ? sanitized : null;
}
/**
* Attempts to extract a valid phone number from the provided content.
* @param {string} content - The content to extract the phone number from.
* @param {string} sourceLabel - Indicates the source ("Selection" or "Clipboard").
* @returns {Object|null} An object with the phone number, source, and content, or null if not found.
*/
function getPhoneNumberFromContent(content, sourceLabel) {
// Attempt to extract a valid phone number from the content
const phoneNumber = extractValidPhoneNumber(content);
if (phoneNumber) {
// Return the phone number, source label, and the original content
return { phoneNumber, source: sourceLabel, content: content };
} else {
// Return null if no valid phone number is found
return null;
}
}
try {
// Note: The following functions are provided by the BetterTouchTool (BTT) JavaScript environment:
// - callBTT
// - get_string_variable
// - runAppleScript
// - runShellScript
// - returnToBTT
// Step 1: Retrieve the clipboard content BEFORE getting 'selected_text'
const clipboardContent = await callBTT("get_clipboard_content", {});
// Step 2: Retrieve the selected text from BTT's variables
const selectionContent = await get_string_variable({
variable_name: "selected_text",
});
// Step 3: Attempt to get the phone number from the selection
const selectionResult = getPhoneNumberFromContent(
selectionContent,
"Selection"
);
// Step 4: If not found in selection, attempt to get from clipboard
const finalResult =
selectionResult ||
getPhoneNumberFromContent(clipboardContent, "Clipboard");
// Step 5: If no valid phone number is found, show an error dialog
if (!finalResult) {
await showDialog(
"Invalid Input",
`No valid phone number found.
Selection Content:
${selectionContent || "[No Selection]"}
Clipboard Content:
${clipboardContent || "[Clipboard is Empty]"}`
);
// Exit the script since there's no valid phone number to proceed with
return;
}
// Step 6: If debug mode is enabled, show debug information to the user
if (DEBUG) {
await showDialog(
"Debug Information",
`Source: ${finalResult.source}
Phone Number: ${finalResult.phoneNumber}`
);
}
// Step 7: Open WhatsApp with the valid phone number
// Constructs a shell command to open WhatsApp using the 'whatsapp://' URL scheme
const shellScriptOpenWhatsApp = `/usr/bin/open "whatsapp://send?phone=${finalResult.phoneNumber}"`;
// Execute the shell command to open WhatsApp
await runShellScript({ script: shellScriptOpenWhatsApp });
// Step 8: Return the valid phone number to BTT
// This is required for BTT to receive the phone number for further actions
returnToBTT(finalResult.phoneNumber);
} catch (error) {
// If any error occurs during execution, show an error dialog to the user
await showDialog("Script Error", `An error occurred: ${error.message}`);
}
})();
Let me know if you encounter any issues or bugs with the JavaScript. If you encounter an issue or bug, it would help me a lot if you shared a screen recordings. This makes identifying the cause a lot easier for me.
You can remove the debug window by setting DEBUG to false instead of true in the script, like this:
const DEBUG = false;
ChatGPT helped me translate the message and the JavaScript into Russian. I hope this helps you understand the code. Please see below the Russian version of the JavaScript. I did some testing with both English and Russian versions and they appear to be working correctly.
Russian translation
Я внес некоторые дополнительные изменения в код, чтобы упростить его. Надеюсь, вы сможете понять, как он работает.
👈 Click to see updated code (Russian version):
/**
* Скрипт для открытия WhatsApp с выбранным или скопированным номером телефона
*
* **Описание:**
* Этот скрипт извлекает номер телефона из выделенного текста или буфера обмена,
* проверяет его соответствие международному формату E.164, а затем открывает WhatsApp
* для отправки сообщения на этот номер.
*
* **Как использовать:**
* - Настройте этот скрипт в BetterTouchTool (BTT) как действие **"Run Real JavaScript"**.
* - **Важно:** Этот скрипт **не** будет работать с другими типами действий, такими как
* "Transform & Copy Selection With JavaScript" или "Transform Clipboard Contents with JavaScript".
* - Чтобы использовать скрипт:
* 1. Выделите номер телефона в любом приложении или скопируйте его в буфер обмена.
* 2. Запустите действие BTT, которое выполняет этот скрипт.
*
* **Включение и отключение режима отладки:**
* - Переменная `DEBUG` контролирует, отображаются ли отладочные диалоги.
* - Установите `DEBUG = true`, чтобы включить режим отладки (показывает дополнительные диалоги с информацией).
* - Установите `DEBUG = false`, чтобы отключить режим отладки (отладочные диалоги не будут отображаться).
*
* **Требования:**
* - BetterTouchTool (BTT) версии **4.867** или выше.
* - WhatsApp для macOS.
*
* **Автор:** https://community.folivora.ai/u/fortred2
* **Дата:** 2024-11-14
*/
(async function () {
// Включить или отключить режим отладки.
// Установите 'true', чтобы показать отладочные диалоги, или 'false', чтобы скрыть их.
const DEBUG = false;
/**
* Отображает диалоговое окно с помощью AppleScript, чтобы показать сообщения пользователю.
* @param {string} title - Заголовок диалогового окна.
* @param {string} message - Содержимое сообщения для отображения.
*/
async function showDialog(title, message) {
// Подготовить команду AppleScript для отображения диалога
const appleScript = `display dialog ${JSON.stringify(
message
)} with title ${JSON.stringify(
title
)} buttons {"OK"} default button "OK"`;
// Выполнить команду AppleScript для показа диалога
await runAppleScript(appleScript);
}
/**
* Проверяет номер телефона на соответствие международному формату E.164.
* @param {string} phoneNumber - Номер телефона для проверки.
* @returns {boolean} True, если номер телефона действителен, иначе false.
*/
function isValidE164(phoneNumber) {
// Регулярное выражение для соответствия номерам телефона в формате E.164
return /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
}
/**
* Извлекает действительный номер телефона из входного текста.
* Удаляет ненужные символы и проверяет, является ли номер действительным.
* @param {string} input - Входной текст, который может содержать номер телефона.
* @returns {string|null} Очищенный номер телефона, если он действителен, или null, если нет.
*/
function extractValidPhoneNumber(input) {
// Если входные данные пусты или равны null, вернуть null
if (!input) return null;
// Удалить все символы, кроме '+' и цифр
const sanitized = input.replace(/[^+\d]/g, "");
// Проверить, является ли очищенный номер действительным по формату E.164
return isValidE164(sanitized) ? sanitized : null;
}
/**
* Пытается извлечь действительный номер телефона из предоставленного содержимого.
* @param {string} content - Содержимое для извлечения номера телефона.
* @param {string} sourceLabel - Указывает источник ("Выделение" или "Буфер обмена").
* @returns {Object|null} Объект с номером телефона, источником и содержимым, или null, если не найдено.
*/
function getPhoneNumberFromContent(content, sourceLabel) {
// Попытаться извлечь действительный номер телефона из содержимого
const phoneNumber = extractValidPhoneNumber(content);
if (phoneNumber) {
// Вернуть номер телефона, метку источника и оригинальное содержимое
return { phoneNumber, source: sourceLabel, content: content };
} else {
// Вернуть null, если действительный номер телефона не найден
return null;
}
}
try {
// Примечание: Следующие функции предоставляются средой JavaScript BetterTouchTool (BTT):
// - callBTT
// - get_string_variable
// - runAppleScript
// - runShellScript
// - returnToBTT
// Шаг 1: Получить содержимое буфера обмена ПЕРЕД получением 'selected_text'
const clipboardContent = await callBTT("get_clipboard_content", {});
// Шаг 2: Получить выделенный текст из переменных BTT
const selectionContent = await get_string_variable({
variable_name: "selected_text",
});
// Шаг 3: Попытаться получить номер телефона из выделения
const selectionResult = getPhoneNumberFromContent(
selectionContent,
"Выделение"
);
// Шаг 4: Если в выделении не найдено, попытаться получить из буфера обмена
const finalResult =
selectionResult ||
getPhoneNumberFromContent(clipboardContent, "Буфер обмена");
// Шаг 5: Если действительный номер телефона не найден, показать сообщение об ошибке
if (!finalResult) {
await showDialog(
"Неверный ввод",
`Действительный номер телефона не найден.
Содержимое выделения:
${selectionContent || "[Нет выделения]"}
Содержимое буфера обмена:
${clipboardContent || "[Буфер обмена пуст]"}`
);
// Выйти из скрипта, так как нет действительного номера телефона для продолжения
return;
}
// Шаг 6: Если режим отладки включен, показать отладочную информацию пользователю
if (DEBUG) {
await showDialog(
"Отладочная информация",
`Источник: ${finalResult.source}
Номер телефона: ${finalResult.phoneNumber}`
);
}
// Шаг 7: Открыть WhatsApp с действительным номером телефона
// Составляет команду shell для открытия WhatsApp с использованием схемы URL 'whatsapp://'
const shellScriptOpenWhatsApp = `/usr/bin/open "whatsapp://send?phone=${finalResult.phoneNumber}"`;
// Выполнить команду shell для открытия WhatsApp
await runShellScript({ script: shellScriptOpenWhatsApp });
// Шаг 8: Вернуть действительный номер телефона в BTT
// Это требуется, чтобы BTT получил номер телефона для дальнейших действий
returnToBTT(finalResult.phoneNumber);
} catch (error) {
// Если во время выполнения происходит ошибка, показать пользователю диалоговое окно с ошибкой
await showDialog("Ошибка скрипта", `Произошла ошибка: ${error.message}`);
}
})();
Сообщите мне, если вы столкнетесь с какими-либо проблемами или ошибками в JavaScript. Если возникнет проблема или ошибка, мне очень поможет, если вы поделитесь записями экрана. Это значительно облегчит определение причины.
Вы можете убрать окно отладки, установив DEBUG в значение false вместо true в скрипте следующим образом:
const DEBUG = false;
Я внес некоторые дополнительные изменения в код, чтобы сделать это проще. Надеюсь, вы поймете, как он работает.
ChatGPT помог мне перевести это сообщение и JavaScript на русский язык. Надеюсь, это поможет вам понять код.
Я провел некоторое обсуждение как с английской, так и с русской версией JavaScript, и они, кажется, работают правильно.
Thank you very much.
My first impression is that everything works.
I will test it and give you feedback ![]()
Hi guys.
Please help me with the same script, but for Telegram.
It's written through their website using this command, for example:
tg://resolve?domain=375296688772
Hi @T-N-T. I hope you are well.
This script should work for Telegram:
/**
* Script to Open Telegram with a Selected or Copied Phone Number
*
* **Description:**
* This script extracts a phone number from either the selected text or the clipboard,
* validates it according to the E.164 international format, and then opens Telegram
* to send a message to that number.
*
* **How to Use:**
* - Set up this script in BetterTouchTool (BTT) as a **"Run Real JavaScript"** action.
* - **Important:** This script will **not** work with other action types like
* "Transform & Copy Selection With JavaScript" or "Transform Clipboard Contents with JavaScript".
* - To use the script:
* 1. Select a phone number in any application, or copy it to the clipboard.
* 2. Trigger the BTT action that runs this script.
*
* **Turning Debug Mode On/Off:**
* - The `DEBUG` variable controls whether debug dialogs are shown.
* - Set `DEBUG = true` to enable debug mode (shows additional dialogs with information).
* - Set `DEBUG = false` to disable debug mode (no debug dialogs will appear).
*
* **Requirements:**
* - BetterTouchTool (BTT) version **4.867** or later.
* - Telegram MacOS app.
*
* **Author:** https://community.folivora.ai/u/fortred2
* **Date:** 2025-12-02
*/
(async function () {
// Enable or disable debug mode.
// Set to 'true' to show debug dialogs, or 'false' to hide them.
const DEBUG = false;
/**
* Displays a dialog box using AppleScript to show messages to the user.
* @param {string} title - The title of the dialog box.
* @param {string} message - The message content to display.
*/
async function showDialog(title, message) {
// Prepare the AppleScript command to display a dialog
const appleScript = `display dialog ${JSON.stringify(
message
)} with title ${JSON.stringify(
title
)} buttons {"OK"} default button "OK"`;
// Execute the AppleScript command to show the dialog
await runAppleScript(appleScript);
}
/**
* Validates a phone number against the E.164 international format.
* @param {string} phoneNumber - The phone number to validate.
* @returns {boolean} True if the phone number is valid, false otherwise.
*/
function isValidE164(phoneNumber) {
// Regular expression to match E.164 phone numbers
return /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
}
/**
* Extracts a valid phone number from input text.
* Removes unwanted characters and checks if the number is valid.
* @param {string} input - The input text that may contain a phone number.
* @returns {string|null} The sanitized phone number if valid, or null if not.
*/
function extractValidPhoneNumber(input) {
// If input is null or empty, return null
if (!input) return null;
// Remove all characters except '+' and digits
const sanitized = input.replace(/[^+\d]/g, "");
// Check if the sanitized number is valid according to E.164 format
return isValidE164(sanitized) ? sanitized : null;
}
/**
* Attempts to extract a valid phone number from the provided content.
* @param {string} content - The content to extract the phone number from.
* @param {string} sourceLabel - Indicates the source ("Selection" or "Clipboard").
* @returns {Object|null} An object with the phone number, source, and content, or null if not found.
*/
function getPhoneNumberFromContent(content, sourceLabel) {
// Attempt to extract a valid phone number from the content
const phoneNumber = extractValidPhoneNumber(content);
if (phoneNumber) {
// Return the phone number, source label, and the original content
return { phoneNumber, source: sourceLabel, content: content };
} else {
// Return null if no valid phone number is found
return null;
}
}
try {
// Note: The following functions are provided by the BetterTouchTool (BTT) JavaScript environment:
// - callBTT
// - get_string_variable
// - runAppleScript
// - runShellScript
// - returnToBTT
// Step 1: Retrieve the clipboard content BEFORE getting 'selected_text'
const clipboardContent = await callBTT("get_clipboard_content", {});
// Step 2: Retrieve the selected text from BTT's variables
const selectionContent = await get_string_variable({
variable_name: "selected_text",
});
// Step 3: Attempt to get the phone number from the selection
const selectionResult = getPhoneNumberFromContent(
selectionContent,
"Selection"
);
// Step 4: If not found in selection, attempt to get from clipboard
const finalResult =
selectionResult ||
getPhoneNumberFromContent(clipboardContent, "Clipboard");
// Step 5: If no valid phone number is found, show an error dialog
if (!finalResult) {
await showDialog(
"Invalid Input",
`No valid phone number found.
Selection Content:
${selectionContent || "[No Selection]"}
Clipboard Content:
${clipboardContent || "[Clipboard is Empty]"}`
);
// Exit the script since there's no valid phone number to proceed with
return;
}
// Step 6: If debug mode is enabled, show debug information to the user
if (DEBUG) {
await showDialog(
"Debug Information",
`Source: ${finalResult.source}
Phone Number: ${finalResult.phoneNumber}`
);
}
// Step 7: Open Telegram with the valid phone number
// Constructs a shell command to open Telegram using the 'tg://' URL scheme
const shellScriptOpenTelegram = `/usr/bin/open "tg://resolve?domain=${finalResult.phoneNumber}"`;
// Execute the shell command to open Telegram
await runShellScript({ script: shellScriptOpenTelegram });
// Step 8: Return the valid phone number to BTT
// This is required for BTT to receive the phone number for further actions
returnToBTT(finalResult.phoneNumber);
} catch (error) {
// If any error occurs during execution, show an error dialog to the user
await showDialog("Script Error", `An error occurred: ${error.message}`);
}
})();
Here's the same script but with the documentation and comments translated into Russian (used ChatGPT for the translation).
/**
* Скрипт для открытия Telegram с выбранным или скопированным номером телефона
*
* **Описание:**
* Этот скрипт извлекает номер телефона из выделенного текста или буфера обмена,
* проверяет его соответствие международному формату E.164 и затем открывает Telegram
* для отправки сообщения на этот номер.
*
* **Как использовать:**
* - Настройте этот скрипт в BetterTouchTool (BTT) как действие **"Run Real JavaScript"**.
* - **Важно:** Скрипт **не** будет работать с другими типами действий, такими как
* "Transform & Copy Selection With JavaScript" или "Transform Clipboard Contents with JavaScript".
* - Чтобы использовать скрипт:
* 1. Выделите номер телефона в любом приложении или скопируйте его в буфер обмена.
* 2. Запустите действие BTT, которое выполняет этот скрипт.
*
* **Включение и отключение режима отладки:**
* - Переменная `DEBUG` управляет отображением отладочных диалогов.
* - Установите `DEBUG = true`, чтобы включить режим отладки (показывает дополнительные диалоги).
* - Установите `DEBUG = false`, чтобы отключить режим отладки (диалоги не отображаются).
*
* **Требования:**
* - BetterTouchTool (BTT) версии **4.867** или новее.
* - Приложение Telegram для macOS.
*
* **Автор:** https://community.folivora.ai/u/fortred2
* **Дата:** 2025-12-02
*/
(async function () {
// Включить или отключить режим отладки.
// Установите 'true' чтобы показывать диалоги, или 'false' чтобы скрыть их.
const DEBUG = false;
/**
* Показывает диалоговое окно с помощью AppleScript.
* @param {string} title - Заголовок диалогового окна.
* @param {string} message - Текст сообщения.
*/
async function showDialog(title, message) {
// Подготовить команду AppleScript для отображения диалога
const appleScript = `display dialog ${JSON.stringify(
message
)} with title ${JSON.stringify(
title
)} buttons {"OK"} default button "OK"`;
// Выполнить AppleScript
await runAppleScript(appleScript);
}
/**
* Проверяет номер телефона на соответствие формату E.164.
* @param {string} phoneNumber - Номер телефона для проверки.
* @returns {boolean} true, если номер корректный; иначе false.
*/
function isValidE164(phoneNumber) {
// Регулярное выражение для проверки номеров в формате E.164
return /^\+?[1-9]\d{1,14}$/.test(phoneNumber);
}
/**
* Извлекает корректный номер телефона из текста.
* Удаляет лишние символы и проверяет валидность номера.
* @param {string} input - Текст, который может содержать номер телефона.
* @returns {string|null} Очищенный номер, если он валиден; иначе null.
*/
function extractValidPhoneNumber(input) {
if (!input) return null;
// Удалить все символы, кроме '+' и цифр
const sanitized = input.replace(/[^+\d]/g, "");
// Проверить валидность номера по E.164
return isValidE164(sanitized) ? sanitized : null;
}
/**
* Пытается извлечь корректный номер телефона из заданного содержимого.
* @param {string} content - Содержимое, из которого извлекается номер.
* @param {string} sourceLabel - Источник ("Selection" или "Clipboard").
* @returns {Object|null} Объект с номером и источником или null.
*/
function getPhoneNumberFromContent(content, sourceLabel) {
const phoneNumber = extractValidPhoneNumber(content);
if (phoneNumber) {
return { phoneNumber, source: sourceLabel, content: content };
} else {
return null;
}
}
try {
// Эти функции предоставляет среда JavaScript в BTT:
// - callBTT
// - get_string_variable
// - runAppleScript
// - runShellScript
// - returnToBTT
// Шаг 1: получить содержимое буфера обмена ДО получения 'selected_text'
const clipboardContent = await callBTT("get_clipboard_content", {});
// Шаг 2: получить выделенный текст
const selectionContent = await get_string_variable({
variable_name: "selected_text",
});
// Шаг 3: поиск номера в выделении
const selectionResult = getPhoneNumberFromContent(
selectionContent,
"Selection"
);
// Шаг 4: если не найдено — попробовать буфер обмена
const finalResult =
selectionResult ||
getPhoneNumberFromContent(clipboardContent, "Clipboard");
// Шаг 5: если номер не найден — показать ошибку
if (!finalResult) {
await showDialog(
"Некорректные данные",
`Не найден корректный номер телефона.
Выделенный текст:
${selectionContent || "[Нет выделения]"}
Буфер обмена:
${clipboardContent || "[Пусто]"}`
);
return;
}
// Шаг 6: если включён DEBUG — показать отладочную информацию
if (DEBUG) {
await showDialog(
"Отладочная информация",
`Источник: ${finalResult.source}
Номер телефона: ${finalResult.phoneNumber}`
);
}
// Шаг 7: открыть Telegram с номером
const shellScriptOpenTelegram = `/usr/bin/open "tg://resolve?domain=${finalResult.phoneNumber}"`;
await runShellScript({ script: shellScriptOpenTelegram });
// Шаг 8: вернуть номер в BTT
returnToBTT(finalResult.phoneNumber);
} catch (error) {
await showDialog("Ошибка скрипта", `Произошла ошибка: ${error.message}`);
}
})();
Good afternoon.
It works if the number looks like this: 375291555201
It doesn't work if the number looks like this:
- +375(29)155-52-01
- +375(29)1555201
@T-N-T The root cause is that Telegram doesn't accept a + character at the beginning of the phone number. The extractValidPhoneNumber function needs to be updated to remove + characters. Replace the current extractValidPhoneNumber function with this:
/**
* Extracts a valid phone number from input text.
* Removes '+' characters and all non-digit characters, then validates.
* @param {string} input - The input text that may contain a phone number.
* @returns {string|null} The sanitized phone number if valid, or null if not.
*/
function extractValidPhoneNumber(input) {
if (!input) return null;
// Remove all non-digit characters (this also removes any '+')
const sanitized = input.replace(/\D+/g, "");
// If nothing left after cleaning, it's not a valid number
if (!sanitized) return null;
// Validate against E.164 *without* '+' (regex allows an optional '+')
return isValidE164(sanitized) ? sanitized : null;
}