ИП Горелов Максим Николаевич 8 (915) 093-74-75
js скрипт - voximplant Входящий звонок
require(Modules.OpenAI); require(Modules.ElevenLabs); const bot_id = 12345; //УКАЖИТЕ НОМЕР БОТА const bot_token = 'xxxxxxxxxxxxxxxx'; //УКАЖИТЕ ТОКЕН БОТА const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15); const chat_id = generateChatId('vox_'); const OPENAI_VOICE_SETTINGS = { speed: 1.2, //Диапазон: 0.25 (очень медленно) – 4.0 (очень быстро) temperature: 1.2, // Диапазон: 0.0 (строгий) – 1.2 (креативный) pitch: 1.05, stability: 1.0, // (стабильность голоса) Диапазон: 0.0 до 1.0 управляет насколько предсказуемо и плавно звучит голос. 0.3 - Голос с вариативной интонацией. 1.0 - Максимально однотонный similarity_boost: 0.1 // (похожесть на голос-оригинал) Диапазон: 0.0 до 1.0 0.2 - Голос будет меняться сильнее }; const get_voximplant_bot_values = async (bot_id, bot_token) => { const url = 'https://eu1.api.pro-talk.ru/api/v1.0/get_voximplant_bot_values'; const data = { bot_id, bot_token }; Logger.write(`get_voximplant_bot_values: ${url} ${bot_id} ${bot_token}`); return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; const run_function = async (function_id, arguments) => { const url = 'https://eu1.api.pro-talk.ru/api/v1.0/run_function'; const data = { function_id, functions_base_id: 'appkq3HrzrxYxoAV8', bot_id, bot_token, arguments }; return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; VoxEngine.addEventListener(AppEvents.CallAlerting, async ({ call }) => { const voximplant_bot_raw = await get_voximplant_bot_values(bot_id, bot_token); const voximplant_bot_values = JSON.parse(voximplant_bot_raw.text); Logger.write('voximplant_bot_values: ' + JSON.stringify(voximplant_bot_values)); let realtimeAPIClient = undefined; let greetingPlayed = false; let player = undefined; let useOpenAIVoice = false; let openAIVoiceName = ''; let recordingUrl = null; if (voximplant_bot_values.voice_id && voximplant_bot_values.voice_id.includes('OpenAI:')) { useOpenAIVoice = true; openAIVoiceName = voximplant_bot_values.voice_id.replace('OpenAI: ', '').toLowerCase(); } else { const pathParameters = { voice_id: voximplant_bot_values.voice_id }; const queryParameters = { model_id: 'eleven_flash_v2_5' }; var ttsParameters = { pathParameters, queryParameters, keepAlive: true }; } call.answer(); call.record({ hd_audio: false, stereo: false, expire: '2 months' }); Logger.write('Call recording started'); call.addEventListener(CallEvents.RecordStarted, (e) => { recordingUrl = e.url; Logger.write('Recording URL: ' + recordingUrl); }); const callBaseHandler = () => { if (realtimeAPIClient) realtimeAPIClient.close(); VoxEngine.terminate(); }; call.addEventListener(CallEvents.Disconnected, callBaseHandler); call.addEventListener(CallEvents.Failed, callBaseHandler); const MODEL = "gpt-4o-realtime-preview"; const ON_WEB_SOCKET_CLOSE = (event) => { Logger.write('WebSocket closed: ' + JSON.stringify(event)); VoxEngine.terminate(); }; const ON_WEB_SOCKET_ERROR = (event) => { Logger.write('WebSocket error: ' + JSON.stringify(event)); if (event.error?.code !== 'unknown_parameter') { VoxEngine.terminate(); } }; try { realtimeAPIClient = await OpenAI.Beta.createRealtimeAPIClient({ apiKey: voximplant_bot_values.openai_api_key, model: MODEL, onWebSocketClose: ON_WEB_SOCKET_CLOSE, onWebSocketError: ON_WEB_SOCKET_ERROR }); const session_update = { instructions: voximplant_bot_values.prompt, // 🎯 стиль речи из ProTalk input_audio_transcription: { model: "gpt-4o-transcribe" } }; if (voximplant_bot_values.functions !== null) { session_update.tools = voximplant_bot_values.functions; session_update.tool_choice = "auto"; } if (useOpenAIVoice) { session_update.voice = openAIVoiceName; session_update.speed = OPENAI_VOICE_SETTINGS.speed; session_update.temperature = OPENAI_VOICE_SETTINGS.temperature; realtimeAPIClient.sendMediaTo(call); } else { session_update.modalities = ["text"]; } realtimeAPIClient.sessionUpdate(session_update); Logger.write('instructions: ' + session_update.instructions); realtimeAPIClient.responseCreate({ instructions: `А теперь скажи так: "${voximplant_bot_values.hello}"` }); if (useOpenAIVoice) { realtimeAPIClient.addEventListener(OpenAI.Beta.Events.WebSocketMediaEnded, () => { if (!greetingPlayed) { greetingPlayed = true; VoxEngine.sendMediaBetween(call, realtimeAPIClient); } }); } else { realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDone, () => { player.append(" ", true); if (!greetingPlayed) { greetingPlayed = true; call.sendMediaTo(realtimeAPIClient); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDelta, (event) => { if (player == undefined) { player = ElevenLabs.createRealtimeTTSPlayer(event.data.delta, ttsParameters); player.sendMediaTo(call); } else { player.append(event.data.delta); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.InputAudioBufferSpeechStarted, () => { if (player != undefined) player.clearBuffer(); }); } realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseFunctionCallArgumentsDone, async (event) => { Logger.write(`RealtimeAPIEvents: ${event.data.arguments}`); try { const args = JSON.parse(event.data.arguments); const res = await run_function(args.function_id, args); Logger.write('run_function res: ' + JSON.stringify(res, null, 2)); const response = { instructions: 'Вот результат выполнения функции: `\n' + JSON.stringify(res, null, 2) + '`\nИспользуй эту информацию для ответа, но не воспроизводи её дословно.' }; if (voximplant_bot_values.log) { const now = new Date(); const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; const args_log = { url: voximplant_bot_values.log, sheet_number: 1, row_to_append: `${dateStr};;${chat_id};; ;;${event.data.arguments};;${JSON.stringify(res, null, 2)};;Voximplant;;${bot_id}`, next_question: '' }; await run_function(182, args_log); } realtimeAPIClient.responseCreate(response); } catch (error) { Logger.write('Error RealtimeAPIEvents: ' + error.message); } }); } catch (error) { Logger.write(error); VoxEngine.terminate(); } });
JS СКРИПТ - VOXIMPLANT исХОДЯЩИЙ ЗВОНОК
require(Modules.OpenAI); require(Modules.ElevenLabs); ////////// DO NOT TOUCH !!! const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15); const chat_id = generateChatId('vox_'); var customDataParsed = {}; let bot_id = undefined; let bot_token = undefined; let realtimeAPIClient = undefined; let greetingPlayed = false; let player = undefined; let useOpenAIVoice = false; let openAIVoiceName = ''; const get_voximplant_bot_values = async (bot_id, bot_token) => { const url = 'https://us1.api.pro-talk.ru/api/v1.0/get_voximplant_bot_values'; const data = { 'bot_id': bot_id, 'bot_token': bot_token, }; Logger.write(`get_voximplant_bot_values: ${url} ${bot_id} ${bot_token}`); return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; const run_function = async (function_id, arguments) => { Logger.write(`run_function: ${function_id} ${bot_id} ${bot_token} ${JSON.stringify(arguments)}`); const url = 'https://us1.api.pro-talk.ru/api/v1.0/run_function'; const data = { 'function_id': function_id, 'functions_base_id': 'appkq3HrzrxYxoAV8', 'bot_id': bot_id, 'bot_token': bot_token, 'arguments': arguments }; return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; // Обработчик события запуска сценария VoxEngine.addEventListener(AppEvents.Started, () => { Logger.write("Scenario started. Checking for custom data..."); // Получаем customData через VoxEngine.customData() customData = VoxEngine.customData(); if (customData) { Logger.write(`Custom data received: ${customData}`); handleCustomData(customData); } else { Logger.write("No custom data provided. Terminating."); VoxEngine.terminate(); } }); // Обработка входящих параметров const handleCustomData = (customData) => { try { customDataParsed = JSON.parse(customData); Logger.write(`Custom data parsed: ${JSON.stringify(customDataParsed)}`); Logger.write(`Making outbound`); // Инициализация звонка call = VoxEngine.callPSTN(customDataParsed.phone, '790100000'); //УКАЖИТЕ СВОЙ ИСХОДЯЩИЙ НОМЕР call.answer(); const callBaseHandler = () => { if (realtimeAPIClient) realtimeAPIClient.close(); VoxEngine.terminate(); }; call.addEventListener(CallEvents.Disconnected, callBaseHandler); call.addEventListener(CallEvents.Failed, callBaseHandler); call.addEventListener(CallEvents.Connected, async () => { bot_id = customDataParsed.script_id; bot_token = customDataParsed.bot_token; const voximplant_bot_raw = await get_voximplant_bot_values(bot_id, bot_token); voximplant_bot_values = JSON.parse(voximplant_bot_raw.text); voximplant_bot_values.hello = customDataParsed.bot_hello; voximplant_bot_values.prompt = voximplant_bot_values.prompt + '\n\n' + customDataParsed.bot_task; Logger.write('voximplant_bot_values: ' + JSON.stringify(voximplant_bot_values)); // Проверяем, используем ли голос OpenAI if (voximplant_bot_values.voice_id && voximplant_bot_values.voice_id.includes('OpenAI:')) { useOpenAIVoice = true; openAIVoiceName = voximplant_bot_values.voice_id.replace('OpenAI: ', '').toLowerCase(); } else { // Параметры для ElevenLabs const pathParameters = { voice_id: voximplant_bot_values.voice_id, }; const queryParameters = { model_id: 'eleven_flash_v2_5', }; var ttsParameters = { pathParameters, queryParameters, keepAlive: true, }; } const MODEL = "gpt-4o-realtime-preview"; const ON_WEB_SOCKET_CLOSE = (event) => { // Connection to OpenAI has been closed VoxEngine.terminate(); }; try { // Create realtime client instance realtimeAPIClient = await OpenAI.Beta.createRealtimeAPIClient({ apiKey: voximplant_bot_values.openai_api_key, model: MODEL, onWebSocketClose: ON_WEB_SOCKET_CLOSE }); // Настройка сессии в зависимости от выбранного голоса var session_update = { 'instructions': voximplant_bot_values.prompt, "input_audio_transcription": { "model": "gpt-4o-transcribe" } }; // Добавляем tools и tool_choice только если functions не null if (voximplant_bot_values.functions !== null) { session_update.tools = voximplant_bot_values.functions; session_update.tool_choice = "auto"; } if (useOpenAIVoice) { // Используем голос OpenAI session_update.voice = openAIVoiceName; // Отправляем медиа сразу в OpenAI realtimeAPIClient.sendMediaTo(call); } else { // Используем ElevenLabs session_update.modalities = ["text"]; } realtimeAPIClient.sessionUpdate(session_update); Logger.write('instructions: ' + voximplant_bot_values.prompt); const response = { instructions: `А теперь скажи так: "${voximplant_bot_values.hello}"` }; realtimeAPIClient.responseCreate(response); if (useOpenAIVoice) { // Логика для голосов OpenAI realtimeAPIClient.addEventListener(OpenAI.Beta.Events.WebSocketMediaEnded, (event) => { if (!greetingPlayed) { greetingPlayed = true; VoxEngine.sendMediaBetween(call, realtimeAPIClient); } }); } else { // Логика для ElevenLabs realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDone, (event) => { player.append(" ", true); if (!greetingPlayed) { greetingPlayed = true; call.sendMediaTo(realtimeAPIClient); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDelta, (event) => { if (player == undefined) { player = ElevenLabs.createRealtimeTTSPlayer(event.data.delta, ttsParameters); player.sendMediaTo(call); } else { player.append(event.data.delta); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.InputAudioBufferSpeechStarted, (event) => { if (player != undefined) player.clearBuffer(); }); } realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseFunctionCallArgumentsDone, async (event) => { Logger.write(`RealtimeAPIEvents: ${event.data.arguments}`); try { const args = JSON.parse(event.data.arguments); Logger.write(`RealtimeAPIEvents args: ${args}`); const res = await run_function(args.function_id, args); Logger.write('run_function res: ' + JSON.stringify(res, null, 2)); var response = { 'instructions': 'Вот результат выполнения функции: `\n' + JSON.stringify(res, null, 2) + '`\nИспользуй эту информацию для ответа, но не воспроизводи её дословно.' }; if (voximplant_bot_values.log) { var now = new Date(); var dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; args_log = { url: voximplant_bot_values.log, sheet_number: 1, row_to_append: `${dateStr};;${chat_id};; ;;${event.data.arguments};;${JSON.stringify(res, null, 2)};;Voximplant;;${bot_id}`, next_question: '' } const res_log = await run_function(182, args_log); } realtimeAPIClient.responseCreate(response); } catch (error) { Logger.write('Error RealtimeAPIEvents: ' + error.message); } }); } catch (error) { Logger.write(error); VoxEngine.terminate(); } }); } catch (error) { Logger.write(`Error parsing custom data: ${error}`); VoxEngine.terminate(); } };
Пример кода для Google AppScript
function runVoximplantCall() { var vox_cred = { "account_email": "user@mail.com", "account_id": 3333333, "key_id": "22520be0-1a99-4c09-a155-770776366657", "private_key": "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-----END PRIVATE KEY-----\n" } // URL вашего API var url = "https://eu1.api.pro-talk.ru/api/v1.0/run_voximplant_call_post"; // Европейский сервер // var url = "https://us1.api.pro-talk.ru/api/v1.0/run_voximplant_call_post"; // Сервер в США (альтернативный вариант) // Данные для отправки в API var payload = { "vox_cred": vox_cred, // Учетные данные Voximplant "phone": "7912XXXXXXX", // Номер телефона, на который будет совершен звонок "bot_id": 12345, // ID бота, который будет использоваться для звонка "bot_token": "XXXXXXXXXXXXXXXXXXXXXXX", // Токен бота для аутентификации "elevellabs_api": "XXXXXXXXXXXXXXXXXXXXXX", // API ключ для Elevellabs (синтез речи) "elevellabs_voice_id": "XXXXXXXXXXXXXX", // ID голоса для синтеза речи "hello_text": "Привет! Это консультант по расчету стоимости установки окон - Олег", // Текст, который будет произнесен при звонке "rule_id": 7654321 // ID правила для обработки звонка в Voximplant }; // Опции для HTTP-запроса var options = { "method": "post", // Метод запроса (POST) "contentType": "application/json", // Тип содержимого (JSON) "payload": JSON.stringify(payload) // Преобразуем данные в JSON-строку }; // Отправка запроса на сервер var response = UrlFetchApp.fetch(url, options); // Логирование ответа от сервера Logger.log(response.getContentText()); // Выводим ответ в лог для отладки }
Интеграция ProTalk + Voximplant + ElevenLabs + Google AppScript
С помощью интеграции с Voximplant и Google AppScript вы можете легко настроить автоматические звонки для своих клиентов. Это идеальное решение для:
Оповещений о заказах, доставках или важных событиях.
Продаж — бот может звонить клиентам и предлагать товары или услуги.
Поддержки — напоминания, консультации и ответы на вопросы в режиме реального времени.
Настройте бота на платформе ProTalk. Используйте готовый Google AppScript для запуска звонков через Voximplant. Бот звонит клиенту, произносит заданный текст (например, приветствие или информацию о заказе) и может взаимодействовать с клиентом через голосовое меню. Простота настройки: Готовый шаблон для Google AppScript.
Гибкость: Настройте текст, голос и сценарий звонка под свои задачи.
Интеграция: Работает с Voximplant и синтезом речи от Elevellabs.
Автоматизация: Ваш бот звонит сам, экономя ваше время и ресурсы.
Теперь можно проводить телефонные опросы с ИИ-ботом прямо из Google Таблиц!
Связка Voximplant + ProTalk позволяет автоматизировать звонки и собирать ответы респондентов напрямую в Google Таблицу — без лишних действий!
Как это работает? Настраиваете сценарий звонка в Voximplant (можно с ИИ-ассистентом).
Запускаете опрос из Google Таблицы через ProTalk.
Получаете ответы респондентов в эту же таблицу — удобно анализировать!
Плюсы: Автоматизация — никаких ручных обзвонов.
Интеграция — все данные сразу в таблице.
Гибкость — можно кастомизировать сценарии.
js скрипт - voximplant Входящий звонок
require(Modules.OpenAI); require(Modules.ElevenLabs); const bot_id = 12345; //УКАЖИТЕ НОМЕР БОТА const bot_token = 'xxxxxxxxxxxxxxxx'; //УКАЖИТЕ ТОКЕН БОТА const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15); const chat_id = generateChatId('vox_'); const OPENAI_VOICE_SETTINGS = { speed: 1.2, //Диапазон: 0.25 (очень медленно) – 4.0 (очень быстро) temperature: 1.2, // Диапазон: 0.0 (строгий) – 1.2 (креативный) pitch: 1.05, stability: 1.0, // (стабильность голоса) Диапазон: 0.0 до 1.0 управляет насколько предсказуемо и плавно звучит голос. 0.3 - Голос с вариативной интонацией. 1.0 - Максимально однотонный similarity_boost: 0.1 // (похожесть на голос-оригинал) Диапазон: 0.0 до 1.0 0.2 - Голос будет меняться сильнее }; const get_voximplant_bot_values = async (bot_id, bot_token) => { const url = 'https://eu1.api.pro-talk.ru/api/v1.0/get_voximplant_bot_values'; const data = { bot_id, bot_token }; Logger.write(`get_voximplant_bot_values: ${url} ${bot_id} ${bot_token}`); return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; const run_function = async (function_id, arguments) => { const url = 'https://eu1.api.pro-talk.ru/api/v1.0/run_function'; const data = { function_id, functions_base_id: 'appkq3HrzrxYxoAV8', bot_id, bot_token, arguments }; return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; VoxEngine.addEventListener(AppEvents.CallAlerting, async ({ call }) => { const voximplant_bot_raw = await get_voximplant_bot_values(bot_id, bot_token); const voximplant_bot_values = JSON.parse(voximplant_bot_raw.text); Logger.write('voximplant_bot_values: ' + JSON.stringify(voximplant_bot_values)); let realtimeAPIClient = undefined; let greetingPlayed = false; let player = undefined; let useOpenAIVoice = false; let openAIVoiceName = ''; let recordingUrl = null; if (voximplant_bot_values.voice_id && voximplant_bot_values.voice_id.includes('OpenAI:')) { useOpenAIVoice = true; openAIVoiceName = voximplant_bot_values.voice_id.replace('OpenAI: ', '').toLowerCase(); } else { const pathParameters = { voice_id: voximplant_bot_values.voice_id }; const queryParameters = { model_id: 'eleven_flash_v2_5' }; var ttsParameters = { pathParameters, queryParameters, keepAlive: true }; } call.answer(); call.record({ hd_audio: false, stereo: false, expire: '2 months' }); Logger.write('Call recording started'); call.addEventListener(CallEvents.RecordStarted, (e) => { recordingUrl = e.url; Logger.write('Recording URL: ' + recordingUrl); }); const callBaseHandler = () => { if (realtimeAPIClient) realtimeAPIClient.close(); VoxEngine.terminate(); }; call.addEventListener(CallEvents.Disconnected, callBaseHandler); call.addEventListener(CallEvents.Failed, callBaseHandler); const MODEL = "gpt-4o-realtime-preview"; const ON_WEB_SOCKET_CLOSE = (event) => { Logger.write('WebSocket closed: ' + JSON.stringify(event)); VoxEngine.terminate(); }; const ON_WEB_SOCKET_ERROR = (event) => { Logger.write('WebSocket error: ' + JSON.stringify(event)); if (event.error?.code !== 'unknown_parameter') { VoxEngine.terminate(); } }; try { realtimeAPIClient = await OpenAI.Beta.createRealtimeAPIClient({ apiKey: voximplant_bot_values.openai_api_key, model: MODEL, onWebSocketClose: ON_WEB_SOCKET_CLOSE, onWebSocketError: ON_WEB_SOCKET_ERROR }); const session_update = { instructions: voximplant_bot_values.prompt, // 🎯 стиль речи из ProTalk input_audio_transcription: { model: "gpt-4o-transcribe" } }; if (voximplant_bot_values.functions !== null) { session_update.tools = voximplant_bot_values.functions; session_update.tool_choice = "auto"; } if (useOpenAIVoice) { session_update.voice = openAIVoiceName; session_update.speed = OPENAI_VOICE_SETTINGS.speed; session_update.temperature = OPENAI_VOICE_SETTINGS.temperature; realtimeAPIClient.sendMediaTo(call); } else { session_update.modalities = ["text"]; } realtimeAPIClient.sessionUpdate(session_update); Logger.write('instructions: ' + session_update.instructions); realtimeAPIClient.responseCreate({ instructions: `А теперь скажи так: "${voximplant_bot_values.hello}"` }); if (useOpenAIVoice) { realtimeAPIClient.addEventListener(OpenAI.Beta.Events.WebSocketMediaEnded, () => { if (!greetingPlayed) { greetingPlayed = true; VoxEngine.sendMediaBetween(call, realtimeAPIClient); } }); } else { realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDone, () => { player.append(" ", true); if (!greetingPlayed) { greetingPlayed = true; call.sendMediaTo(realtimeAPIClient); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDelta, (event) => { if (player == undefined) { player = ElevenLabs.createRealtimeTTSPlayer(event.data.delta, ttsParameters); player.sendMediaTo(call); } else { player.append(event.data.delta); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.InputAudioBufferSpeechStarted, () => { if (player != undefined) player.clearBuffer(); }); } realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseFunctionCallArgumentsDone, async (event) => { Logger.write(`RealtimeAPIEvents: ${event.data.arguments}`); try { const args = JSON.parse(event.data.arguments); const res = await run_function(args.function_id, args); Logger.write('run_function res: ' + JSON.stringify(res, null, 2)); const response = { instructions: 'Вот результат выполнения функции: `\n' + JSON.stringify(res, null, 2) + '`\nИспользуй эту информацию для ответа, но не воспроизводи её дословно.' }; if (voximplant_bot_values.log) { const now = new Date(); const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; const args_log = { url: voximplant_bot_values.log, sheet_number: 1, row_to_append: `${dateStr};;${chat_id};; ;;${event.data.arguments};;${JSON.stringify(res, null, 2)};;Voximplant;;${bot_id}`, next_question: '' }; await run_function(182, args_log); } realtimeAPIClient.responseCreate(response); } catch (error) { Logger.write('Error RealtimeAPIEvents: ' + error.message); } }); } catch (error) { Logger.write(error); VoxEngine.terminate(); } });
JS СКРИПТ - VOXIMPLANT исХОДЯЩИЙ ЗВОНОК
require(Modules.OpenAI); require(Modules.ElevenLabs); ////////// DO NOT TOUCH !!! const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15); const chat_id = generateChatId('vox_'); var customDataParsed = {}; let bot_id = undefined; let bot_token = undefined; let realtimeAPIClient = undefined; let greetingPlayed = false; let player = undefined; let useOpenAIVoice = false; let openAIVoiceName = ''; const get_voximplant_bot_values = async (bot_id, bot_token) => { const url = 'https://us1.api.pro-talk.ru/api/v1.0/get_voximplant_bot_values'; const data = { 'bot_id': bot_id, 'bot_token': bot_token, }; Logger.write(`get_voximplant_bot_values: ${url} ${bot_id} ${bot_token}`); return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; const run_function = async (function_id, arguments) => { Logger.write(`run_function: ${function_id} ${bot_id} ${bot_token} ${JSON.stringify(arguments)}`); const url = 'https://us1.api.pro-talk.ru/api/v1.0/run_function'; const data = { 'function_id': function_id, 'functions_base_id': 'appkq3HrzrxYxoAV8', 'bot_id': bot_id, 'bot_token': bot_token, 'arguments': arguments }; return Net.httpRequestAsync(url, { headers: ["Content-Type: application/json"], method: 'POST', postData: JSON.stringify(data) }); }; // Обработчик события запуска сценария VoxEngine.addEventListener(AppEvents.Started, () => { Logger.write("Scenario started. Checking for custom data..."); // Получаем customData через VoxEngine.customData() customData = VoxEngine.customData(); if (customData) { Logger.write(`Custom data received: ${customData}`); handleCustomData(customData); } else { Logger.write("No custom data provided. Terminating."); VoxEngine.terminate(); } }); // Обработка входящих параметров const handleCustomData = (customData) => { try { customDataParsed = JSON.parse(customData); Logger.write(`Custom data parsed: ${JSON.stringify(customDataParsed)}`); Logger.write(`Making outbound`); // Инициализация звонка call = VoxEngine.callPSTN(customDataParsed.phone, '790100000'); //УКАЖИТЕ СВОЙ ИСХОДЯЩИЙ НОМЕР call.answer(); const callBaseHandler = () => { if (realtimeAPIClient) realtimeAPIClient.close(); VoxEngine.terminate(); }; call.addEventListener(CallEvents.Disconnected, callBaseHandler); call.addEventListener(CallEvents.Failed, callBaseHandler); call.addEventListener(CallEvents.Connected, async () => { bot_id = customDataParsed.script_id; bot_token = customDataParsed.bot_token; const voximplant_bot_raw = await get_voximplant_bot_values(bot_id, bot_token); voximplant_bot_values = JSON.parse(voximplant_bot_raw.text); voximplant_bot_values.hello = customDataParsed.bot_hello; voximplant_bot_values.prompt = voximplant_bot_values.prompt + '\n\n' + customDataParsed.bot_task; Logger.write('voximplant_bot_values: ' + JSON.stringify(voximplant_bot_values)); // Проверяем, используем ли голос OpenAI if (voximplant_bot_values.voice_id && voximplant_bot_values.voice_id.includes('OpenAI:')) { useOpenAIVoice = true; openAIVoiceName = voximplant_bot_values.voice_id.replace('OpenAI: ', '').toLowerCase(); } else { // Параметры для ElevenLabs const pathParameters = { voice_id: voximplant_bot_values.voice_id, }; const queryParameters = { model_id: 'eleven_flash_v2_5', }; var ttsParameters = { pathParameters, queryParameters, keepAlive: true, }; } const MODEL = "gpt-4o-realtime-preview"; const ON_WEB_SOCKET_CLOSE = (event) => { // Connection to OpenAI has been closed VoxEngine.terminate(); }; try { // Create realtime client instance realtimeAPIClient = await OpenAI.Beta.createRealtimeAPIClient({ apiKey: voximplant_bot_values.openai_api_key, model: MODEL, onWebSocketClose: ON_WEB_SOCKET_CLOSE }); // Настройка сессии в зависимости от выбранного голоса var session_update = { 'instructions': voximplant_bot_values.prompt, "input_audio_transcription": { "model": "gpt-4o-transcribe" } }; // Добавляем tools и tool_choice только если functions не null if (voximplant_bot_values.functions !== null) { session_update.tools = voximplant_bot_values.functions; session_update.tool_choice = "auto"; } if (useOpenAIVoice) { // Используем голос OpenAI session_update.voice = openAIVoiceName; // Отправляем медиа сразу в OpenAI realtimeAPIClient.sendMediaTo(call); } else { // Используем ElevenLabs session_update.modalities = ["text"]; } realtimeAPIClient.sessionUpdate(session_update); Logger.write('instructions: ' + voximplant_bot_values.prompt); const response = { instructions: `А теперь скажи так: "${voximplant_bot_values.hello}"` }; realtimeAPIClient.responseCreate(response); if (useOpenAIVoice) { // Логика для голосов OpenAI realtimeAPIClient.addEventListener(OpenAI.Beta.Events.WebSocketMediaEnded, (event) => { if (!greetingPlayed) { greetingPlayed = true; VoxEngine.sendMediaBetween(call, realtimeAPIClient); } }); } else { // Логика для ElevenLabs realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDone, (event) => { player.append(" ", true); if (!greetingPlayed) { greetingPlayed = true; call.sendMediaTo(realtimeAPIClient); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseTextDelta, (event) => { if (player == undefined) { player = ElevenLabs.createRealtimeTTSPlayer(event.data.delta, ttsParameters); player.sendMediaTo(call); } else { player.append(event.data.delta); } }); realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.InputAudioBufferSpeechStarted, (event) => { if (player != undefined) player.clearBuffer(); }); } realtimeAPIClient.addEventListener(OpenAI.Beta.RealtimeAPIEvents.ResponseFunctionCallArgumentsDone, async (event) => { Logger.write(`RealtimeAPIEvents: ${event.data.arguments}`); try { const args = JSON.parse(event.data.arguments); Logger.write(`RealtimeAPIEvents args: ${args}`); const res = await run_function(args.function_id, args); Logger.write('run_function res: ' + JSON.stringify(res, null, 2)); var response = { 'instructions': 'Вот результат выполнения функции: `\n' + JSON.stringify(res, null, 2) + '`\nИспользуй эту информацию для ответа, но не воспроизводи её дословно.' }; if (voximplant_bot_values.log) { var now = new Date(); var dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; args_log = { url: voximplant_bot_values.log, sheet_number: 1, row_to_append: `${dateStr};;${chat_id};; ;;${event.data.arguments};;${JSON.stringify(res, null, 2)};;Voximplant;;${bot_id}`, next_question: '' } const res_log = await run_function(182, args_log); } realtimeAPIClient.responseCreate(response); } catch (error) { Logger.write('Error RealtimeAPIEvents: ' + error.message); } }); } catch (error) { Logger.write(error); VoxEngine.terminate(); } }); } catch (error) { Logger.write(`Error parsing custom data: ${error}`); VoxEngine.terminate(); } };
промпт - оператор телефонных опросов
Цель: Твоя цель - запустить телефонный опрос. Роль: Ты - женщина. Тебя зовут - Анна Ты работаешь в должности - Оператор телефонных опросов Ты - Анна, ты дружелюбная и внимательная девушка. Поведение: 1. Из предоставленной тебе переписки возьми только один контакт клиента (если их несколько, то бери первый контакт в списке). 2. Запусти функцию `ask_question_by_phone` со следующими параметрами: ``` "employee_id": 3671, "voxapikeys_url": "https://АДРЕС_КУДА_СОХРАНИТЕ_СКРИПТ_ДЛЯ_VOXIMPLANT/DialogAI_skgpt.json", "rule_id": 3702231, "role": "Ты женщина, ты менеджер по сбору обратной связи у клиентов. Задай строго последовательно два вопроса: 1. Вы довольны установкой рекламной вывески? 2. Вы будете рекомендовать нашу компанию знакомым?\n\nВеди диалог последовательно и задавай вопросы только по одному за раз.", "hello_text": "Добрый день! У вас будет пара минут для опроса?", "voice": "Rachel", ``` остальные параметры заполни самостоятельно. Внимание! При каждом моем обращении сразу вызывай функцию `ask_question_by_phone`, иначе то ты получишь штраф.
промпт - специалист по анализу данных в exel
Цель: Помочь с работой с таблицей по адресу: `https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxx/edit?usp=sharing` Роль: Ты - мужчина. Тебя зовут - Александр Ты работаешь в должности - Специалист по анализу данных в Excel Ты - опытный менеджер Google таблиц, готовый помочь с любыми задачами в таблицах. Поведение: 1. Будь внимателен к деталям при работе с таблицами. 2. Во всех датах первое число в дате это день, затем месяц. 3. Если тебе нужно получить данные из таблицы то передай `mode="?"`, если что-то изменить в таблице то `mode="."`. 4. При режиме `mode="?"` передавай в параметр `request` запрос: `Мне нужно имя и телефон клиента с которым еще не проводился опрос`. 5. При использовании режима `mode="."` сформируй JSON словарь, содержащий имя клиента, телефон клиента и ответы клиента на вопросы из диалога и передай его в параметр `request`. 6. Передавай очень внимательно ссылку на таблицу. 7. При чтении данных из таблицы выдай только имя и телефон первого клиента. ! При каждом моем обращении вызывай функцию работы с таблицей.
промпт - руководитель отдела телефонных опросов
Цель: Твоя цель запустить цепочку задач и следовать четко по этому чек-листу: ``` 1. Передать такой запрос Александру [3672]: `Дай мне один контакт клиента из твоей таблицы у которого еще не собиралась обратная связь`. 2. Передать такой запрос Анне [3671]: `Запусти телефонный опрос с выбранным клиентом`. 3. Попроси Александра [3672] записать в таблицу ответы клиента. ``` Строго следуй очередности в выбранной цепочке задач и передавай подзадачи от одного помощника к другому при помощи вызова функции: `send_taks_to_assistant`. Роль: Ты - мужчина. Тебя зовут - Александр Ты работаешь в должности - Руководитель отдела по телефонным опросам клиентов Ты не отвечаешь на мои вопросы или сообщения, а только сразу передаешь дословно мои сообщения исполнителям. Список исполнителей: 1. Менеджер Александр (работает с таблицей клиентов в Google Sheets) 2. Оператор Анна (проведет телефонный опрос с клиентом) Поведение: Критически важные инструкции: 1. Максимально подробно объясняй задачу своему сотруднику. 2. Когда я скажу что утверждаю, сразу переходи к следующему сотруднику в цепочке задач. ! Перед каждым своим ответом проверяй очерёдность выполнения чек-листа шаг за шагом.
Пример кода для Google AppScript
function runVoximplantCall() { var vox_cred = { "account_email": "user@mail.com", "account_id": 3333333, "key_id": "22520be0-1a99-4c09-a155-770776366657", "private_key": "-----BEGIN PRIVATE KEY-----XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-----END PRIVATE KEY-----\n" } // URL вашего API var url = "https://eu1.api.pro-talk.ru/api/v1.0/run_voximplant_call_post"; // Европейский сервер // var url = "https://us1.api.pro-talk.ru/api/v1.0/run_voximplant_call_post"; // Сервер в США (альтернативный вариант) // Данные для отправки в API var payload = { "vox_cred": vox_cred, // Учетные данные Voximplant "phone": "7912XXXXXXX", // Номер телефона, на который будет совершен звонок "bot_id": 12345, // ID бота, который будет использоваться для звонка "bot_token": "XXXXXXXXXXXXXXXXXXXXXXX", // Токен бота для аутентификации "elevellabs_api": "XXXXXXXXXXXXXXXXXXXXXX", // API ключ для Elevellabs (синтез речи) "elevellabs_voice_id": "XXXXXXXXXXXXXX", // ID голоса для синтеза речи "hello_text": "Привет! Это консультант по расчету стоимости установки окон - Олег", // Текст, который будет произнесен при звонке "rule_id": 7654321 // ID правила для обработки звонка в Voximplant }; // Опции для HTTP-запроса var options = { "method": "post", // Метод запроса (POST) "contentType": "application/json", // Тип содержимого (JSON) "payload": JSON.stringify(payload) // Преобразуем данные в JSON-строку }; // Отправка запроса на сервер var response = UrlFetchApp.fetch(url, options); // Логирование ответа от сервера Logger.log(response.getContentText()); // Выводим ответ в лог для отладки }
ИП Горелов Максим Николаевич ИНН 500104951533 ОГРН 309500106900065