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"]);
}
}
node helpfulpos //pair2
{
do
{
}
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;
}
onexit
}
node nowhatsapp
{
do
{
end_conversation: do
#sayText("Продиктуйте, пожалуйста данные, куда я могу отправить промокод на трехдневное обучение в нашей Академии и скидку на подписку Аналитики ХантерСейл.",
options: {speed: 1.15});
wait*;
}
transitions
{
var abonent_say = #getMessageText();
set $abonent_request = external performed_stage($abonent_request, abonent_say);
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
......@@ -142,4 +236,3 @@ 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
*.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!