Commit 548f92d2 by softwhiskey8

0.1

1 parent 762bfc3e
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.
......@@ -35,6 +35,52 @@
]
},
"canttalkrn":
{
"includes": [
"не готов говорить",
"не могу говорить",
"занят",
"нету времени говорить"
]
},
"cantalk":
{
"includes":[
"слушаю",
"я вас слушаю"
]
},
"sendit":
{
"includes":[
"отправляйте на",
"отправьте на почту",
"отправьте",
"отправляйте"
]
},
"dontneed":
{
"includes":[
"ничего не нужно",
"не нужно",
"неинтересно"
]
},
"nowhatsapp":
{
"includes":[
"вотсапа нету",
"ватсапа нету",
"нету"
],
"excludes":[
"вотсап есть",
"ватсап есть"
]
},
"wait_message": {
"includes": [
"передайте",
......
......@@ -2,7 +2,7 @@ import "commonReactions/all.dsl";
context {
input phone: string;
input caller: string;
//input caller: string;
output cjm: string[] = [];
output need_ask_name: boolean = true;
......@@ -18,31 +18,14 @@ context {
output phone_suffix: string = "";
}
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 array_size(arr: unknown): number;
external function string_trim(one_line: string): string;
external function is_empty(check: unknown): boolean;
external function performed_stage(stages: string[], stage: string): string[];
external function sleep_ms(duration: number): unknown;
external function json_encode(object: unknown): string;
external function time_stamp(): number;
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;
start node root
{
do
{
set $cjm = external performed_stage($cjm, "root");
set $conversation_start = external time_stamp();
//set $cjm = external performed_stage($cjm, "root");
//set $conversation_start = external time_stamp();
goto caller_id;
}
transitions
......@@ -61,7 +44,8 @@ node caller_id
#waitForSpeech(1000);
// Строим приветствие, зависящее от времени суток
var part_of_the_day_name = external part_of_the_day();
//var part_of_the_day_name = external part_of_the_day();
var part_of_the_day_name = "day";
var abonent_greeting = "Здравствуйте";
if (part_of_the_day_name == "night")
{
......@@ -81,26 +65,136 @@ node caller_id
}
// Запоминаем временной штамп перед самой первой репликой
set $conversation_begin = external time_stamp();
//set $conversation_begin = external time_stamp();
set abonent_greeting += ", меня зовут Алина, я ассистент Азамата. Буквально на минутку, позволите?";
#sayText(abonent_greeting, options: {speed: 1.15});
#say("dont_understand_question");
//#say("dont_understand_question");
wait *;
}
transitions
{
end_conversation: goto end_conversation on true;
//end_conversation: goto end_conversation on true;
canttalkrn: goto canttalkrn on #messageHasAnyIntent(["decline", "canttalkrn"]);
cantalk: goto cantalk on #messageHasAnyIntent(["accept", "cantalk"]);
}
onexit
}
node helpfulpos //pair2
{
do
{
end_conversation: do
{
var abonent_say = #getMessageText();
set $abonent_request = external performed_stage($abonent_request, abonent_say);
}
}
transitions
{
}
}
node helpfulneg //pair2
{
do
{
#sayText("Подскажите, пожалуйста, что Вам не понравилось? На что нам следует обратить внимание?",
options: {speed: 1.15});
wait*;
}
transitions
{
understood: goto understood on true;
}
}
node understood //pair3
{
do
{
#sayText("Поняла Вас. Мы постараемся учесть Ваши пожелания. И специально для Вас мы подготовили специальный подарок - промокод на трехдневное обучение в нашей Академии и скидку на подписку Аналитики ХантерСейл. Куда я могу Вам всё отправить?",
options: {speed: 1.15});
wait*;
}
transitions
{
positive: goto sendit on #messageHasSentiment("positive") || #messageHasIntent("sendit");
//negative: goto
}
}
node sendit
{
do
{
#sayText("Подскажите, а на этом номере есть вотсап?",
options: {speed: 1.15});
wait*;
}
transitions
{
positive: goto yeswhatsapp on #messageHasSentiment("positive");
negative: goto nowhatsapp on #messageHasSentiment("negative") || #messageHasIntent("nowhatsapp");
}
}
node yeswhatsapp
{
do
{
#sayText("Отлично. Сейчас отправлю Вам промокод. Всего доброго до свидания.",
options: {speed: 1.15});
}
transitions
{
end_conversation: goto end_conversation;
}
}
node nowhatsapp
{
do
{
#sayText("Продиктуйте, пожалуйста данные, куда я могу отправить промокод на трехдневное обучение в нашей Академии и скидку на подписку Аналитики ХантерСейл.",
options: {speed: 1.15});
wait*;
}
transitions
{
thanksbye: goto thanksbye on true;
}
}
node thanksbye
{
do
{
#sayText("Благодарю за уделенное время. Всего доброго.");
}
transitions
{
end_conversation: goto end_conversation;
}
}
node cantalk //pair1
{
do
{
#sayText("Вы недавно посетили вебинар от команды ХантерСЕЙЛС, аналитика Вайлдберис. Скажите, пожалуйста, насколько полезна была для Вас информация?"
, options: {speed: 1.15 });
wait*;
}
transitions
{
positive: goto helpfulpos on #messageHasSentiment("positive");
negative: goto helpfulneg on #messageHasSentiment("negative");
}
}
node canttalkrn //pair1
{
do
{
#sayText("Благодарю за уделенное время. Мы свяжемся с Вами позднее. Всего доброго, досвидания"
, options: {speed: 1.15});
}
transitions
{
end_conversation: goto end_conversation;
}
}
node end_conversation
{
do
......@@ -120,7 +214,7 @@ node do_before_exit
do
{
set $cjm = external performed_stage($cjm, "do_before_exit");
set $conversation_stop = external time_stamp();
//set $conversation_stop = external time_stamp();
exit;
}
transitions
......@@ -141,5 +235,4 @@ digression @exit_dig
{
do_before_exit: goto do_before_exit;
}
}
}
\ No newline at end of file
......@@ -250,6 +250,7 @@
"text": "Благодарим вас за уделенное время! Остаемся на связи!"
}
]
},
"types": {},
"macros": {
......
......@@ -7,8 +7,8 @@
},
"node::caller_id": {
"common.position": {
"x": 598.377372534425,
"y": 134.60160992786908
"x": 573.5712618840289,
"y": 129.43367020903653
}
},
"node::end_conversation": {
......@@ -19,8 +19,8 @@
},
"node::do_before_exit": {
"common.position": {
"x": 600,
"y": 709.2035244482049
"x": 567.9587737432382,
"y": 822.8981982625207
}
},
"node::repeat_or_ping_hangup": {
......@@ -103,8 +103,8 @@
},
"node::hello_preprocessor": {
"common.position": {
"x": 600,
"y": 585
"x": 585.529768787269,
"y": 611.8732865379292
}
},
"node::hello": {
......@@ -139,8 +139,8 @@
},
"node::can_hear_you": {
"common.position": {
"x": 600,
"y": 645
"x": 582.4290049559694,
"y": 684.2763418631273
}
},
"node::exit_dig": {
......
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)}`)
}
}
async addFolder(folder)
{
const files = fs.readdirSync(folder);
for (const fileName of files)
{
if (path.extname(fileName) === ".json")
{
const fname = path.join(folder, fileName);
console.log(`Parsing ${fname}`);
await this.appendJson(folder, JSON.parse(fs.readFileSync(fname).toString()));
}
}
}
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 local_num = "4014";
const events_owner = "80";
const sip_config = "megafon";
const preset_template_id = 3;
const logs_path = "./logs/";
const file_mime_type = "text/plain";
let txtData = "";
let abonent_phone = process.argv[2] ?? "";
let caller_phone = process.argv[3] ?? abonent_phone;
const report_file = "jobs/" + caller_phone + ".json";
const pid_file = report_file.replace( /\.json$/, '.pid' );
fs.writeFileSync(pid_file, JSON.stringify(process.pid, undefined, 2));
fs.chmod(pid_file, 0o666, (err) => { if (err) console.log(err) });
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 = await dasha.deploy("./app");
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();
audio.addFolder("audio");
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);
};
process.on("SIGTERM", async () => {
console.log("\nReceived SIGTERM");
await app.stop();
app.dispose();
if (fs.existsSync(pid_file)) await fs.unlinkSync(pid_file);
let job_data = { status: "Terminated",
reason: "Received SIGTERM",
details: txtData };
fs.writeFileSync(report_file, JSON.stringify(job_data, undefined, 2),
{encoding:'utf8', mode:0o666, flag:'w'});
console.log(job_data);
});
process.on("SIGINT", async () => {
console.log("\nReceived SIGINT");
await app.stop();
app.dispose();
if (fs.existsSync(pid_file)) await fs.unlinkSync(pid_file);
let job_data = { status: "Terminated",
reason: "Received SIGINT",
details: txtData };
fs.writeFileSync(report_file, JSON.stringify(job_data, undefined, 2),
{encoding:'utf8', mode:0o666, flag:'w'});
console.log(job_data);
});
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.connectionProvider = async (conv) =>
conv.input.phone === "chat"
? dasha.chat.connect(await dasha.chat.createConsoleChat())
: dasha.sip.connect(new dasha.sip.Endpoint("default"));
await app.start();
app.setExternal("performed_stage", (args, conv) => {
const arr = args.stages;
const val = args.stage;
arr.push(val);
return arr;
});
const conv = app.createConversation({ phone: process.argv[2] ?? "" });
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("time_stamp", async (args, conv) => {
return Date.now();
});
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;
});
await app.start();
const input_context = { phone: abonent_phone, caller: caller_phone };
const conv = app.createConversation(input_context);
const audioChannel = conv.input.phone !== "chat";
if (audioChannel) {
conv.sip.config = sip_config;
// 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;
if (conv.input.phone !== "chat") conv.on("transcription", console.log);
// 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;
const logFile = await fs.promises.open("./log.txt", "w");
await logFile.appendFile("#".repeat(100) + "\n");
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
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);
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);
await logFile.appendFile(`${entry.speaker}: ${entry.text}\n`);
});
let connectionStatus = {};
if (audioChannel)
connectionStatus = { status: "Connected", reason: "", details: "" };
else
connectionStatus = { status: "Chat mode", reason: "", details: "" };
conv.on("debugLog", async (event) => {
if (event?.msg?.msgId === "FailedOpenSessionChannelMessage") {
connectionStatus = { status: "Failed",
reason: event?.msg?.reason,
details: event?.msg?.details };
}
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 = "Не определённое в сценанрии завершение разговора";
}
if (connectionStatus.status === "Failed") {
if (connectionStatus.reason === "Busy") {
conversation_status = "линия занята";
} else {
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";
else
audio_url = "\nЗапись разговора доступна по ссылке:\n"
+ result.recordingUrl + "\n";
// txtData = txtData + audio_url;
await txtFile.appendFile(audio_url);
await logFile.appendFile(audio_url);
let job_data = {};
if (connectionStatus.status === "Failed")
job_data = connectionStatus;
else {
job_data = { status: connectionStatus.status,
reason: conversation_status_id,
audio: result.recordingUrl,
report: result.output,
details: txtData };
}
fs.writeFileSync(report_file, JSON.stringify(job_data, undefined, 2),
{encoding:'utf8', mode:0o666, flag:'w'});
console.log(job_data);
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;
if (conv_begin == 0) conv_begin = conv_start;
if (conv_stop == 0) conv_stop = Date.now();
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);
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();
if (fs.existsSync(pid_file)) await fs.unlinkSync(pid_file);
await logFile.close();
}
main().catch((err) => {console.error(err)});
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
\ No newline at end of file
*.in
*.pid
*.out
!demo*
\ No newline at end of file
*.txt
*.log
*.debug
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) {
let proc = sys.spawnSync('php', [ file_sender, file_name, file_mime ]);
let exit_code = proc.status;
let look_stdout = proc.stdout.toString();
let check_error = proc.error;
let look_stderr = proc.stderr.toString();
if (check_error)
console.log("error: \n" + JSON.stringify(check_error, undefined, 2));
if (look_stderr) console.log("stderr: \n" + look_stderr);
if (look_stdout) console.log("stdout: \n" + look_stdout);
},
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);
},
pid_name() {
pidName = process.pid;
if (pidName < 10) pidName = '0000' + pidName;
if (pidName < 100) pidName = '000' + pidName;
if (pidName < 1000) pidName = '00' + pidName;
if (pidName < 10000) pidName = '0' + pidName;
return pidName;
},
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
};
}
};
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!