ИП Горелов Максим Николаевич 8 (915) 093-74-75
Интеграция ProTalk + Voximplant + ElevenLabs + Google AppScript
С помощью интеграции с Voximplant и Google AppScript вы можете легко настроить автоматические звонки для своих клиентов. Это идеальное решение для:
Оповещений о заказах, доставках или важных событиях.
Продаж — бот может звонить клиентам и предлагать товары или услуги.
Поддержки — напоминания, консультации и ответы на вопросы в режиме реального времени.
Настройте бота на платформе ProTalk. Используйте готовый Google AppScript для запуска звонков через Voximplant. Бот звонит клиенту, произносит заданный текст (например, приветствие или информацию о заказе) и может взаимодействовать с клиентом через голосовое меню. Простота настройки: Готовый шаблон для Google AppScript.
Гибкость: Настройте текст, голос и сценарий звонка под свои задачи.
Интеграция: Работает с Voximplant и синтезом речи от Elevellabs.
Автоматизация: Ваш бот звонит сам, экономя ваше время и ресурсы.
Если у вас не получается самостоятельно настроить скрипт, напишите нам в группу https://t.me/+KQ6mUbTAtFQ4YWEy
js - voximplant Входящий звонок ИИ агенту
require(Modules.ASR);
const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15);
const askURL = 'https://eu1.api.pro-talk.ru.ru/api/v1.0/ask/XXXXXXXXXXXXXXXXXXXXXXXX';
const bot_id = 11620;
const elevellabs_api_key = 'XXXXXXXXXXXXXXXXXXXXXX';
const elevellabs_voice = 'voice_id:XXXXXXXXXXXXXX';
const hello_text = 'Здравствуйте! Какой у вас вопрос по нашей платформе нейро-сотрудников для бизнеса?';
const chat_id = generateChatId('vox_');
let ai_busy = false;
let dialog_started = false;
let call, asr;
let isPlayingResponse = false; // Флаг для отслеживания воспроизведения ответа
let callerNumber = '';
// List of universal phrases
const universalPhrases = [
'Одну минутку, я обдумаю свой ответ.',
'Хорошо, я вас услышал!',
'Да, да, мне понятен ваш вопрос!',
'Понятно, секундочку.',
'Мне нужно немного времени на ответ.',
'Еще немного, я скоро отвечу.',
'Ваш запрос обрабатывается, пожалуйста, подождите.',
'Дайте мне минутку подумать над ответом.',
'Собираю информацию для ответа на ваш вопрос.',
'Подождите минутку, я дам развернутый ответ',
'Ммм_ окей.',
'Ага, да да.',
'Ээээ, да, я понял.',
'Да, услышал.',
'Да, принято.',
];
// Функция для выполнения API-запроса в фоновом режиме
function startDialog() {
if (!dialog_started) {
dialog_started = true;
requestCompletion('🏁')
.then(res => {
if (res.code === 200) {
const jsData = JSON.parse(res.text);
// Обработка ответа от API, если нужно
Logger.write(`API response: ${jsData.done}`);
}
})
.catch(error => {
Logger.write(`Error during API call: ${error}`);
});
}
}
// Function to shuffle an array using Fisher-Yates algorithm
function shuffle(array) {
let shuffled = array.slice();
for (let i = shuffled.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
// Initialize shuffled phrases and index
let shuffledPhrases = shuffle(universalPhrases);
let phraseIndex = 0;
// Function to send a random universal phrase without repetition
function sendRandomPhrase(call) {
if (phraseIndex >= shuffledPhrases.length) {
shuffledPhrases = shuffle(universalPhrases);
phraseIndex = 0;
}
const phrase = shuffledPhrases[phraseIndex];
phraseIndex++;
Logger.write(`sendRandomPhrase: ${phrase}`);
sendMessageText(call, phrase);
}
function sendMessageText(call, text) {
const textToSynthesize = encodeURIComponent(text);
var tts_url = `https://eu1.dialog.ai.atiks.org/api/v1.0/tts?token=dai-tts-3605ae6627c857bbaa9c249864522b8a&voice=${elevellabs_voice}&text=${textToSynthesize}&apikey=${elevellabs_api_key}`;
Logger.write(`tts_url: ${tts_url}`);
const speechSynthesisApiUrl = tts_url;
Net.httpRequest(speechSynthesisApiUrl, (res) => {
if (res.code === 200) {
let audioUrl = res.text;
Logger.write(res.code + " sendMessage: " + text);
call.startPlayback(audioUrl);
} else {
Logger.write(`Ошибка: ${res.code} - ${res.text}`);
}
}, {method: 'GET'});
return 'OK';
};
const sendMessage = (call, text) => {
call.startPlayback(text);
return 'OK';
};
const requestCompletion = async message => {
return Net.httpRequestAsync(askURL, {
headers: ["Content-Type: application/json"],
method: 'POST',
postData: JSON.stringify({
bot_id, chat_id, message,
audio: `ElevenLabs: ${elevellabs_voice}`,
elevellabs_api_key: elevellabs_api_key,
})
});
};
const handleASRResult = async e => {
if (!isPlayingResponse) { // Проверяем, идет ли воспроизведение ответа
sendRandomPhrase(call);
if (!ai_busy) {
ai_busy = true;
const startTime = Date.now();
const res = await requestCompletion(`[${callerNumber}] ${e.text}`);
if (res.code === 200) {
const jsData = JSON.parse(res.text);
isPlayingResponse = true; // Устанавливаем флаг перед воспроизведением ответа
sendMessage(call, jsData.done);
}
ai_busy = false; // Ensure ai_busy is set to false after response
}
}
call.sendMediaTo(asr);
};
const handleCallConnected = async e => {
callerNumber = e.callerid; // Получаем номер телефона звонящего
sendMessageText(call, hello_text);
startDialog();
call.sendMediaTo(asr);
};
// Обработчик окончания воспроизведения аудио
const handlePlaybackFinished = () => {
isPlayingResponse = false; // Сбрасываем флаг после завершения воспроизведения
Logger.write("Playback finished, resetting isPlayingResponse flag.");
};
VoxEngine.addEventListener(AppEvents.CallAlerting, e => {
call = e.call;
asr = VoxEngine.createASR({
profile: ASRProfileList.Google.ru_RU,
singleUtterance: true
});
asr.addEventListener(ASREvents.Result, handleASRResult);
call.addEventListener(CallEvents.Connected, handleCallConnected);
call.addEventListener(CallEvents.Disconnected, () => VoxEngine.terminate());
call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished); // Добавляем обработчик окончания воспроизведения
call.answer();
});
JS - VOXIMPLANT исХОДЯЩИЙ ЗВОНОК ИИ АГЕНТУ
require(Modules.ASR);
const generateChatId = prefix => prefix + Math.random().toString(36).substring(2, 15);
let askURL = 'https://api.chatforyou.ru/api/v1.0/ask/XXXXXXXXXXXXXXXXXX'; // Европа
//let askURL = 'https://api.chatforyou.ru/api/v1.0/ask/XXXXXXXXXXXXXXX'; // США
let bot_id = 0;
let elevellabs_api_key = 'XXXXXXXXXXXXXXXXXXXXXXXXX';
let elevellabs_voice = 'XXXXXXXXXXXX';
let hello_text = 'Добрый день!';
let chat_id;
let ai_busy = false;
let dialog_started = false;
let call, asr;
let isPlayingResponse = false; // Флаг для отслеживания воспроизведения ответа
// List of universal phrases
const universalPhrases = [
'Одну минутку, я обдумаю свой ответ.',
'Хорошо, я вас услышал!',
'Да, да, мне понятен ваш вопрос!',
'Понятно, секундочку.',
'Мне нужно немного времени на ответ.',
'Еще немного, я скоро отвечу.',
'Ваш запрос обрабатывается, пожалуйста, подождите.',
'Дайте мне минутку подумать над ответом.',
'Собираю информацию для ответа на ваш вопрос.',
'Подождите минутку, я дам развернутый ответ',
'Ммм_ окей.',
'Ага, да да.',
'Ээээ, да, я понял.',
'Да, услышал.',
'Да, принято.',
];
// Функция для выполнения API-запроса в фоновом режиме
function startDialog() {
if (!dialog_started) {
dialog_started = true;
requestCompletion('🏁')
.then(res => {
if (res.code === 200) {
const jsData = JSON.parse(res.text);
// Обработка ответа от API, если нужно
Logger.write(`API response: ${jsData.done}`);
}
})
.catch(error => {
Logger.write(`Error during API call: ${error}`);
});
}
}
// Function to shuffle an array using Fisher-Yates algorithm
function shuffle(array) {
let shuffled = array.slice();
for (let i = shuffled.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
// Initialize shuffled phrases and index
let shuffledPhrases = shuffle(universalPhrases);
let phraseIndex = 0;
// Function to send a random universal phrase without repetition
function sendRandomPhrase(call) {
if (phraseIndex >= shuffledPhrases.length) {
shuffledPhrases = shuffle(universalPhrases);
phraseIndex = 0;
}
const phrase = shuffledPhrases[phraseIndex];
phraseIndex++;
Logger.write(`sendRandomPhrase: ${phrase}`);
sendMessageText(call, phrase);
}
function sendMessageText(call, text) {
const textToSynthesize = encodeURIComponent(text);
var tts_url = `https://api.chatforyou.ru/api/v1.0/tts?token=dai-tts-3605ae6627c857bbaa9c249864522b8a&voice=${elevellabs_voice}&text=${textToSynthesize}&apikey=${elevellabs_api_key}`;
Logger.write(`tts_url: ${tts_url}`);
const speechSynthesisApiUrl = tts_url;
Net.httpRequest(speechSynthesisApiUrl, (res) => {
if (res.code === 200) {
let audioUrl = res.text;
Logger.write(res.code + " sendMessage: " + text);
if (call && call.startPlayback) {
call.startPlayback(audioUrl);
} else {
Logger.write("Ошибка: объект call не инициализирован или недоступен.");
}
} else {
Logger.write(`Ошибка: ${text} - ${res.code} - ${res.text}`);
}
}, {method: 'GET'});
return 'OK';
};
const sendMessage = (call, text) => {
call.startPlayback(text);
return 'OK';
};
const requestCompletion = async message => {
return Net.httpRequestAsync(askURL, {
headers: ["Content-Type: application/json"],
method: 'POST',
postData: JSON.stringify({
bot_id, chat_id, message,
audio: `ElevenLabs: ${elevellabs_voice}`,
elevellabs_api_key: elevellabs_api_key,
})
});
};
const handleASRResult = async e => {
if (!isPlayingResponse) { // Проверяем, идет ли воспроизведение ответа
sendRandomPhrase(call);
if (!ai_busy) {
ai_busy = true;
const startTime = Date.now();
const res = await requestCompletion(e.text);
if (res.code === 200) {
const jsData = JSON.parse(res.text);
isPlayingResponse = true; // Устанавливаем флаг перед воспроизведением ответа
sendMessage(call, jsData.done);
}
ai_busy = false; // Ensure ai_busy is set to false after response
}
}
call.sendMediaTo(asr);
};
const handleCallConnected = async () => {
sendMessageText(call, hello_text);
startDialog();
call.sendMediaTo(asr);
};
// Обработчик окончания воспроизведения аудио
const handlePlaybackFinished = () => {
isPlayingResponse = false; // Сбрасываем флаг после завершения воспроизведения
Logger.write("Playback finished, resetting isPlayingResponse flag.");
};
// Функция для инициализации исходящего звонка
const makeOutboundCall = (phone, script_id, bot_token, bot_hello, elevellabs_api, elevellabs_voice_id) => {
chat_id = generateChatId('vox_');
askURL = askURL + bot_token;
bot_id = script_id;
hello_text = bot_hello;
elevellabs_api_key = elevellabs_api;
elevellabs_voice = 'voice_id:' + elevellabs_voice_id;
Logger.write(`Making outbound call to ${phone} with script_id: ${script_id}`);
// Инициализация ASR
asr = VoxEngine.createASR({
profile: ASRProfileList.Google.ru_RU,
singleUtterance: true
});
asr.addEventListener(ASREvents.Result, handleASRResult);
// Инициализация звонка
call = VoxEngine.callPSTN(phone, '79014684884'); // 7901XXXXXXX - замените на ваш исходящий номер
call.addEventListener(CallEvents.Connected, handleCallConnected);
call.addEventListener(CallEvents.Disconnected, () => VoxEngine.terminate());
call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
};
// Обработка входящих параметров
const handleCustomData = (customData) => {
try {
const data = JSON.parse(customData);
const { phone, script_id, bot_token, bot_hello, elevellabs_api, elevellabs_voice_id } = data;
Logger.write(`Received custom data: phone=${phone}, script_id=${script_id}`);
makeOutboundCall(phone, script_id, bot_token, bot_hello, elevellabs_api, elevellabs_voice_id);
} catch (error) {
Logger.write(`Error parsing custom data: ${error}`);
VoxEngine.terminate();
}
};
// Обработчик события запуска сценария
VoxEngine.addEventListener(AppEvents.Started, () => {
Logger.write("Scenario started. Checking for custom data...");
// Получаем customData через VoxEngine.customData()
const customData = VoxEngine.customData();
if (customData) {
Logger.write(`Custom data received: ${customData}`);
handleCustomData(customData);
} else {
Logger.write("No custom data provided. Terminating.");
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()); // Выводим ответ в лог для отладки
}
© ProTalk 2023-2025 ИП Горелов Максим Николаевич ИНН 500104951533 ОГРН 309500106900065
Новая, 8, 9 Реутов, Московская область 143964 Россия