new(all) General Bots Auto Tester 1.0; Unlimited conditionals after-code BASIC 3.0;

This commit is contained in:
rodrigorodriguez 2022-10-22 13:23:32 -03:00
parent c66f9bfe04
commit 30c93526c0
11 changed files with 8829 additions and 28129 deletions

View file

@ -1 +1,5 @@
echo General Bots
echo Installing modules for the first time...
npm i
node . node .

18592
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -98,6 +98,7 @@
"googleapis": "75.0.0", "googleapis": "75.0.0",
"ibm-watson": "6.1.1", "ibm-watson": "6.1.1",
"js-beautify": "1.13.13", "js-beautify": "1.13.13",
"keyv": "^4.5.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"luxon": "2.0.2", "luxon": "2.0.2",
"mammoth": "^1.4.21", "mammoth": "^1.4.21",
@ -147,6 +148,7 @@
"url-join": "4.0.1", "url-join": "4.0.1",
"vbscript-to-typescript": "1.0.8", "vbscript-to-typescript": "1.0.8",
"vhost": "^3.0.2", "vhost": "^3.0.2",
"vm2": "^3.9.11",
"walk-promise": "0.2.0", "walk-promise": "0.2.0",
"washyourmouthoutwithsoap": "1.0.2", "washyourmouthoutwithsoap": "1.0.2",
"whatsapp-web.js": "github:pedroslopez/whatsapp-web.js#fix-buttons-list", "whatsapp-web.js": "github:pedroslopez/whatsapp-web.js#fix-buttons-list",

View file

