Commit 76bcfcec by Xander Fox

First commit

0 parents
node_modules/
package-lock.json
log.txt
MIT License
Copyright (c) 2020 Dasha Samples
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# How to start the application
marketplacea audit RB #111266
1. Clone the repo and install the dependencies:
```sh
git clone git@gitlab.veeble.ru:Polina/marketplaceaudit-dev.git
cd marketplaceaudit-dev
touch log.txt
mkdir -p stt audio
chmod a+rw log.txt logs threads audio app/phrasemap.json
npm install
```
2. To start a text chat, run:
```sh
npm start chat
```
3. To receive a phone call from Dasha, run:
```sh
npm start <your phone number>
```
The phone number should be in the international format without the `+` (e.g. `12223334455`)
{
"formatVersion": "2",
"dialogue": {
"file": "main.dsl"
},
"nlu": {
"language": "ru-RU",
"skills": [
"sentiment",
"common_phrases",
"common-numbers"
],
"customIntents": {
"file": "intents.json"
}
},
"nlg": {
"type": "phrases",
"file": "phrasemap.json",
"signatureFile": "phrasemap.json"
},
"name": "My_test_Fox"
}
library
import "canHearYou.dsl";
import "dontUnderstand.dsl";
import "hangup.dsl";
import "hello.dsl";
import "repeatAndPing.dsl";
//import "whatCompany.dsl";
import "iAmRobot.dsl";
import "ivr_machine.dsl";
library
digression call_operator
{
conditions { on #messageHasAnyIntent(digression.call_operator.triggers); }
var retriesLimit = 2;
var counter = 0;
var resetOnRecognized=false;
var triggers = ["call_operator"];
var responses: Phrases[] = ["call_operator"];
do
{
$cjm.push("call_operator");
for (var item in digression.call_operator.responses)
{
#say(item, repeatMode: "ignore");
}
#repeat(accuracy: "short");
set $time = 0;
return;
}
transitions
{
}
}
\ No newline at end of file
library
digression can_hear_you
{
conditions { on #messageHasAnyIntent(digression.can_hear_you.triggers) priority -100; }
var triggers = ["can_you_hear_me"];
var responses: Phrases[] = ["i_can_hear_you"];
do
{
for (var item in digression.can_hear_you.responses)
{
#say(item, repeatMode: "ignore");
}
set $time = 0;
return;
}
transitions
{
}
}
library
context
{
output status:string?;
output serviceStatus:string?;
}
digression dont_understand_hangup_params
{
conditions { on false; }
var responses: Phrases[] = ["dont_understand_hangup"];
var status = "DontUnderstandHangup";
var serviceStatus = "Done";
do
{
}
transitions
{
}
}
digression dont_understand
{
conditions { on true priority -1000; }
var retriesLimit=0;
var counter=0;
var resetOnRecognized=false;
var responses: Phrases[] = ["dont_understand"];
do
{
if (digression.dont_understand.counter > digression.dont_understand.retriesLimit)
{
goto hangup;
}
set digression.dont_understand.counter=digression.dont_understand.counter+1;
set digression.dont_understand.resetOnRecognized = false;
for (var item in digression.dont_understand.responses)
{
#say(item, repeatMode: "ignore");
//#say("dont_understand_question");
}
//#repeat(accuracy: "short");
set $time = 0;
return;
}
transitions
{
hangup: goto dont_understand_hangup;
}
}
preprocessor digression dont_understand_preprocessor
{
conditions { on true priority 50000; }
do
{
if (digression.dont_understand.resetOnRecognized)
{
set digression.dont_understand.counter = 0;
}
set digression.dont_understand.resetOnRecognized = true;
set $time = 0;
return;
}
transitions
{
}
}
node dont_understand_hangup
{
do
{
$cjm.push("dont_understand_hangup");
for (var item in digression.dont_understand_hangup_params.responses)
{
#say(item, repeatMode: "ignore");
}
set $status=digression.dont_understand_hangup_params.status;
set $serviceStatus=digression.dont_understand_hangup_params.serviceStatus;
//#forward("12223334455"); //use if you want to transfer a call
//#disconnect();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
}
library
context
{
output serviceStatus: string?;
}
digression hangup
{
conditions
{
on true tags: onclosed;
}
var serviceStatus = "UserHangup";
do
{
$cjm.push("hangup");
set $serviceStatus = digression.hangup.serviceStatus;
//#disconnect();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
}
library
/**
* Reaction if nothing meaningful happens in the dialogue for too long.
*/
preprocessor digression hello
{
conditions { on #getIdleTime() - digression.hello.lastIdleTime > digression.hello.idleTimeLimit tags: ontick; }
var idleTimeLimit=8000;
var lastIdleTime=0;
var retriesLimit=2;
var counter=0;
do
{
$cjm.push("hello_dig");
set digression.hello.lastIdleTime=#getIdleTime();
if(digression.hello.counter == 1){
set $conversation_result = "автоответчик";
}
if (digression.hello.counter == 2)
{
goto answering_machine;
}
set digression.hello.lastIdleTime=#getIdleTime();
/*if (digression.hello.counter > digression.hello.retriesLimit)
{
goto hangup;
}*/
set digression.hello.counter=digression.hello.counter+1;
#say("hello", repeatMode: "ignore");
// #say("dont_understand_question");
#repeat(accuracy: "short");
set $time = 0;
return;
}
transitions
{
answering_machine: goto answering_machine;
hangup: goto hello_hangup;
}
}
preprocessor digression hello_preprocessor
{
conditions { on true priority 50000; }
do
{
set digression.hello.lastIdleTime = 0;
set digression.hello.counter = 0;
set $time = 0;
return;
}
transitions
{
}
}
node hello_hangup
{
do
{
$cjm.push("hello_hangup");
#say("dont_understand_hangup");
set $status="EmptyCall";
set $serviceStatus="Done";
//#disconnect();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
}
library
digression i_am_robot
{
conditions { on #messageHasAnyIntent(digression.i_am_robot.triggers); }
var triggers = ["are_you_a_robot", "if_robot"];
var responses: Phrases[] = ["yes_i_am_a_robot"];
var counter = 0;
do
{
$cjm.push("i_am_robot");
if(digression.i_am_robot.counter == 1)
{
goto answering_machine;
}
set digression.i_am_robot.counter = digression.repeat.counter + 1;
#log(digression.i_am_robot.counter);
for (var item in digression.i_am_robot.responses)
{
#say(item, repeatMode: "ignore");
}
#repeat(accuracy: "short");
var result = blockcall SkipMessagesBlock();
return;
}
transitions
{
answering_machine: goto answering_machine;
}
}
library
preprocessor digression machine
{
conditions { on #messageHasIntent("robot_marker") || #messageHasIntent("answering_machine") priority 110000; }
var visited = 0;
do
{
$cjm.push("machine_dig");
digression disable hello;
set $conversation_result = "автоответчик";
set $status = "wait_answer";
set digression.machine.visited += 1;
//if(digression.machine.visited == 2 || (digression.wait_answer.visited == 1 && digression.machine.visited == 1)) {goto do_before_exit;}
return;
//wait *;
}
transitions
{
do_before_exit: goto do_before_exit;
cantalk: goto fitness_not_qualified on timeout 5000;
bot: goto answering_machine on #messageHasIntent("robot_marker") || #messageHasIntent("answering_machine");
someone: goto fitness_not_qualified on true;
//maybe_bot: goto answering_machine on timeout 5000;
}
}
digression wait_answer // дигрессии
{
conditions
{
on #messageHasIntent("robot_marker") && digression.wait_answer.visited < 3 priority 110000;
}
var visited = 0;
do
{
$cjm.push("wait_answer_dig");
digression disable hello;
set $status = "wait_answer";
set digression.wait_answer.visited += 1;
wait *;
}
transitions
{
bot: goto answering_machine on #messageHasIntent("robot_marker");
someone: goto fitness_not_qualified on true;
maybe_bot: goto answering_machine on timeout 10000;
}
}
digression ivr // дигрессии
{
conditions
{
on #messageHasIntent("robot_marker") && digression.wait_answer.visited == 0 && digression.ivr.visited < 3 priority 110000;
}
var visited = 0;
do
{
$cjm.push("ivr_dig");
set $status = "ivr";
set digression.ivr.visited += 1;
set $conversation_result = "автоответчик";
return;
}
transitions
{
}
}
node fitness_not_qualified
{
do
{
$cjm.push("fitness_not_qualified");
#say("greetings");
digression enable {repeat, hello};
#waitingMode(duration: 2000);
wait *;
}
transitions
{
targeted_action_vm_botmachine: goto answering_machine on #messageHasIntent("robot_marker");
bot: goto its_machine on #messageHasIntent("robot_marker");
/* what_webinar: goto webinar_name on #messageHasIntent("what_webinar");
other: goto been_at_webinar on #messageHasAnyIntent(["what_question",
"who_are_you",
"where_my_number"]);
positive: goto been_at_webinar on #messageHasAnyIntent(["accept", "may_continue"]) || #messageHasSentiment("positive");
negative: goto call_later on #messageHasAnyIntent(["decline", "cant_talk_rn"]) || #messageHasSentiment("negative"); */
}
}
node its_machine
{
do
{
$cjm.push("its_machine");
set $status = "voice_menu";
wait *;
}
transitions
{
//someone: goto fitness_not_qualified on #messageHasIntent("can_you_hear_me") priority -100;
bot: goto answering_machine on #messageHasIntent("robot_marker");
someone: goto fitness_not_qualified on true;
maybe_bot: goto answering_machine on timeout 5000;
//targeted_action_vm_nqa_1: goto fitness_not_qualified on true;
}
}
node answering_machine
{
do
{
$cjm.push("answering_machine");
//#waitingMode(duration: 1500);
digression disable {repeat, dont_understand, can_hear_you};
set $status = "answering_machine";
goto do_before_exit;
//exit;
}
transitions
{
do_before_exit: goto do_before_exit;
//targeted_action_bot_yes: goto fitness_validation_yes;
//off_target_action_am: goto root;
}
onexit
{
default: do
{
set $conversation_result = "автоответчик";
}
}
}
\ No newline at end of file
library
preprocessor digression repeat_preprocessor
{
conditions { on true priority 50000; }
var retriesLimit=2;
var counter=0;
do
{
if (digression.repeat.resetOnRecognized)
{
set digression.repeat.counter = 0;
#log(digression.repeat.counter);
}
set digression.repeat.resetOnRecognized = true;
#log(digression.repeat.resetOnRecognized);
set $time = 0;
return;
}
transitions
{
}
}
digression repeat_hangup_params
{
conditions { on false; }
var responses: Phrases[] = ["dont_understand_hangup"];
var status = "RepeatHangup";
var serviceStatus = "Done";
do
{
}
transitions
{
}
}
digression repeat
{
conditions { on #messageHasAnyIntent(digression.repeat.triggers) && !#messageHasIntent("i_dont_intersted"); }
var retriesLimit = 2;
var counter = 0;
var resetOnRecognized=false;
var triggers = ["repeat", "dont_understand", "what"];
var responses: Phrases[] = ["i_said"];
do
{
$cjm.push("repeat_dig");
if (digression.repeat.counter == digression.repeat.retriesLimit)
{
goto hangup;
}
set digression.repeat.counter = digression.repeat.counter + 1;
#log(digression.repeat.counter);
set digression.repeat.resetOnRecognized = false;
for (var item in digression.repeat.responses)
{
#say(item, repeatMode: "ignore");
}
#repeat(accuracy: "short");
set $time = 0;
return;
}
transitions
{
hangup: goto repeat_or_ping_hangup;
}
}
digression ping
{
conditions { on #messageHasAnyIntent(digression.ping.triggers) and !#messageHasIntent("greeting") priority -100; }
var retriesLimit = 2;
var counter = 0;
var resetOnRecognized=false;
var triggers = ["ping"];
do
{
$cjm.push("ping_dig");
if(digression.repeat.counter == 1){
set $conversation_result = "автоответчик";
}
if (digression.repeat.counter == digression.repeat.retriesLimit)
{
goto hangup;
}
set digression.repeat.counter = digression.repeat.counter + 1;
#log(digression.repeat.counter);
set digression.repeat.resetOnRecognized = false;
#repeat(accuracy: "short");
set $time = 0;
return;
}
transitions
{
hangup: goto repeat_or_ping_hangup;
}
}
node repeat_or_ping_hangup
{
do
{
$cjm.push("repeat_or_ping_hangup");
for (var item in digression.repeat_hangup_params.responses)
{
#say(item, repeatMode: "ignore");
}
set $status=digression.repeat_hangup_params.status;
set $serviceStatus=digression.repeat_hangup_params.serviceStatus;
set $conversation_result = "автоответчик";
//#disconnect();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
}
library
block doWait():doWaitMore {
start node root {
do {
#log("block 'doWait'");
wait*;
}
transitions {
transition1: goto wait_for_abonent_speech on timeout 5000;
}
}
node wait_for_abonent_speech {
do {
#sayText("Вы готовы продолжить?", repeatMode: "ignore");
wait*;
}
transitions {
yes: goto stopwaiting on #messageHasSentiment("positive") || #messageHasIntent("ready");
maybe: goto root on true && !#messageHasSentiment("positive") && !#messageHasIntent("ready") && !#messageHasSentiment("negative");
no: goto waiting on #messageHasSentiment("negative");
}
}
node waiting {
do {
#sayText("Жду.", repeatMode: "ignore");
goto root;
}
transitions{
root: goto root;
}
}
node stopwaiting {
do {
// return value from block
return "yes";
}
}
}
digression wait_for_abonent
{
conditions { on #messageHasAnyIntent(digression.wait_for_abonent.triggers); }
var triggers = ["wait", "waitfor"];
var responses: Phrases[] = ["imwaiting"];
do
{
$cjm.push("wait_for_abonent");
for (var item in digression.wait_for_abonent.responses)
{
#say(item, repeatMode: "ignore");
}
var block_result = blockcall doWait();
if (block_result == "yes")
{
#sayText("Давайте продолжим", repeatMode: "ignore");
#repeat(accuracy:"short");
return;
}
}
transitions
{
}
}
\ No newline at end of file
library
digression what_company
{
conditions { on #messageHasAnyIntent(["what_company", "common_what_company", "who_are_you"]); }
var retriesLimit = 2;
var counter = 0;
var resetOnRecognized=false;
//var triggers = ["what_company"];
var responses: Phrases[] = ["who_are_you"];
do
{
$cjm.push("what_company");
for (var item in digression.what_company.responses)
{
#say(item, repeatMode: "ignore");
}
// #repeat(accuracy: "repeat");
var result = blockcall SkipMessagesBlock();
return;
}
transitions
{
}
}
\ No newline at end of file
library
digression when_callback
{
conditions { on #messageHasIntent("when_callback") && !#messageHasIntent("notAboutCallback") priority 100; }
var responses: Phrases[] = ["willcallback"];
do
{
$cjm.push("when_callback");
for (var item in digression.when_callback.responses)
{
#say(item, repeatMode: "ignore");
//#sayText("У вас остались еще вопросы?");
}
#repeat(accuracy:"short");
return;
}
transitions
{
}
}
This diff could not be displayed because it is too large.
import "commonReactions/all.dsl";
context {
input phone: string;
input caller: string;
output cjm: string[] = [];
output conversation_start: number = 0;
output conversation_begin: number = 0;
output conversation_stop: number = 0;
output conversation_status: string = "normal";
output conversation_result: string = "Нет ответа";
output time: number = 0;
// Сохраняемая информация по клиенту
output client_agreed: boolean = false;
}
external function numbers_from_text(text: string): string;
external function dates_from_text(text: string): string;
external function part_of_the_day(): string;
external function is_empty(check: unknown): boolean;
external function sleep_ms(duration: number): unknown;
external function json_encode(object: unknown): string;
external function math_floor(value: number, presision: number|empty = 0): number;
external function math_round(value: number, presision: number|empty = 0): number;
external function math_ceil(value: number, presision: number|empty = 0): number;
external function phone_human(phone: string): string;
external function last_four(phone: string): string;
external function hours_now(): number;
external function check_mobile_code(phone: string): boolean;
external function countWords(message: string): number;
block SkipMessagesBlock():boolean
{
start node root
{
do
{
return true;
}
}
}
start node root
{
do
{
$cjm.push("root");
set $conversation_start = #getCurrentTime();
goto caller_id;
}
transitions
{
caller_id: goto caller_id;
}
}
node caller_id
{
do
{
$cjm.push("caller_id");
#connectSafe($phone);
#waitForSpeech(2000);
// Запоминаем временной штамп перед самой первой репликой
set $conversation_begin = #getCurrentTime();
if (#say("greetings", interruptible: true, options: { interruptConditions: ["robot_marker", "answering_machine"], repeatMode: "ignore" }))
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
bot: goto its_machine on #messageHasAnyIntent(["robot_marker", "answering_machine"]);
positive: goto presentation on #messageHasAnyIntent(["accept" ,"agree"], "positive");
what_question: goto presentation on #messageHasIntent("what_question");
cant_talk: goto letme_40sec on #messageHasAnyIntent(["cant_talk_rn", "call_later", "decline"]);
time: goto presentation on timeout 2000;
}
onexit
{
bot: do
{
set $conversation_result = "автоответчик";
}
}
}
node presentation
{
do
{
$cjm.push("presentation");
if (#say("presentation", interruptible: true, options: {repeatMode: "short" }))
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"], "positive");
negative: goto yandex_guaranteed on #messageHasIntent("decline", "negative");
}
onexit
{
}
}
node letme_40sec
{
do
{
$cjm.push("letme_40sec");
digression disable {wait_answer, machine};
#say("letme_40sec");
var result = blockcall SkipMessagesBlock();
wait*;
}
transitions
{
positive: goto presentation on #messageHasAnyIntent(["accept", "agree"], "positive");
negative: goto call_later on #messageHasAnyIntent(["decline", "not_interested"], "negative");
}
onexit
{
}
}
node yandex_guaranteed
{
do
{
$cjm.push("yandex_guaranteed");
if (#say("yandex_guaranteed", interruptible: true, options: {repeatMode: "short" }))
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"], "positive");
negative: goto decline on #messageHasAnyIntent(["decline", "not_interested"], "negative");
}
onexit
{
}
}
node call_later
{
do
{
$cjm.push("call_later");
#say("call_later");
var result = blockcall SkipMessagesBlock();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
onexit
{
do_before_exit: do
{
set $conversation_result = "перезвонить";
}
}
}
node last_word
{
do
{
$cjm.push("last_word");
if (#say("last_word", repeatMode: "ignore"))
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
any: goto acception on true;
}
}
node acception
{
do
{
$cjm.push("acception");
#say("acception");
var result = blockcall SkipMessagesBlock();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
onexit
{
do_before_exit: do
{
set $conversation_result = "ожидает звонка специалиста";
}
}
}
node decline
{
do
{
$cjm.push("decline");
#say("decline");
var result = blockcall SkipMessagesBlock();
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
onexit
{
do_before_exit: do
{
set $conversation_result = "действий не требуется";
}
}
}
node do_before_exit
{
do
{
$cjm.push("do_before_exit");
set $conversation_stop = #getCurrentTime();
if ($conversation_status == "broken" && $conversation_result == "") { set $conversation_result = "сбросил трубку"; }
if ($conversation_result == "") { set $conversation_result = "непредвиденное завершение"; }
exit;
}
}
digression @exit_dig
{
conditions { on true tags: onclosed; }
do
{
$cjm.push("@exit_dig");
set $conversation_status = "broken";
goto do_before_exit;
}
transitions
{
do_before_exit: goto do_before_exit;
}
}
global digression @exit_dig_global
{
conditions { on true tags: onclosed; }
do
{
$cjm.push("@exit_dig");
set $conversation_status = "broken";
set $conversation_stop = #getCurrentTime();
if ($conversation_status == "broken" && $conversation_result == "") { set $conversation_result = "сбросил трубку"; }
if ($conversation_result == "" ) { set $conversation_result = "непредвиденное завершение"; }
exit;
}
}
// Дигрессии с возможным положительным исходом
digression what_company
{
conditions { on #messageHasAnyIntent(["what_company", "common_what_company", "who_are_you"]); }
var retriesLimit = 2;
var counter = 0;
var resetOnRecognized=false;
var responses: Phrases[] = ["who_are_you"];
do
{
$cjm.push("what_company");
for (var item in digression.what_company.responses)
{
#say(item, repeatMode: "ignore");
}
var result = blockcall SkipMessagesBlock();
return;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
}
digression city_address
{
conditions { on #messageHasIntent("city_address"); }
do
{
$cjm.push("city_address");
digression disable { city_address };
#say("city_address", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression what_price
{
conditions { on #messageHasIntent("what_price"); }
do
{
$cjm.push("what_price");
digression disable { what_price };
#say("what_price", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression send_on_mail
{
conditions { on #messageHasIntent("send_on_mail"); }
do
{
$cjm.push("send_on_mail");
digression disable { send_on_mail };
#say("send_on_mail", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression guarantee
{
conditions { on #messageHasIntent("guarantee"); }
do
{
$cjm.push("guarantee");
digression disable { guarantee };
#say("guarantee", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression what_conditions
{
conditions { on #messageHasIntent("what_conditions"); }
do
{
$cjm.push("what_conditions");
digression disable { what_conditions };
#say("what_conditions", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression what_service
{
conditions { on #messageHasIntent("what_service"); }
do
{
$cjm.push("what_service");
digression disable { what_service };
#say("what_service", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression what_difference
{
conditions { on #messageHasIntent("what_difference"); }
do
{
$cjm.push("what_difference");
digression disable { what_difference };
#say("what_difference", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression already_using
{
conditions { on #messageHasIntent("already_using"); }
do
{
$cjm.push("already_using");
digression disable { already_using };
#say("already_using", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression not_social
{
conditions { on #messageHasIntent("not_social"); }
do
{
$cjm.push("not_social");
digression disable { not_social };
#say("not_social", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression our_site
{
conditions { on #messageHasIntent("our_site"); }
do
{
$cjm.push("our_site");
digression disable { our_site };
#say("our_site", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
digression alternatives
{
conditions { on #messageHasIntent("alternatives"); }
do
{
$cjm.push("alternatives");
digression disable { alternatives };
#say("alternatives", repeatMode: "ignore");
var result = blockcall SkipMessagesBlock();
wait *;
}
transitions
{
positive: goto last_word on #messageHasAnyIntent(["accept", "agree"]);
negative: goto yandex_guaranteed on #messageHasAnyIntent(["not_interested", "decline"]);
}
onexit
{
}
}
// Дигрессии с негативным исходом
digression refusenik
{
conditions { on #messageHasAnyIntent(["not_lpr", "closed"]); }
do
{
$cjm.push("refusenik");
digression disable { refusenik };
var result = blockcall SkipMessagesBlock();
return;
}
transitions
{
decline: goto decline on true;
}
onexit
{
}
}
{
"default": {
"voiceInfo": {
"emotion": "good",
"lang": "ru-RU",
"speaker": "alena",
"speed": 1.0
},
"phrases": {
"hello": {
"random": [
[
{
"text": "Алло, день добрый."
}
],
[
{
"text": "Ало?"
}
],
[
{
"text": "Вы меня слышите?"
}
]
]
},
"greetings": [
{
"text": "Добрый день! Здравствуйте, это Денис, агентство Р-брокер, мы официальные партнёры Яндекса, я насчёт профиля вашей организации на Яндекс.Картах, удобно говорить?"
}
],
"presentation": {
"first": [
{
"text": "Мы представляем инструмент для автоматизации рекламы на картах, - Яндекс подписку, которая позволяет привлекать новых клиентов. Хотели бы со специалистом пообщаться, он подробнее об условиях расскажет?"
}
],
"short": [
{
"text": "Интересно ли вам привлекать новых клиентов в свой бизнес через новый канал?"
}
]
},
"last_word": [
{
"text": "Хорошо, я передам всё менеджеру, он вам перезвонит и подробней проконсультирует."
}
],
"acception": [
{
"text": "Спасибо."
}
],
"decline": [
{
"text": "Спасибо, что уделили время! До свидания"
}
],
"yandex_guaranteed": {
"first": [
{
"text": "Яндекс гарантирует поток заинтересованных клиентов. А стоимость будет всего от трёх тысяч в месяц. Может, всё же обсудите с менеджером выгоду и там уже примете решение, что скажете?"
}
],
"short": [
{
"text": "Смотрите, стоимость будет всего от трёх тысяч рублей, а результат точно будет, так как Яндекс гарантирует поток заинтересованных клиентов. Давайте я ваш контакт передам менеджеру, он обо всём расскажет, хорошо?"
}
]
},
"letme_40sec": [
{
"text": "Я всего в 40 секундах, позволите?"
}
],
"call_later": [
{
"text": "Хорошо, я перезвоню Вам позднее, до свидания!"
}
],
"who_are_you": [
{
"text": "Меня зовут Денис, агентство ЭР-брокер, партнёры Яндекса. Мы представляем инструмент для автоматизации рекламы на картах, - Яндекс подписку, которая позволяет привлекать новых клиентов. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
},
{
"text": "Агентство Р-брокер, мы официальные партнёры Яндекса. представляем инструмент для автоматизации рекламы на картах, - Яндекс подписку, которая позволяет привлекать новых клиентов. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"city_address": [
{
"text": "Наш главный офис находится в Москве, а так мы работаем онлайн по всей России. Мы официальные партнёры Яндекса. Представляем инструмент для автоматизации рекламы на картах, - Яндекс подписку, которая позволяет привлекать новых клиентов. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"what_price": [
{
"text": "Смотрите, стоимость будет всего от трёх тысяч рублей, а результат точно будет, так как Яндекс гарантирует поток заинтересованных клиентов. Давайте я ваш контакт передам менеджеру, он обо всём расскажет, хорошо?"
}
],
"send_on_mail": [
{
"text": "Для составления кп нам нужно собрать ваши вводные данные по компании. результат точно будет, так как Яндекс гарантирует поток заинтересованных клиентов. Давайте я ваш контакт передам менеджеру, он обо всём расскажет, хорошо?"
}
],
"guarantee": [
{
"text": "Количество привлекаемых клиентов будет отображено в договоре так как Яндекс гарантирует поток заинтересованных клиентов. Давайте я ваш контакт передам менеджеру, он обо всём расскажет, хорошо?"
}
],
"what_conditions": [
{
"text": "Более подробно про все условия сможет расскажет менеджер, в любом случае эта бесплатная консультация. Давайте я передам информацию менеджеру и он вам наберет, хорошо?"
}
],
"what_service": [
{
"text": "Сервис автоматизирует привлечение клиентов, - это размещение рекламы на сайтах партнёров Яндекса, в поисковике. А самое эффективное, это привлечение клиентов через геолокацию в Яндекс.Картах. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"what_difference": [
{
"text": "Мы предлагаем ту же стоимость что и Яндекс, но у нас есть ряд преимуществ, - это возможность рассрочки, свои специалисты для правильной настройки рекламы и своя поддержка, готовая помочь в любую минуту. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"already_using": [
{
"text": "В таком случае, можем продлить вашу подписку на более выгодных условиях с рассрочкой платежей и бесплатными дополнительными услугами. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"not_social": [
{
"text": "Для запуска рекламы в яндекс подписке необязательно иметь сайт или соц.сеть. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"our_site": [
{
"text": "У нас есть сайт, можно вбить r-broker в поисковике. Подробней можно как раз с нашим экспертом обсудить, вам было бы интересно?"
}
],
"alternatives": [
{
"text": "А вообще альтернативы рассматриваете? Просто наш специалист может связаться с вами, подробней проконсультировать, оцените выгоду и там уже примете решение, что скажете?"
}
],
"dont_understand": {
"random": [
[
{
"text": "Извините, можете повторить?"
}
],
[
{
"text": "Извините, что то со связью, можете повторить?"
}
],
[
{
"text": "Извините, не расслышала"
}
]
]
},
"dont_understand_hangup": [
{
"text": "Что то со связью, плохо вас слышно. Я перезвоню вам позже. До свидания!"
}
],
"dont_understand_forward": [
{
"text": "Вы знаете, я вас очень плохо слышу. Я перезвоню вам позже"
}
],
"dont_understand_question": [
{
"text": "Повторите пожалуйста"
}
],
"i_can_hear_you": [
{
"text": "Да. Я вас слышу."
}
],
"i_said": [
{
"text": "Я говорю"
}
],
"yes_i_am_a_robot": [
{
"text": "Да, я робот. Надеюсь это не повлияет на наш дальнейший диалог"
}
]
},
"types": {},
"macros": {
"hello": {},
"greetings": {},
"presentation": {},
"last_word": {},
"acception": {},
"decline": {},
"yandex_guaranteed": {},
"fourty_seconds": {},
"call_later": {},
"dont_understand": {},
"dont_understand_hangup": {},
"dont_understand_forward": {},
"dont_understand_question": {},
"i_can_hear_you": {},
"i_said": {},
"yes_i_am_a_robot": {}
}
}
}
\ No newline at end of file
{
"node::root": {
"common.position": {
"x": 600,
"y": 0
}
},
"node::caller_id": {
"common.position": {
"x": 598.377372534425,
"y": 134.60160992786908
}
},
"node::end_conversation": {
"common.position": {
"x": 600,
"y": 1090.3536507636595
}
},
"node::do_before_exit": {
"common.position": {
"x": 600,
"y": 709.2035244482049
}
},
"node::repeat_or_ping_hangup": {
"common.position": {
"x": 0,
"y": 606.0839340885684
}
},
"node::hello_hangup": {
"common.position": {
"x": 0,
"y": 311.682305525608
}
},
"node::dont_understand_hangup": {
"common.position": {
"x": -3.3135942327497423,
"y": 909.3975283213182
}
},
"node::conversation_done": {
"common.position": {
"x": 1480,
"y": 966.137946142489
}
},
"node::wait_message": {
"common.position": {
"x": 2080,
"y": 644.8260546478509
}
},
"node::get_abonent_name": {
"common.position": {
"x": 1480,
"y": 851.0084920989109
}
},
"node::check_abonent_name": {
"common.position": {
"x": 2080,
"y": 893.0535799922327
}
},
"node::say_wrote": {
"common.position": {
"x": 2080,
"y": 770.7061811030865
}
},
"node::add_message": {
"common.position": {
"x": 1480,
"y": 533.057587652757
}
},
"node::ping": {
"common.position": {
"x": -600,
"y": 397.8398558187435
}
},
"node::repeat": {
"common.position": {
"x": 0,
"y": 703.8748712667353
}
},
"node::repeat_hangup_params": {
"common.position": {
"x": 600,
"y": 530
}
},
"node::repeat_preprocessor": {
"common.position": {
"x": 600,
"y": 475
}
},
"node::hello_preprocessor": {
"common.position": {
"x": 600,
"y": 585
}
},
"node::hello": {
"common.position": {
"x": 0,
"y": 115
}
},
"node::hangup": {
"common.position": {
"x": 0,
"y": 496.4410467506296
}
},
"node::dont_understand_preprocessor": {
"common.position": {
"x": 600,
"y": 370
}
},
"node::dont_understand": {
"common.position": {
"x": 0,
"y": 1015.442842430484
}
},
"node::dont_understand_hangup_params": {
"common.position": {
"x": 600,
"y": 420
}
},
"node::can_hear_you": {
"common.position": {
"x": 600,
"y": 645
}
},
"node::exit_dig": {
"common.position": {
"x": 0,
"y": 800.5612770339856
}
},
"node::no_message": {
"common.position": {
"x": 1480,
"y": 734.0697783815012
}
},
"node::non_work_branch": {
"common.position": {
"x": 1046.6676796891302,
"y": 324.76777774720415
}
},
"node::request_accepted": {
"common.position": {
"x": 2680,
"y": 1029.454349255128
}
},
"node::get_client_questions": {
"common.position": {
"x": 2680,
"y": 868.8603922782834
}
},
"node::get_cell_phone": {
"common.position": {
"x": 3280,
"y": 575.5801672846062
}
},
"node::is_shared_phone": {
"common.position": {
"x": 3280,
"y": 389.5873927822081
}
},
"node::check_shared_phone": {
"common.position": {
"x": 2680,
"y": 679.7473236381277
}
},
"node::get_company_name": {
"common.position": {
"x": 2680,
"y": 224.565654864841
}
},
"node::get_another_phone": {
"common.position": {
"x": 3280,
"y": 10.131652410874324
}
},
"node::phone_confirmation": {
"common.position": {
"x": 2680,
"y": 42.55689227944265
}
},
"node::ask_client_name": {
"common.position": {
"x": 2080,
"y": 306.0375290687854
}
},
"node::check_client_name": {
"common.position": {
"x": 2080,
"y": 148.12143450145916
}
},
"node::by_work_branch": {
"common.position": {
"x": 2080,
"y": 26.98742096578726
}
},
"node::get_alter_company_name": {
"common.position": {
"x": 3280,
"y": 136.54309920643223
}
},
"node::check_mobile_code": {
"common.position": {
"x": 2680,
"y": 425.40569848894876
}
},
"node::answering_machine": {
"common.position": {
"x": 0,
"y": -688.1700160056909
}
},
"node::its_machine": {
"common.position": {
"x": 0,
"y": -590.5139605192957
}
},
"node::fitness_not_qualified": {
"common.position": {
"x": 0,
"y": -411.9864840832295
}
},
"node::decline": {
"common.position": {
"x": -1800,
"y": 1312.1335523649486
}
},
"node::acception": {
"common.position": {
"x": -1804.3861967559367,
"y": 808.0569466968057
}
},
"node::last_word": {
"common.position": {
"x": -1201.3545571151494,
"y": 1388.8344130494233
}
},
"node::call_later": {
"common.position": {
"x": -1200,
"y": 72.00052656883884
}
},
"node::yandex_guaranteed": {
"common.position": {
"x": -1200,
"y": 1765.9588036973776
}
},
"node::letme_40sec": {
"common.position": {
"x": 0,
"y": -260.92477325271204
}
},
"node::presentation": {
"common.position": {
"x": -600,
"y": -540.1600569091232
}
},
"node:SkipMessagesBlock:root": {
"common.position": {
"x": 755.3085541525875,
"y": -445.5557531566779
}
},
"node::ivr": {
"common.position": {
"x": 765.989685221412,
"y": -552.3670638449225
}
},
"node::wait_answer": {
"common.position": {
"x": -600,
"y": -776.6708162902364
}
},
"node::machine": {
"common.position": {
"x": 600,
"y": -891.1115063133558
}
},
"node::i_am_robot": {
"common.position": {
"x": 280,
"y": -1013.1815756713497
}
},
"node::exit_dig_global": {
"common.position": {
"x": 790.4036990930108,
"y": -346.3738218033078
}
},
"node::refusenik": {
"common.position": {
"x": 0,
"y": 1162.9292211584047
}
},
"node::alternatives": {
"common.position": {
"x": 0,
"y": 1259.2497900241433
}
},
"node::our_site": {
"common.position": {
"x": 0,
"y": 1397.2997612292656
}
},
"node::not_social": {
"common.position": {
"x": 0,
"y": 1537.6226509708185
}
},
"node::already_using": {
"common.position": {
"x": 0,
"y": 1675.9691619836171
}
},
"node::what_difference": {
"common.position": {
"x": 0,
"y": 1825.8001562284296
}
},
"node::what_service": {
"common.position": {
"x": 0,
"y": 1977.049619638246
}
},
"node::what_conditions": {
"common.position": {
"x": 0,
"y": 2118.1209358672963
}
},
"node::guarantee": {
"common.position": {
"x": 0,
"y": 2264.198241789179
}
},
"node::send_on_mail": {
"common.position": {
"x": 0,
"y": 2407.466368751025
}
},
"node::what_price": {
"common.position": {
"x": 0,
"y": 2545.9521738733774
}
},
"node::city_address": {
"common.position": {
"x": 0,
"y": 2689.7543505748263
}
},
"node::what_company": {
"common.position": {
"x": -3.268231288669291,
"y": -29.414081598023614
}
}
}
\ No newline at end of file
File mode changed
const fs = require("fs");
const path = require("path");
class AudioResources
{
constructor()
{
this.resources = {};
}
getResourceKey(text, voiceInfo) {
return [
voiceInfo.lang ?? "",
voiceInfo.speaker ?? "",
voiceInfo.emotion ?? "Neutral",
voiceInfo.speed ?? 1,
voiceInfo.variation ?? 0,
text,
].join("|");
}
async appendJson(folder, pack)
{
const errors = [];
let i = 0;
for (const phrase of pack.phrases ?? []) {
i++;
// валидация
if (phrase.phrase === undefined) {
errors.push(`For ${i} phrase field 'phrase' is undefined`);
continue;
}
if (phrase.audio === undefined) {
errors.push(`For ${i} phrase field 'audio' is undefined`);
continue;
}
const audioFile = path.join(folder, phrase.audio);
if (!fs.existsSync(audioFile)) {
errors.push(`For ${i} phrase '${phrase.phrase}' file not found`);
continue;
}
phrase.voice = { ...pack.voice, ...phrase.voice };
const resourceKey = this.getResourceKey(phrase.phrase, phrase.voice ?? {});
if (resourceKey in this.resources) {
// дубликат - не ошибка
console.warn("Skip", i, "phrase because it's duplicate");
continue;
}
this.resources[resourceKey] = audioFile;
}
if (errors.length > 0)
{
console.error(`Was errors: ${JSON.stringify(errors, undefined, 2)}`);
return false;
}
return true;
}
async addFolder(folder)
{
const files = fs.readdirSync(folder);
let result = false;
for (const fileName of files)
{
if (path.extname(fileName) === ".json")
{
const fname = path.join(folder, fileName);
console.log(`Parsing ${fname}`);
result = await this.appendJson(folder, JSON.parse(fs.readFileSync(fname).toString()));
}
}
return result;
}
GetPath(text, voiceInfo)
{
const key = this.getResourceKey(text, voiceInfo);
const fpath = this.resources[key];
if (fpath === undefined)
throw new Error(`Failed to get ${key}`);
return fpath;
}
}
module.exports = AudioResources;
\ No newline at end of file
module.exports = exports = {
mobile_codes() {
return [
"900", "902", "903",
"904", "905", "906",
"908", "909", "950",
"951", "953", "960",
"961", "962", "963",
"964", "965", "966",
"967", "968", "969",
"980", "983", "986",
"901", "910", "911",
"912", "913", "914",
"915", "916", "917",
"918", "919", "978",
"981", "982", "984",
"985", "987", "988",
"989", "920", "921",
"922", "923", "924",
"925", "926", "927",
"928", "929", "930",
"931", "932", "933",
"934", "936", "937",
"938", "939", "999",
"952", "958", "977",
"991", "992", "993",
"994", "995", "996"
];
}
};
\ No newline at end of file
const dasha = require("@dasha.ai/sdk");
const AudioResources = require("./customTts.js");
const fs = require("fs");
const sys = require('child_process');
const shared = require("./shared");
const { exec } = require('child_process');
const local_num = "4054";
const events_owner = "155";
const sip_config = "marketplacerb";
const preset_template_id = 1;
const logs_path = "./logs/";
const file_mime_type = "text/plain";
const audio_storage = "audio";
let prerecorded = true;
function nextInterval(iterationNum) {
var x = iterationNum;
// Максимальный период ожидания 10 минут
var msMax = 600000;
// Прогрессивный период ожидания
var msWait = x * x * 1000;
// Возвращаем мниммльное из двух значений
return Math.min(msMax, msWait);
}
async function main() {
const app_suffix = shared.pid_name();
const app_work_dir = shared.make_unique_app(app_suffix);
const app = await dasha.deploy(app_work_dir, {
// Указываем название рабочей группы
groupName: "Default",
// Указываем количество попыток соединения с
// серверами Dasha.AI при потере соединения
// до остановки приложения из-за потери связи
maxReconnectCount: 100,
// Указываем функцию вычисления ожидания до
// следующей попытки соединения с серверами
reconnectInterval: x => nextInterval(x)
});
if (app_work_dir !== './app') shared.drop_unique_app(app_suffix);
const audio = new AudioResources();
if (prerecorded) prerecorded = await audio.addFolder(audio_storage);
app.ttsDispatcher = (conv) => "custom";
app.customTtsProvider = async (text, voice) => {
console.log(`Tts asking for phrase with text ${text} and voice ${JSON.stringify(voice)}`);
const fname = audio.GetPath(text, voice);
console.log(`Found in file ${fname}`);
return dasha.audio.fromFile(fname);
};
app.setExternal("numbers_from_text", async (args) => {
return await shared.cct_numbers_from_text(args.text);
});
app.setExternal("dates_from_text", async (args) => {
return await shared.cct_dates_from_text(args.text);
});
app.setExternal("part_of_the_day", (args, conv) => {
return shared.get_part_of_the_day();
});
app.setExternal("array_size", (args, conv) => {
const arr = args.arr;
return arr.length;
});
app.setExternal("string_trim", (args, conv) => {
const str = args.one_line;
return str.trim();
});
app.setExternal("is_empty", (args, conv) => {
return shared.empty(args.check);
});
app.setExternal("performed_stage", (args, conv) => {
const arr = args.stages;
const val = args.stage;
arr.push(val);
return arr;
});
app.setExternal("sleep_ms", async (args, conv) => {
return new Promise((resolve) => {
setTimeout(resolve, args.duration);
});
});
app.setExternal("json_encode", async (args, conv) => {
return JSON.stringify(args.object, undefined, 2);
});
app.setExternal("math_floor", async (args, conv) => {
return Math.floor(args.value, args.presision);
});
app.setExternal("math_round", async (args, conv) => {
return Math.round(args.value, args.presision);
});
app.setExternal("math_ceil", async (args, conv) => {
return Math.ceil(args.value, args.presision);
});
app.setExternal("phone_human", async (args, conv) => {
return shared.human_format(args.phone);
});
app.setExternal("last_four", async (args, conv) => {
return shared.last_four_digits(args.phone);
});
app.setExternal("check_mobile_code", async (args, conv) => {
return shared.is_mobile_code(args.phone);
});
app.setExternal("hours_now", async (args, conv) => {
let date_time_now = new Date(ts);
return date_time_now.getHours();
});
app.setExternal("countWords", (args) => {
return args.message.split(' ').length;
});
var _msg = "";
var txtFilePath = "";
app.setExternal("sendTelegram", async (args)=> {
var caller_phone_ = caller_phone;
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', 'second': 'numeric'};
var time_mark_ = new Date().toLocaleDateString("ru-RU", options);
var txtdata = await fs.promises.readFile(txtFilePath, { encoding: 'utf8' });
var txtDataNew = txtdata.substring(txtdata.indexOf("\n") + 1); //обрезаем 1 строку с телефоном и датой звонка
var message = {
caller_phone: caller_phone_,
time_mark: time_mark_,
txtData: txtDataNew
};
_msg = message;
return true;
});
await app.start();
let abonent_phone = process.argv[2] ?? "";
let caller_phone = process.argv[3] ?? abonent_phone;
const input_data = {
phone: abonent_phone,
caller: caller_phone,
waiting: 0,
about_abonent: ["phone", "first name", "second name", "last name", "comments"],
about_fields: ["+7 (906) 781-51-65", "Николай", "Николаевич", "Сухарев", "основной сотовый"]
};
const conv = app.createConversation(input_data);
const audioChannel = conv.input.phone !== "chat";
if (audioChannel) {
conv.sip.config = sip_config;
if (prerecorded) conv.audio.tts = 'custom';
conv.on("transcription", console.log);
} else {
await dasha.chat.createConsoleChat(conv);
}
// Устанавливаем минимальный уровень фонового шума
conv.audio.noiseVolume = 0.1;
let debugItem = 0;
// current timestamp in milliseconds
let ts = Date.now();
// Get now date info
let date_ob = new Date(ts);
let date_day = date_ob.getDate();
let month = date_ob.getMonth() + 1;
let year = date_ob.getFullYear();
let hours = date_ob.getHours();
let minutes = date_ob.getMinutes();
let seconds = date_ob.getSeconds();
let unique = ts / 1000;
if (month < 10) month = '0' + month;
if (date_day < 10) date_day = '0' + date_day;
if (hours < 10) hours = '0' + hours;
if (minutes < 10) minutes = '0' + minutes;
if (seconds < 10) seconds = '0' + seconds;
// Make date and time string for log & debug file
let solid_date = year + '' + month + '' + date_day;
let solid_time = hours + '' + minutes + '' + seconds;
let time_mark = year + '-' + month + '-' + date_day
+ ' ' + hours + ':' + minutes + ':' + seconds;
let toUser = conv.input.phone;
// Check PBX phone prefix and cut it
let check_prefix = toUser.substring(0, 4);
if (local_num == check_prefix) toUser = toUser.substring(4);
// Make log & debug files names
let txt_file_name = logs_path + 'out-' + toUser + '-'
+ local_num + '-' + solid_date + '-'
+ solid_time + '-' + unique + '.txt';
let log_file_name = logs_path + 'out-' + toUser + '-'
+ local_num + '-' + solid_date + '-'
+ solid_time + '-' + unique + '.log';
let debug_file_name = logs_path + 'out-' + toUser + '-'
+ local_num + '-' + solid_date + '-'
+ solid_time + '-' + unique + '.debug';
// Open text, log and debug files for write
txtFilePath = txt_file_name;
const txtFile = await fs.promises.open(txt_file_name, "w");
const logFile = await fs.promises.open(log_file_name, "w");
const debugFile = await fs.promises.open(debug_file_name, "w");
caller_phone = shared.human_format(conv.input.caller);
let txtData = "\t" + caller_phone + "\t-=#=-\t" + time_mark + "\n\n";
await txtFile.appendFile(txtData);
debugString = 'Initialize call to: ' + toUser + "\n";
await debugFile.appendFile(debugString);
if (audioChannel) console.log(debugString);
logString = 'Conversation started to: ' + toUser
+ "\n\nConversation replics:\n";
await logFile.appendFile(logString);
conv.on("transcription", async (entry) => {
rts = entry.startTime.getTime();
dts = rts - ts;
dts_m = Math.floor(dts / 60000);
dts_s = ((dts - (dts_m * 60000)) / 1000).toFixed(1);
if (dts_m < 10) dts_m = '0' + dts_m;
if (dts_s < 10) dts_s = '0' + dts_s;
hours = entry.startTime.getHours();
minutes = entry.startTime.getMinutes();
seconds = entry.startTime.getSeconds();
if (hours < 10) hours = '0' + hours;
if (minutes < 10) minutes = '0' + minutes;
if (seconds < 10) seconds = '0' + seconds;
speaker = entry.speaker;
if (speaker === "ai") speaker = " ai";
replic = speaker + ': [' + hours + ':' + minutes + ':' + seconds + ' '
+ dts_m + ':' + dts_s + '] - ' + entry.text + "\n";
txtData = txtData + replic;
await txtFile.appendFile(replic);
await logFile.appendFile(replic);
debugItem ++;
let debugString = '"Debug Info #' + debugItem + ' dialogue": '
+ JSON.stringify(entry, undefined, 2) + "\n";
await debugFile.appendFile(debugString);
});
conv.on("debugLog", async (event) => {
if (event?.msg?.msgId === "RecognizedSpeechMessage") {
const logEntry = event?.msg?.results[0]?.facts;
await logFile.appendFile(JSON.stringify(logEntry, undefined, 2) + "\n");
debugItem ++;
let debugString = '"Debug Info #' + debugItem + ' entry": '
+ JSON.stringify(logEntry, undefined, 2) + "\n";
await debugFile.appendFile(debugString);
}
debugItem ++;
let debugString = '"Debug Info #' + debugItem + ' event": '
+ JSON.stringify(event, undefined, 2) + "\n";
await debugFile.appendFile(debugString);
});
const result = await conv.execute();
let close_time = new Date();
rts = close_time.getTime();
dts = rts - ts;
dts_m = Math.floor(dts / 60000);
dts_s = ((dts - (dts_m * 60000)) / 1000).toFixed(1);
if (dts_m < 10) dts_m = '0' + dts_m;
if (dts_s < 10) dts_s = '0' + dts_s;
hours = close_time.getHours();
minutes = close_time.getMinutes();
seconds = close_time.getSeconds();
if (hours < 10) hours = '0' + hours;
if (minutes < 10) minutes = '0' + minutes;
if (seconds < 10) seconds = '0' + seconds;
const conversations_statuses = {
normal: "разговор завершён",
broken: "абонент повесил трубку",
forward: "разговор переведён на другой номер"
};
try {
conversation_status_id = result.output.conversation_status;
conversation_status = conversations_statuses[conversation_status_id];
} catch(err) {
conversation_status_id = "unknown";
conversation_status = "Не определённое в сценанрии завершение разговора";
}
speaker = " sys";
replic = speaker + ': [' + hours + ':' + minutes + ':' + seconds + ' '
+ dts_m + ':' + dts_s + "] - / " + conversation_status + " /\n";
txtData = txtData + replic;
await txtFile.appendFile(replic);
await logFile.appendFile(replic);
logString = "\nConversation results:" +
JSON.stringify(result.output, undefined, 2) + "\n";
await logFile.appendFile(logString);
console.log(result.output);
let audio_url = "";
if (shared.empty(result.recordingUrl)){
audio_url = "\nАудио запись разговора не доступна\n";
result.output.audio_url = "нет ссылки";}
else {
audio_url = "\nЗапись разговора доступна по ссылке:\n"
+ result.recordingUrl + "\n";
result.output.audio_url = result.recordingUrl;
}
// txtData = txtData + audio_url;
await txtFile.appendFile(audio_url);
await logFile.appendFile(audio_url);
const conv_info = shared.get_conversation_info(app, conv);
// Close text, log and debug files
await txtFile.close();
await logFile.close();
await debugFile.close();
// Store conversation text and data
let conv_start = result.output.conversation_start;
let conv_begin = result.output.conversation_begin;
let conv_stop = result.output.conversation_stop;
if (shared.empty(conv_start)) conv_start = 0;
if (shared.empty(conv_begin)) conv_begin = 0;
if (shared.empty(conv_stop)) conv_stop = 0;
if (conv_start == 0) conv_start = ts; else conv_start += ts;
if (conv_begin == 0) conv_begin = conv_start; else conv_begin += ts;
if (conv_stop == 0) conv_stop = Date.now(); else conv_stop += ts;
let output_data = JSON.stringify(result.output, undefined, 2);
let template_id = result.output.template_id??preset_template_id;
let action = "store_conversations";
let request = {
owner: events_owner,
datetime_event: Math.ceil(conv_start/1000),
// src: caller_phone,
// dst: local_num,
dst: caller_phone,
src: local_num,
duration: Math.ceil((conv_stop - conv_start)/1000),
direction: "output",
billsec: Math.ceil((conv_stop - conv_begin)/1000),
app_name: conv_info.app_name,
app_name_full: conv_info.app_name_full,
app_id: conv_info.app_id,
job_id: conv_info.job_id,
report_data: output_data,
conversation: txtData,
template_id: template_id
};
console.log(txtData);
let response = await shared.ai_api_hook(action, request);
console.log(response);
try //отправка в тг
{
let tmp = _msg['txtData'];
tmp+= audio_url;
_msg['txtData'] = tmp;
var find = '\"';
var re = new RegExp(find, 'g');
var tmp_json = JSON.stringify(_msg).replace(re, "'");
let request = {
events_owner: events_owner,
template_id: 1,
config_id: 1,
json: tmp_json
};
let action = "send_telegram_owner";
shared.ai_api_hook(action, request);
}
catch (e)
{
}
if (conversation_status_id == "forward") {
var conversations_id = response.conversations_id ?? 0;
action = "force_conversations_mail";
request = {
owner: events_owner,
time: Math.ceil(conv_start/1000),
id: conversations_id,
swap: 1
};
console.log(request);
response = await shared.ai_api_hook(action, request);
console.log(response);
}
// Send text file to Google Disk
if (audioChannel)
{
shared.renew_last_log_file(txt_file_name);
shared.send_to_google_disk(txt_file_name, file_mime_type);
}
// Send log file to Google Disk
if (audioChannel)
shared.send_to_google_disk(log_file_name, file_mime_type);
// Send debug file to Google Disk
if (audioChannel)
shared.send_to_google_disk(debug_file_name, file_mime_type);
await app.stop();
app.dispose();
}
main().catch((err) => {console.error(err)});
*.txt
*.log
*.debug
{
"name": "marketplaceaudit-dev",
"version": "1.0.0",
"description": "",
"author": "",
"license": "MIT",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"devDependencies": {
"prettier": "^2.3.0"
},
"dependencies": {
"string": "^3.3.3",
"moment": "^2.29.1",
"@dasha.ai/sdk": "^0.8.5"
}
}
const fs = require("fs");
const url = require("url");
const axios = require("axios");
const moment = require("moment");
const strings = require("string");
const dataset = require("./dataset");
const sys = require('child_process');
const cct_api = 'http://cct.r-broker.ru/api/';
const cct_key = 'y8Bzq1C0vIwhcrl98emy04JauUlWd7glhvUo2TcWBSkdMYzrKS'
+ 'KhQNMuoU_NecQJVNLIgRA4eeDTX4QFDA6np1VI0ctrmYJkcIQK'
+ 'muMgCjKzqHVSzZ2-n_0vefeFZPCMZJz4NO8xgrPwQ1OWgn_ACt'
+ 'be9DWhSR-yi_-u2k2NcF3Heg193PSKvov9QPlShLtq_UzdQfmb'
+ 'C9uS1-bukZFdFyQ9M6mO8Qnlyzm95ouQPODwqieKjiXIFkXUIW';
const file_sender = '../../htdocs/uploadgd/up.php';
const api_hook_url = 'https://ai.r-broker.ru/api/api.php';
const api_hook_key = 'kd56h4jfhj54f62hjf6d8YgjL9';
const api_hook = api_hook_url + '?key=' + api_hook_key;
const mobile_codes = dataset.mobile_codes();
function empty (value) {
if (value === undefined) return true;
if (value === null) return true;
const str = strings(value);
if (str == "") return true;
if (str.trim() == "") return true;
return false;
}
async function cct_api_service (service, api_request) {
let post_data = JSON.stringify(api_request);
let headers = {
"Language": "ru-RU",
"Authorization": "Bearer " + cct_key,
"Content-Type": "application/json; charset=utf-8"
};
let config = { headers: headers };
let result = await axios.post(cct_api + service, post_data, config);
let wrong_data = { status: 'error', result: 'axios crashed' };
if (result.data === null) return wrong_data
if (result.data === undefined) return wrong_data;
return result.data;
}
module.exports = exports = {
empty (value) {
return empty (value);
},
async ai_api_hook (action, web_hook_data) {
let api_hook_action = api_hook + '&action=' + action;
let post_data = new url.URLSearchParams(web_hook_data);
let result = {};
let wrong_data = { status: 'error', detail: 'axios crashed' };
try {
result = await axios.post(api_hook_action, post_data);
} catch (err) {
wrong_data.detail = JSON.stringify(err, undefined, 2);
result.data = wrong_data;
}
if (empty(result.data)) return wrong_data
return result.data;
},
get_part_of_the_day() {
ts = Date.now();
let date_ob = new Date(ts);
hours = date_ob.getHours();
if (hours < 4) return 'night';
if (hours < 12) return 'morning';
if (hours < 16) return 'day';
if (hours < 24) return 'evening';
return 'night';
},
human_format(abonent_phone) {
let clean_phone = abonent_phone.replace( /[^\d]/g, '' );
var size = clean_phone.length;
var human_phone = "";
if (size < 4) return clean_phone;
if (size == 11) {
prefix = clean_phone.substring(0, 1);
if (prefix == 8) human_phone = prefix; else human_phone = "+" + prefix;
human_phone = human_phone + " (" + clean_phone.substring(1, 4);
human_phone = human_phone + ") " + clean_phone.substring(4, 7);
human_phone = human_phone + "-" + clean_phone.substring(7, 9);
human_phone = human_phone + "-" + clean_phone.substring(9, 11);
return human_phone;
}
if (size == 10) {
human_phone = "+7";
human_phone = human_phone + " (" + clean_phone.substring(0, 3);
human_phone = human_phone + ") " + clean_phone.substring(3, 6);
human_phone = human_phone + "-" + clean_phone.substring(6, 8);
human_phone = human_phone + "-" + clean_phone.substring(8, 10);
return human_phone;
}
const cuts = [ 2, 2, 3, 3 ];
const divs = [ "-", "-", ") ", "(" ];
let phone = clean_phone;
for (var i = 0; i < 4; i ++) {
if (size > cuts[i]) {
size -= cuts[i];
part = phone.substring(size, size + cuts[i]);
human_phone = divs[i] + part + human_phone;
phone = phone.substring(0, size);
} else {
var div = "";
if (divs[i] == "(") div = divs[i];
return div + phone + human_phone;
}
}
return "+" + phone + " " + human_phone;
},
last_four_digits(abonent_phone) {
let clean_phone = abonent_phone.replace( /[^\d]/g, '' );
var size = clean_phone.length;
let last_four = "";
if (size > 3) {
last_four = clean_phone.substring(size - 2, size);
size -= 2;
return clean_phone.substring(size - 2, size) + "-" + last_four;
}
return clean_phone;
},
async cct_numbers_from_text (text) {
const service = 'markers-data';
request = {
debug: false,
input: "text",
channel: "client",
distance: { "min": 0.166667, "mid": 0.166667, "max": 0.166667 },
language: "russian",
text: text,
triggers: [
{ name: "numbers", markers: [ { type: "number" } ] }
]
};
const response = await cct_api_service(service, request);
let result = "";
if (response.status !== "success") return result;
for (trigger of response.result) {
if (trigger.state === "on") {
if (trigger.name === "numbers") {
for (i in trigger.details) {
found = trigger.details[i];
for (j in found) {
element = found[j];
result = result + " " + element.value;
}
}
}
}
}
result = result.replace( /^\s+/, '' );
return result;
},
async cct_dates_from_text (text) {
const service = 'markers-data';
request = {
debug: false,
input: "text",
channel: "client",
distance: { "min": 0.166667, "mid": 0.166667, "max": 0.166667 },
language: "russian",
text: text,
triggers: [
{ name: "dates", markers: [ { type: "date time" } ] }
]
};
const response = await cct_api_service(service, request);
let result = "";
if (response.status !== "success") return result;
for (trigger of response.result) {
if (trigger.state === "on") {
if (trigger.name === "dates") {
for (i in trigger.details) {
found = trigger.details[i];
for (j in found) {
element = found[j];
pattern = element.pattern??"";
if ((pattern === "%date%") && (result === ""))
result = element.value;
}
}
}
}
}
if (result === "") return result;
moment.locale("ru");
result = moment(result, "YYYY-MM-DD").format("DD.MM.YYYY");
return result;
},
renew_last_log_file(last_text_file) {
let sysCommand = 'cat ' + last_text_file + ' >log.txt';
sys.exec(sysCommand, (error, stdout, stderr) => {
if (error) {
console.error(`error: ${error.message}`);
}
if (stderr) {
console.error(`stderr: ${stderr}`);
}
// console.log(`stdout:\n${stdout}`);
});
},
send_to_google_disk(file_name, file_mime) {
/*
* Сохранение файлов с отчётами на Google Disk отключено,
* так как просмотр и анализ этих файлов именно на Google
* Disk более не актуален, у всех разработчиков есть к ним
* доступ по FTP
*
const sender = sys.spawn('php', [ file_sender, file_name, file_mime ]);
sender.on("close", (code) => {
if (code != 0)
console.log("send 'file_name' to google disk return code: " + code);
});
sender.on("error", (error) => {
console.log("send to google disk error: \n" + JSON.stringify(error, undefined, 2));
});
sender.stderr.on("data", (data) => {
console.log("send to google disk stderr: \n" + data);
});
sender.stdout.on("data", (data) => {
console.log("send to google disk stdout: \n" + data);
});
*/
},
is_mobile_code(abonent_phone) {
let clean_phone = abonent_phone.replace( /[^\d]/g, '' );
var size = clean_phone.length;
let phone_code = "";
if (size > 6) {
phone_code = clean_phone.substring(1, 4);
}
return (mobile_codes.indexOf(phone_code)!=-1);
},
str_pad(num, size, pad) {
let str = "" + num;
let len = str.length;
while (len < size) {
str = pad + str;
len ++;
}
return str;
},
pid_name() {
return this.str_pad(process.pid, 5, "0");
},
make_unique_app(app_suffix) {
let app_suffix_file = './threads/' + app_suffix + '/app.dashaapp'
if (fs.existsSync(app_suffix_file)) return './threads/' + app_suffix;
let proc = sys.spawnSync('cp', [ '-rfp', './app', './threads/' + app_suffix ]);
let exit_code = proc.status;
let exit_data = proc.stdout.toString();
let check_error = proc.error;
let look_stderr = proc.stderr.toString();
if (check_error) {
console.log('error: ' + JSON.stringify(check_error, undefined, 2));
return './app';
}
if (look_stderr) {
console.log('stderr: ' + look_stderr);
return './app';
}
app_data = fs.readFileSync('app/app.dashaapp');
app_info = JSON.parse(app_data);
let app_name = app_info.name + '-' + app_suffix;
app_info.name = app_name;
fs.writeFileSync(app_suffix_file, JSON.stringify(app_info, undefined, 2));
return './threads/' + app_suffix;
},
drop_unique_app(app_suffix) {
let proc = sys.spawnSync('rm', [ '-rf', './threads/' + app_suffix ]);
let exit_code = proc.status;
let exit_data = proc.stdout.toString();
let check_error = proc.error;
let look_stderr = proc.stderr.toString();
if (check_error) {
console.log('error: ' + JSON.stringify(check_error, undefined, 2));
return false;
}
if (look_stderr) {
console.log('stderr: ' + look_stderr);
return false;
}
return true;
},
get_conversation_info(app, conv) {
app_data = fs.readFileSync('app/app.dashaapp');
app_info = JSON.parse(app_data);
let app_name = app_info.name;
app_name_full = app.applicationName;
app_id = app.applicationId;
job_id = conv._jobId;
return {
app_name: app_name,
app_name_full: app_name_full,
app_id: app_id,
job_id: job_id
};
}
};
*
!.gitignore
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!