@ -1,404 +0,0 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of Pragmatismo.io. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog } from 'botlib';
import * as fs from 'fs';
import { CollectionUtil } from 'pragmatismo-io-framework';
import * as request from 'request-promise-native';
import { Messages } from '../strings';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
const { Buttons } = require('whatsapp-web.js');
const Path = require('path');
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
const phone = require('phone');
//tslint:disable-next-line:no-submodule-imports
/**
* HEAR Bot Framework support.
*/
export class HearDialog {
private static async downloadAttachmentAndWrite(attachment) {
const url = attachment.contentUrl;
const localFolder = Path.join('work'); // TODO: , '${botId}', 'uploads');
const localFileName = Path.join(localFolder, attachment.name);
try {
let response;
if (url.startsWith('data:')) {
var regex = /^data:.+\/(.+);base64,(.*)$/;
var matches = url.match(regex);
var ext = matches[1];
var data = matches[2];
response = Buffer.from(data, 'base64');
}
else {
// arraybuffer is necessary for images
const options = {
url: url,
method: 'GET',
encoding: 'binary',
};
response = await request.get(options);
}
fs.writeFile(localFileName, response, (fsError) => {
if (fsError) {
throw fsError;
}
});
} catch (error) {
console.error(error);
return undefined;
}
// If no error was thrown while writing to disk, return the attachment's name
// and localFilePath for the response back to the user.
return {
fileName: attachment.name,
localPath: localFileName
};
}
public static addHearDialog(min) {
min.dialogs.add(
new WaterfallDialog('/hear', [
async step => {
step.activeDialog.state.options = step.options;
step.activeDialog.state.options.id = (step.options as any).id;
step.activeDialog.state.options.previousResolve = (step.options as any).previousResolve;
const args = step.options['args'];
if (args && args.length >1) {
let choices = [];
let i = 0;
args.forEach(arg => {
i++;
choices.push({ body: arg, id: `button${i}` });
});
const locale = step.context.activity.locale;
const button = new Buttons(Messages[locale].choices, choices, ' ', ' ');
await min.conversationalService.sendText(min, step, button);
GBLog.info(`BASIC: Asking for input (HEAR with ${step.options['args'][0]}).`);
}
else {
GBLog.info('BASIC: Asking for input (HEAR).');
}
step.activeDialog.state.options = step.options;
if (step.activeDialog.state.options['kind'] === "login") {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
GBLog.info('BASIC: Authenticating beforing running General Bots BASIC code.');
return await step.beginDialog('/auth');
}
}
return await step.next(step.options);
},
async step => {
if (step.activeDialog.state.options['kind'] === "login") {
return await step.next(step.options);
} else {
if (step.activeDialog.state.options['kind'] === "file") {
return await step.prompt('attachmentPrompt', {});
}
else {
return await min.conversationalService.prompt(min, step, null);
}
}
},
async step => {
const isIntentYes = (locale, utterance) => {
return utterance.toLowerCase().match(Messages[locale].affirmative_sentences);
}
let result = step.context.activity['originalText'];
if (step.activeDialog.state.options['kind'] === "file") {
// Prepare Promises to download each attachment and then execute each Promise.
const promises = step.context.activity.attachments.map(
HearDialog.downloadAttachmentAndWrite);
const successfulSaves = await Promise.all(promises);
async function replyForReceivedAttachments(localAttachmentData) {
if (localAttachmentData) {
// Because the TurnContext was bound to this function, the bot can call
// `TurnContext.sendActivity` via `this.sendActivity`;
await this.sendActivity(`Upload OK.`);
} else {
await this.sendActivity('Error uploading file. Please, start again.');
}
}
// Prepare Promises to reply to the user with information about saved attachments.
// The current TurnContext is bound so `replyForReceivedAttachments` can also send replies.
const replyPromises = successfulSaves.map(replyForReceivedAttachments.bind(step.context));
await Promise.all(replyPromises);
result = {
data: fs.readFileSync(successfulSaves[0].localPath),
filename: successfulSaves[0].fileName
};
}
else if (step.activeDialog.state.options['kind'] === "boolean") {
if (isIntentYes('pt-BR', step.result)) {
result = true;
}
else {
result = false;
}
}
else if (step.activeDialog.state.options['kind'] === "email") {
const extractEntity = (text) => {
return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi);
}
const value = extractEntity(step.result);
if (value === null) {
await step.context.sendActivity("Por favor, digite um e-mail válido.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "name") {
const extractEntity = text => {
return text.match(/[_a-zA-Z][_a-zA-Z0-9]{0,16}/gi);
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite um nome válido.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "integer") {
const extractEntity = text => {
return text.match(/\d+/gi);
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite um número válido.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "date") {
const extractEntity = text => {
return text.match(/(^(((0[1-9]|1[0-9]|2[0-8])[\/](0[1-9]|1[012]))|((29|30|31)[\/](0[13578]|1[02]))|((29|30)[\/](0[4,6,9]|11)))[\/](19|[2-9][0-9])\d\d$)|(^29[\/]02[\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)/gi);
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite uma data no formato 12/12/2020.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "hour") {
const extractEntity = text => {
return text.match(/^([0-1]?[0-9]|2[0-4]):([0-5][0-9])(:[0-5][0-9])?$/gi);
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite um horário no formato hh:ss.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "money") {
const extractEntity = text => {
if (step.context.locale === 'en') { // TODO: Change to user.
return text.match(/(?:\d{1,3},)*\d{1,3}(?:\.\d+)?/gi);
}
else {
return text.match(/(?:\d{1,3}.)*\d{1,3}(?:\,\d+)?/gi);
}
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite um valor monetário.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value;
}
else if (step.activeDialog.state.options['kind'] === "mobile") {
const locale = step.context.activity.locale;
let phoneNumber;
try {
phoneNumber = phone(step.result, 'BRA')[0]; // TODO: Use accordingly to the person.
phoneNumber = phoneUtil.parse(phoneNumber);
} catch (error) {
await step.context.sendActivity(Messages[locale].validation_enter_valid_mobile);
return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options);
}
if (!phoneUtil.isPossibleNumber(phoneNumber)) {
await step.context.sendActivity("Por favor, digite um número de telefone válido.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = phoneNumber;
}
else if (step.activeDialog.state.options['kind'] === "zipcode") {
const extractEntity = text => {
text = text.replace(/\-/gi, '');
if (step.context.locale === 'en') { // TODO: Change to user.
return text.match(/\d{8}/gi);
}
else {
return text.match(/(?:\d{1,3}.)*\d{1,3}(?:\,\d+)?/gi);
}
};
const value = extractEntity(step.result);
if (value === null || value.length != 1) {
await step.context.sendActivity("Por favor, digite um valor monetário.");
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
result = value[0];
}
else if (step.activeDialog.state.options['kind'] === "menu") {
const list = step.activeDialog.state.options['args'];
result = null;
const typed = step.context.activity['originalText'];
await CollectionUtil.asyncForEach(list, async item => {
if (GBConversationalService.kmpSearch(typed, item) != -1) {
result = item;
}
});
if (result === null) {
await step.context.sendActivity(`Escolha por favor um dos itens sugeridos.`);
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
}
else if (step.activeDialog.state.options['kind'] === "language") {
result = null;
const list = [
{ name: 'english', code: 'en' },
{ name: 'inglês', code: 'en' },
{ name: 'portuguese', code: 'pt' },
{ name: 'português', code: 'pt' },
{ name: 'français', code: 'fr' },
{ name: 'francês', code: 'fr' },
{ name: 'french', code: 'fr' },
{ name: 'spanish', code: 'es' },
{ name: 'espanõl', code: 'es' },
{ name: 'espanhol', code: 'es' },
{ name: 'german', code: 'de' },
{ name: 'deutsch', code: 'de' },
{ name: 'alemão', code: 'de' }
];
const text = step.context.activity['originalText'];
await CollectionUtil.asyncForEach(list, async item => {
if (GBConversationalService.kmpSearch(text.toLowerCase(), item.name.toLowerCase()) != -1 ||
GBConversationalService.kmpSearch(text.toLowerCase(), item.code.toLowerCase()) != -1) {
result = item.code;
}
});
if (result === null) {
await min.conversationalService.sendText(min, step, `Escolha por favor um dos idiomas sugeridos.`);
return await step.replaceDialog('/hear', step.activeDialog.state.options);
}
}
const id = step.activeDialog.state.options.id;
if (min.cbMap[id]) {
const promise = min.cbMap[id].promise;
delete min.cbMap[id];
try {
const opts = await promise(step, result);
if (opts) {
return await step.replaceDialog('/hear', opts);
}
} catch (error) {
GBLog.error(`Error in BASIC code: ${error}`);
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, Messages[locale].very_sorry_about_error);
}
}
return await step.endDialog();
}
])
);
}
}

View file

@ -44,8 +44,19 @@ import { GBMinService } from '../../core.gbapp/services/GBMinService';
import { HubSpotServices } from '../../hubspot.gblib/services/HubSpotServices'; import { HubSpotServices } from '../../hubspot.gblib/services/HubSpotServices';
import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine'; import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
const DateDiff = require('date-diff');
import { createBrowser } from '../../core.gbapp/services/GBSSR'; import { createBrowser } from '../../core.gbapp/services/GBSSR';
import * as request from 'request-promise-native';
import { Messages } from '../strings';
import * as fs from 'fs';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService';
const DateDiff = require('date-diff');
const { Buttons } = require('whatsapp-web.js');
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
const phone = require('phone');
const Path = require('path'); const Path = require('path');
const sgMail = require('@sendgrid/mail'); const sgMail = require('@sendgrid/mail');
@ -881,6 +892,49 @@ export class DialogKeywords {
public async showMenu(step) { public async showMenu(step) {
return await step.beginDialog('/menu'); return await step.beginDialog('/menu');
} }
private static async downloadAttachmentAndWrite(attachment) {
const url = attachment.contentUrl;
const localFolder = Path.join('work'); // TODO: , '${botId}', 'uploads');
const localFileName = Path.join(localFolder, attachment.name);
try {
let response;
if (url.startsWith('data:')) {
var regex = /^data:.+\/(.+);base64,(.*)$/;
var matches = url.match(regex);
var ext = matches[1];
var data = matches[2];
response = Buffer.from(data, 'base64');
}
else {
// arraybuffer is necessary for images
const options = {
url: url,
method: 'GET',
encoding: 'binary',
};
response = await request.get(options);
}
fs.writeFile(localFileName, response, (fsError) => {
if (fsError) {
throw fsError;
}
});
} catch (error) {
console.error(error);
return undefined;
}
// If no error was thrown while writing to disk, return the attachment's name
// and localFilePath for the response back to the user.
return {
fileName: attachment.name,
localPath: localFileName
};
}
/** /**
* Performs the transfer of the conversation to a human agent. * Performs the transfer of the conversation to a human agent.
@ -898,32 +952,289 @@ export class DialogKeywords {
* @example HEAR name * @example HEAR name
* *
*/ */
public async hear(step, promise, previousResolve, kind, ...args) { public async hear(step, kind, ...args) {
function random(low, high) {
return Math.random() * (high - low) + low;
}
const idPromise = random(0, 120000000);
this.min.cbMap[idPromise] = {};
this.min.cbMap[idPromise].promise = promise;
let opts = { id: idPromise, previousResolve: previousResolve, kind: kind, args }; try {
let user;
const isIntentYes = (locale, utterance) => {
return utterance.toLowerCase().match(Messages[locale].affirmative_sentences);
}
if (this.hrOn) { if (this.hrOn) {
const sec = new SecService();
user = await sec.getUserFromAgentSystemId(this.hrOn)
}
else {
user = this.user.systemUser;
}
const userId = user.userId;
let result;
// Waits for next message in HEAR delegated context. const locale = user.locale ? user.locale : 'en-US';
// TODO: https://github.com/GeneralBots/BotServer/issues/266
const botId = this.min.botId; if (args && args.length > 1) {
WhatsappDirectLine.state[botId + this.hrOn] = {
promise: promise, previousResolve: previousResolve let choices = [];
}; let i = 0;
args.forEach(arg => {
i++;
choices.push({ body: arg, id: `button${i}` });
});
const button = new Buttons(Messages[locale].choices, choices, ' ', ' ');
await this.talk(button);
GBLog.info(`BASIC: HEAR with ${args.toString()} (Asking for input).`);
} }
else { else {
if (previousResolve !== undefined) { GBLog.info('BASIC: HEAR (Asking for input).');
previousResolve(opts);
} else {
await step.beginDialog('/hear', opts);
} }
// Wait for the user to answer.
let sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
this.min.cbMap[userId] = {}
this.min.cbMap[userId]['promise'] = '!GBHEAR';
while (this.min.cbMap[userId].promise === '!GBHEAR') {
await sleep(500);
}
const text = this.min.cbMap[userId].promise;
if (kind === "file") {
await step.prompt('attachmentPrompt', {});
// Prepare Promises to download each attachment and then execute each Promise.
const promises = step.context.activity.attachments.map(
DialogKeywords.downloadAttachmentAndWrite);
const successfulSaves = await Promise.all(promises);
async function replyForReceivedAttachments(localAttachmentData) {
if (localAttachmentData) {
// Because the TurnContext was bound to this function, the bot can call
// `TurnContext.sendActivity` via `this.sendActivity`;
await this.sendActivity(`Upload OK.`);
} else {
await this.sendActivity('Error uploading file. Please, start again.');
}
}
// Prepare Promises to reply to the user with information about saved attachments.
// The current TurnContext is bound so `replyForReceivedAttachments` can also send replies.
const replyPromises = successfulSaves.map(replyForReceivedAttachments.bind(step.context));
await Promise.all(replyPromises);
result = {
data: fs.readFileSync(successfulSaves[0]['localPath']),
filename: successfulSaves[0]['fileName']
};
}
else if (kind === "boolean") {
if (isIntentYes('pt-BR', text)) {
result = true;
}
else {
result = false;
}
}
else if (kind === "email") {
const extractEntity = (text) => {
return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi);
}
const value = extractEntity(text);
if (value === null) {
await this.talk("Por favor, digite um e-mail válido.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "name") {
const extractEntity = text => {
return text.match(/[_a-zA-Z][_a-zA-Z0-9]{0,16}/gi);
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite um nome válido.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "integer") {
const extractEntity = text => {
return text.match(/\d+/gi);
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite um número válido.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "date") {
const extractEntity = text => {
return text.match(/(^(((0[1-9]|1[0-9]|2[0-8])[\/](0[1-9]|1[012]))|((29|30|31)[\/](0[13578]|1[02]))|((29|30)[\/](0[4,6,9]|11)))[\/](19|[2-9][0-9])\d\d$)|(^29[\/]02[\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)/gi);
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite uma data no formato 12/12/2020.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "hour") {
const extractEntity = text => {
return text.match(/^([0-1]?[0-9]|2[0-4]):([0-5][0-9])(:[0-5][0-9])?$/gi);
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite um horário no formato hh:ss.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "money") {
const extractEntity = text => {
if (step.context.locale === 'en') { // TODO: Change to user.
return text.match(/(?:\d{1,3},)*\d{1,3}(?:\.\d+)?/gi);
}
else {
return text.match(/(?:\d{1,3}.)*\d{1,3}(?:\,\d+)?/gi);
}
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite um valor monetário.");
return await this.hear(step, kind, args);
}
result = value;
}
else if (kind === "mobile") {
const locale = step.context.activity.locale;
let phoneNumber;
try {
phoneNumber = phone(text, 'BRA')[0]; // TODO: Use accordingly to the person.
phoneNumber = phoneUtil.parse(phoneNumber);
} catch (error) {
await this.talk(Messages[locale].validation_enter_valid_mobile);
return await this.hear(step, kind, args);
}
if (!phoneUtil.isPossibleNumber(phoneNumber)) {
await this.talk("Por favor, digite um número de telefone válido.");
return await this.hear(step, kind, args);
}
result = phoneNumber;
}
else if (kind === "zipcode") {
const extractEntity = text => {
text = text.replace(/\-/gi, '');
if (step.context.locale === 'en') { // TODO: Change to user.
return text.match(/\d{8}/gi);
}
else {
return text.match(/(?:\d{1,3}.)*\d{1,3}(?:\,\d+)?/gi);
}
};
const value = extractEntity(text);
if (value === null || value.length != 1) {
await this.talk("Por favor, digite um valor monetário.");
return await this.hear(step, kind, args);
}
result = value[0];
}
else if (kind === "menu") {
const list = args;
result = null;
await CollectionUtil.asyncForEach(list, async item => {
if (GBConversationalService.kmpSearch(text, item) != -1) {
result = item;
}
});
if (result === null) {
await this.talk(`Escolha por favor um dos itens sugeridos.`);
return await this.hear(step, kind, args);
}
}
else if (kind === "language") {
result = null;
const list = [
{ name: 'english', code: 'en' },
{ name: 'inglês', code: 'en' },
{ name: 'portuguese', code: 'pt' },
{ name: 'português', code: 'pt' },
{ name: 'français', code: 'fr' },
{ name: 'francês', code: 'fr' },
{ name: 'french', code: 'fr' },
{ name: 'spanish', code: 'es' },
{ name: 'espanõl', code: 'es' },
{ name: 'espanhol', code: 'es' },
{ name: 'german', code: 'de' },
{ name: 'deutsch', code: 'de' },
{ name: 'alemão', code: 'de' }
];
const text = step.context.activity['originalText'];
await CollectionUtil.asyncForEach(list, async item => {
if (GBConversationalService.kmpSearch(text.toLowerCase(), item.name.toLowerCase()) != -1 ||
GBConversationalService.kmpSearch(text.toLowerCase(), item.code.toLowerCase()) != -1) {
result = item.code;
}
});
if (result === null) {
await this.min.conversationalService.sendText(this.min, step, `Escolha por favor um dos idiomas sugeridos.`);
return await this.hear(step, kind, args);
}
}
return result;
} catch (error) {
GBLog.error(`BASIC RUNTIME ERR HEAR ${error.message ? error.message : error}\n Stack:${error.stack}`);
} }
} }
@ -953,10 +1264,14 @@ export class DialogKeywords {
/** /**
* Talks to the user by using the specified text. * Talks to the user by using the specified text.
*/ */
public async talk(step, text: string) { public async talk(text: string) {
GBLog.info(`BASIC: TALK '${text}'.`);
const translate = this.user ? this.user.basicOptions.translatorOn : false; const translate = this.user ? this.user.basicOptions.translatorOn : false;
await this.min.conversationalService['sendTextWithOptions'](this.min, step, text, // TODO: Translate.
translate, null);
await this.min.conversationalService['sendOnConversation'](this.min,
this.user.systemUser, text);
} }
private static getChannel(step): string { private static getChannel(step): string {
@ -965,7 +1280,7 @@ export class DialogKeywords {
return 'webchat'; return 'webchat';
} else { } else {
if (step.context.activity.from && !isNaN(step.context.activity.from.id)) { if (step.context.activity.from && !isNaN(step.context.activity.from.id)) {
return 'whatsapp'; return 'w}, 0);hatsapp';
} }
return 'webchat'; return 'webchat';
} }

View file

@ -40,10 +40,11 @@ import { CollectionUtil } from 'pragmatismo-io-framework';
const urlJoin = require('url-join'); const urlJoin = require('url-join');
import { DialogKeywords } from './DialogKeywords'; import { DialogKeywords } from './DialogKeywords';
import { ScheduleServices } from './ScheduleServices'; import { ScheduleServices } from './ScheduleServices';
import { HearDialog } from '../dialogs/HearDialog';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
//tslint:disable-next-line:no-submodule-imports //tslint:disable-next-line:no-submodule-imports
const vm = require('vm'); const { VM } = require('vm2');
const vb2ts = require('./vbscript-to-typescript'); const vb2ts = require('./vbscript-to-typescript');
const beautify = require('js-beautify').js; const beautify = require('js-beautify').js;
const textract = require('textract'); const textract = require('textract');
@ -65,7 +66,6 @@ const Path = require('path');
export class GBVMService extends GBService { export class GBVMService extends GBService {
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) { public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
const files = await walkPromise(folder); const files = await walkPromise(folder);
HearDialog.addHearDialog(min);
await CollectionUtil.asyncForEach(files, async file => { await CollectionUtil.asyncForEach(files, async file => {
if (!file) { if (!file) {
@ -173,18 +173,18 @@ export class GBVMService extends GBService {
code = `<%\n code = `<%\n
step=dk.step
id = sys().getRandomId() id = sys().getRandomId()
username = step ? this.userName(step) : sys().getRandomId(); username = step ? dk.userName(step) : sys().getRandomId();
mobile = step ? this.userMobile(step) : sys().getRandomId(); mobile = step ? dk.userMobile(step) : sys().getRandomId();
from = mobile; from = mobile;
ENTER = String.fromCharCode(13); ENTER = String.fromCharCode(13);
ubound = function(array){return array.length}; ubound = function(array){return array.length};
isarray = function(array){return Array.isArray(array) }; isarray = function(array){return Array.isArray(array) };
weekday = this.getWeekFromDate.bind(this); weekday = dk.getWeekFromDate.bind(dk);
hour = this.getHourFromDate.bind(this); hour = dk.getHourFromDate.bind(dk);
base64 = this.getCoded; base64 = dk.getCoded;
tolist = this.getToLst; tolist = dk.getToLst;
headers = {}; headers = {};
data = {}; data = {};
list = []; list = [];
@ -288,7 +288,7 @@ export class GBVMService extends GBService {
}); });
code = code.replace(/CALL\s*(.*)/gi, ($0, $1, $2, $3) => { code = code.replace(/CALL\s*(.*)/gi, ($0, $1, $2, $3) => {
return `sys().callVM("${$1}", this.getMin(), this.getStep(), this.getDeployer())\n`; return `sys().callVM("${$1}", dk.getMin(), dk.getStep(), dk.getDeployer())\n`;
}); });
code = code.replace(/(\w)\s*\=\s*find\s*(.*)/gi, ($0, $1, $2, $3) => { code = code.replace(/(\w)\s*\=\s*find\s*(.*)/gi, ($0, $1, $2, $3) => {
@ -604,7 +604,7 @@ export class GBVMService extends GBService {
const tsfile: string = `${filename}.ts`; const tsfile: string = `${filename}.ts`;
let tsCode: string = fs.readFileSync(tsfile, 'utf8'); let tsCode: string = fs.readFileSync(tsfile, 'utf8');
tsCode = tsCode.replace(/export.*\n/gi, `export function ${mainName}(step:any) { let resolve;`); tsCode = tsCode + `let resolve;`;
fs.writeFileSync(tsfile, tsCode); fs.writeFileSync(tsfile, tsCode);
const tsc = new TSCompiler(); const tsc = new TSCompiler();
tsc.compile([tsfile]); tsc.compile([tsfile]);
@ -621,70 +621,11 @@ export class GBVMService extends GBService {
// Finds all hear calls. // Finds all hear calls.
let parsedCode = code; let parsedCode = code;
const hearExp = /(\w+).*hear.*\((.*)\)/;
let match1;
while ((match1 = hearExp.exec(code))) {
let pos = 0;
// Writes async body.
const variable = match1[1]; // Construct variable = hear ().
const args = match1[2]; // Construct variable = hear ("A", "B").
const promiseName = `promiseFor${variable}`;
parsedCode = code.substring(pos, pos + match1.index);
parsedCode += ``;
parsedCode += `const ${promiseName}= async (step, ${variable}) => {`;
parsedCode += ` return new Promise(async (resolve, reject) => { try {`;
// Skips old construction and point to the async block.
pos = pos + match1.index;
let tempCode = code.substring(pos + match1[0].length + 1);
const start = pos;
// Balances code blocks and checks for exits.
let right = 0;
let left = 1;
let match2;
while ((match2 = /\{|\}/.exec(tempCode))) {
const c = tempCode.substring(match2.index, match2.index + 1);
if (c === '}') {
right++;
} else if (c === '{') {
left++;
}
tempCode = tempCode.substring(match2.index + 1);
pos += match2.index + 1;
if (left === right) {
break;
}
}
parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length);
parsedCode += '}catch(error){reject(error);}});\n';
parsedCode += '}\n';
parsedCode += `hear (step, ${promiseName}, resolve, ${args === '' ? null : args});\n`;
parsedCode += code.substring(pos + match1[0].length);
// A interaction will be made for each hear.
code = parsedCode;
}
parsedCode = this.handleThisAndAwait(parsedCode); parsedCode = this.handleThisAndAwait(parsedCode);
parsedCode = parsedCode.replace(/(\bnow\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'await this.getNow()'); parsedCode = parsedCode.replace(/(\bnow\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'await dk.getNow()');
parsedCode = parsedCode.replace(/(\btoday\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'await this.getToday(step)'); parsedCode = parsedCode.replace(/(\btoday\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'await dk.getToday(step)');
parsedCode = parsedCode.replace(/(\bweekday\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'weekday'); parsedCode = parsedCode.replace(/(\bweekday\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'weekday');
parsedCode = parsedCode.replace(/(\bhour\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'hour'); parsedCode = parsedCode.replace(/(\bhour\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'hour');
parsedCode = parsedCode.replace(/(\btolist\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'tolist'); parsedCode = parsedCode.replace(/(\btolist\b)(?=(?:[^"]|"[^"]*")*$)/gi, 'tolist');
@ -708,89 +649,89 @@ export class GBVMService extends GBService {
private handleThisAndAwait(code: string) { private handleThisAndAwait(code: string) {
// this insertion. // this insertion.
code = code.replace(/sys\(\)/gi, 'this.sys()'); code = code.replace(/sys\(\)/gi, 'dk.sys()');
code = code.replace(/("[^"]*"|'[^']*')|\btalk\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\btalk\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.talk' : $1; return $1 === undefined ? 'dk.talk' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bhear\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bhear\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.hear' : $1; return $1 === undefined ? 'dk.hear' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\baskEmail\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\baskEmail\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.askEmail' : $1; return $1 === undefined ? 'dk.askEmail' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsendFileTo\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsendFileTo\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.sendFileTo' : $1; return $1 === undefined ? 'dk.sendFileTo' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsendFile\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsendFile\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.sendFile' : $1; return $1 === undefined ? 'dk.sendFile' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsetLanguage\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsetLanguage\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.setLanguage' : $1; return $1 === undefined ? 'dk.setLanguage' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bdateAdd\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bdateAdd\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.dateAdd' : $1; return $1 === undefined ? 'dk.dateAdd' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bdateDiff\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bdateDiff\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.dateDiff' : $1; return $1 === undefined ? 'dk.dateDiff' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bgotoDialog\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bgotoDialog\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.gotoDialog' : $1; return $1 === undefined ? 'dk.gotoDialog' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsetMaxLines\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsetMaxLines\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.setMaxLines' : $1; return $1 === undefined ? 'dk.setMaxLines' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsetTranslatorOn\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsetTranslatorOn\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.setTranslatorOn' : $1; return $1 === undefined ? 'dk.setTranslatorOn' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsetTheme\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsetTheme\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.setTheme' : $1; return $1 === undefined ? 'dk.setTheme' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsetWholeWord\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsetWholeWord\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.setWholeWord' : $1; return $1 === undefined ? 'dk.setWholeWord' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\btransferTo\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\btransferTo\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.transferTo' : $1; return $1 === undefined ? 'dk.transferTo' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bchart\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bchart\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.chart' : $1; return $1 === undefined ? 'dk.chart' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bcreateDeal\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bcreateDeal\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.createDeal' : $1; return $1 === undefined ? 'dk.createDeal' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bfndContact\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bfndContact\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.fndContact' : $1; return $1 === undefined ? 'dk.fndContact' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bgetActiveTasks\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bgetActiveTasks\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.getActiveTasks' : $1; return $1 === undefined ? 'dk.getActiveTasks' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bmenu\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bmenu\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.menu' : $1; return $1 === undefined ? 'dk.menu' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bgetPage\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bgetPage\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.getPage' : $1; return $1 === undefined ? 'dk.getPage' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bclick\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bclick\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.click' : $1; return $1 === undefined ? 'dk.click' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\blinkByText\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\blinkByText\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.linkByText' : $1; return $1 === undefined ? 'dk.linkByText' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bpressKey\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bpressKey\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.pressKey' : $1; return $1 === undefined ? 'dk.pressKey' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bscreenshot\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bscreenshot\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.screenshot' : $1; return $1 === undefined ? 'dk.screenshot' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bhover\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bhover\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.hover' : $1; return $1 === undefined ? 'dk.hover' : $1;
}); });
code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/gi, ($0, $1) => { code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.sendEmail' : $1; return $1 === undefined ? 'dk.sendEmail' : $1;
}); });
// await insertion. // await insertion.
code = code.replace(/this\./gm, 'await this.'); code = code.replace(/dk\./gm, 'await dk.');
code = code.replace(/\nfunction/i, 'async function'); code = code.replace(/\nfunction/i, 'async function');
code = code.replace('ubound = async', 'ubound ='); // TODO: Improve this. code = code.replace('ubound = async', 'ubound ='); // TODO: Improve this.
code = code.replace('hour = await', 'hour ='); // TODO: Improve this. code = code.replace('hour = await', 'hour ='); // TODO: Improve this.
@ -837,38 +778,25 @@ export class GBVMService extends GBService {
// Injects the .gbdialog generated code into the VM. // Injects the .gbdialog generated code into the VM.
const context = vm.createContext(sandbox); let code = min.sandBoxMap[text];
const code = min.sandBoxMap[text];
try { code = `(async () => {${code}})();`;
// SEE https://github.com/patriksimek/vm2 sandbox['this'] = sandbox;
vm.runInContext(code, context); const vm = new VM({
} catch (error) { timeout: 600000,
throw new Error(`INVALID BASIC CODE: ${error.message} ${error.stack}`); allowAsync: true,
} sandbox: { dk: sandbox }
});
// Tries to find the method related to this call.
const mainMethod = text.toLowerCase();
if (!sandbox[mainMethod]) {
GBLog.error(`BASIC: Associated '${mainMethod}' dialog not found for: ${min.instance.botId}. Verify if .gbdialog is correctly published.`);
return null;
}
sandbox[mainMethod].bind(sandbox);
// Calls the function. // Calls the function.
let ret = null; let ret = null;
try { try {
ret = await sandbox[mainMethod](step); vm.run(code)
if (ret == -1) {
await step.endDialog();
}
} catch (error) { } catch (error) {
throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`); throw new Error(`BASIC RUNTIME ERR: ${error.message ? error.message : error}\n Stack:${error.stack}`);
} }
return ret; return ret;
} }
} }

View file

@ -30,7 +30,7 @@ function convertImports(input, name) {
result = convertCode(result); result = convertCode(result);
result = convertExpressions(result); result = convertExpressions(result);
result = convertStrings(result); result = convertStrings(result);
result = "\nexport function " + name + "() {\n" + result + "\n}";
for (var _i = 0, items_1 = items; _i < items_1.length; _i++) { for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
var item = items_1[_i]; var item = items_1[_i];
result = "import {" + item.name + "} from \"" + item.path + "\"\n" + result; result = "import {" + item.name + "} from \"" + item.path + "\"\n" + result;

View file

@ -458,7 +458,7 @@ export class GBConversationalService {
? user.systemUser.locale ? user.systemUser.locale
: min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE')) : min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
); );
GBLog.info(`Translated text(playMarkdown): ${text}.`); GBLog.verbose(`Translated text(playMarkdown): ${text}.`);
} }
var renderer = new marked.Renderer(); var renderer = new marked.Renderer();
@ -925,7 +925,7 @@ export class GBConversationalService {
? systemUser.locale ? systemUser.locale
: min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE')) : min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
); );
GBLog.info(`Translated text(prompt): ${text}.`); GBLog.verbose(`Translated text(prompt): ${text}.`);
} }
if (step.activeDialog.state.options['kind'] === "file") { if (step.activeDialog.state.options['kind'] === "file") {
return await step.prompt('attachmentPrompt', {}); return await step.prompt('attachmentPrompt', {});
@ -981,7 +981,7 @@ export class GBConversationalService {
}); });
} }
GBLog.info(`Translated text(sendText): ${text}.`); GBLog.verbose(`Translated text(sendText): ${text}.`);
} }
const analytics = new AnalyticsService(); const analytics = new AnalyticsService();
@ -1034,12 +1034,17 @@ export class GBConversationalService {
else { else {
const ref = JSON.parse(user.conversationReference); const ref = JSON.parse(user.conversationReference);
MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl); MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl);
await min.bot['createConversation'](ref, async (t1) => { try {
await min.bot['continueConversation'](ref, async (t1) => {
const ref2 = TurnContext.getConversationReference(t1.activity); const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async (t2) => { await min.bot.continueConversation(ref2, async (t2) => {
await t2.sendActivity(message); await t2.sendActivity(message);
}); });
}); });
} catch (error) {
console.log(error) ;
}
} }
} }

View file

@ -38,6 +38,7 @@
const cliProgress = require('cli-progress'); const cliProgress = require('cli-progress');
const { DialogSet, TextPrompt } = require('botbuilder-dialogs'); const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
const express = require('express'); const express = require('express');
const Swagger = require('swagger-client');
const Fs = require('fs'); const Fs = require('fs');
const request = require('request-promise-native'); const request = require('request-promise-native');
const removeRoute = require('express-remove-route'); const removeRoute = require('express-remove-route');
@ -171,13 +172,15 @@ export class GBMinService {
GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient.bind(this)); GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient.bind(this));
} }
// Calls mountBot event to all bots. // Calls mountBot event to all bots.
let i = 1;
if (instances.length > 1) {
this.bar1 = new cliProgress.SingleBar({ this.bar1 = new cliProgress.SingleBar({
format: '[{bar}] ({value}/{total}) Loading {botId} ...', barsize: 40, format: '[{bar}] ({value}/{total}) Loading {botId} ...', barsize: 40,
forceRedraw: true forceRedraw: true
}, cliProgress.Presets.rect); }, cliProgress.Presets.rect);
let i = 1;
this.bar1.start(instances.length, i, { botId: "Boot" }); this.bar1.start(instances.length, i, { botId: "Boot" });
}
const throttledPromiseAll = async (promises) => { const throttledPromiseAll = async (promises) => {
const MAX_IN_PROCESS = 20; const MAX_IN_PROCESS = 20;
@ -204,15 +207,18 @@ export class GBMinService {
try { try {
await this['mountBot'](instance); await this['mountBot'](instance);
if (this.bar1) {
this.bar1.update(i++, { botId: instance.botId }); this.bar1.update(i++, { botId: instance.botId });
}
} catch (error) { } catch (error) {
GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`); GBLog.error(`Error mounting bot ${instance.botId}: ${error.message}\n${error.stack}`);
} }
}).bind(this))); }).bind(this)));
if (this.bar1) {
this.bar1.stop(); this.bar1.stop();
}
// Loads schedules. // Loads schedules.
GBLog.info(`Preparing SET SCHEDULE dialog calls...`); GBLog.info(`Preparing SET SCHEDULE dialog calls...`);
@ -321,6 +327,47 @@ export class GBMinService {
}); });
GBLog.verbose(`GeneralBots(${instance.engineName}) listening on: ${url}.`); GBLog.verbose(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
// Test code.
if (process.env.TEST_MESSAGE) {
const client = await new Swagger({
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), usePromise: true
});
client.clientAuthorizations.add(
'AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${min.instance.webchatKey}`, 'header')
);
const response = await client.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
GBServer.globals.debugConversationId = conversationId;
const steps = process.env.TEST_MESSAGE.split(';');
const sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
await CollectionUtil.asyncForEach(steps, async step => {
client.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: step,
type: 'message',
from: {
id: 'test',
name: 'test'
}
}
});
await sleep(15000);
});
}
// Serves individual URL for each bot user interface. // Serves individual URL for each bot user interface.
if (process.env.DISABLE_WEB !== 'true') { if (process.env.DISABLE_WEB !== 'true') {
@ -796,11 +843,14 @@ export class GBMinService {
try { try {
const sec = new SecService();
const user = await min.userProfile.get(context, {}); const user = await min.userProfile.get(context, {});
const conversationReference = JSON.stringify(
TurnContext.getConversationReference(context.activity)
);
// First time processing. // First time processing.
const sec = new SecService();
if (!user.loaded) { if (!user.loaded) {
if (step.context.activity.channelId !== 'msteams') { if (step.context.activity.channelId !== 'msteams') {
await min.conversationalService.sendEvent(min, step, 'loadInstance', {}); await min.conversationalService.sendEvent(min, step, 'loadInstance', {});
@ -839,6 +889,8 @@ export class GBMinService {
} }
await sec.updateConversationReferenceById(user.systemUser.userId, conversationReference);
if (step.context.activity.channelId !== 'msteams') { if (step.context.activity.channelId !== 'msteams') {
const service = new KBService(min.core.sequelize); const service = new KBService(min.core.sequelize);
const data = await service.getFaqBySubjectArray(instance.instanceId, 'faq', undefined); const data = await service.getFaqBySubjectArray(instance.instanceId, 'faq', undefined);
@ -875,10 +927,7 @@ export class GBMinService {
step.context.activity.text = urlJoin(GBServer.globals.publicAddress, `${min.instance.botId}`, 'cache', filename); step.context.activity.text = urlJoin(GBServer.globals.publicAddress, `${min.instance.botId}`, 'cache', filename);
} }
const conversationReference = JSON.stringify(
TurnContext.getConversationReference(context.activity)
);
await sec.updateConversationReferenceById(user.systemUser.userId, conversationReference);
if (!user.welcomed) { if (!user.welcomed) {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null); const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
@ -892,7 +941,7 @@ export class GBMinService {
// Required for F0 handling of persisted conversations. // Required for F0 handling of persisted conversations.
GBLog.info(`User>: text:${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId}, value: ${context.activity.value})`); GBLog.info(`Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`);
// Answer to specific BOT Framework event conversationUpdate to auto start dialogs. // Answer to specific BOT Framework event conversationUpdate to auto start dialogs.
// Skips if the bot is talking. // Skips if the bot is talking.
@ -1219,7 +1268,7 @@ export class GBMinService {
GBConfigService.get('DEFAULT_CONTENT_LANGUAGE') GBConfigService.get('DEFAULT_CONTENT_LANGUAGE')
); );
text = await min.conversationalService.translate(min, text, contentLocale); text = await min.conversationalService.translate(min, text, contentLocale);
GBLog.info(`Translated text (processMessageActivity): ${text}.`); GBLog.verbose(`Translated text (processMessageActivity): ${text}.`);
// Restores all token text back after spell checking and translation. // Restores all token text back after spell checking and translation.
@ -1233,7 +1282,7 @@ export class GBMinService {
step.context.activity['text'] = text; step.context.activity['text'] = text;
step.context.activity['originalText'] = originalText; step.context.activity['originalText'] = originalText;
GBLog.info(`Final text ready for NLP/Search/.gbapp: ${text}.`); GBLog.info(`Text>: ${text}.`);
if (user.systemUser.agentMode === 'self') { if (user.systemUser.agentMode === 'self') {
const manualUser = await sec.getUserFromAgentSystemId(user.systemUser.userSystemId); const manualUser = await sec.getUserFromAgentSystemId(user.systemUser.userSystemId);
@ -1258,9 +1307,14 @@ export class GBMinService {
} }
else { else {
if (min.cbMap[user.systemUser.userId] &&
min.cbMap[user.systemUser.userId].promise == '!GBHEAR') {
min.cbMap[user.systemUser.userId].promise = text;
}
// If there is a dialog in course, continue to the next step. // If there is a dialog in course, continue to the next step.
if (step.activeDialog !== undefined) { else if (step.activeDialog !== undefined) {
await step.continueDialog(); await step.continueDialog();
} else { } else {

View file

@ -69,6 +69,7 @@ export class RootData {
public minBoot: GBMinInstance; // Reference to boot bot. public minBoot: GBMinInstance; // Reference to boot bot.
public wwwroot: string; // .gbui or a static webapp. public wwwroot: string; // .gbui or a static webapp.
public entryPointDialog: string; // To replace default welcome dialog. public entryPointDialog: string; // To replace default welcome dialog.
public debugConversationId: any; // Used to self-message during debug.
} }
/** /**
* General Bots open-core entry point. * General Bots open-core entry point.

17301
yarn.lock

File diff suppressed because it is too large Load diff