diff --git a/DATABASE-CHANGES.md b/DATABASE-CHANGES.md index 3afbdd3c..afdb79ff 100644 --- a/DATABASE-CHANGES.md +++ b/DATABASE-CHANGES.md @@ -59,4 +59,11 @@ ALTER TABLE dbo.GuaribasPackage ADD params custom(512) NULL GO -``` \ No newline at end of file +``` + + +# 2.0.56 + +ALTER TABLE dbo.GuaribasUser ADD + hearOnDialog nvarchar(64) NULL +GO \ No newline at end of file diff --git a/packages/core.gbapp/services/GBAPIService.ts b/packages/core.gbapp/services/GBAPIService.ts index fe150266..9461ce95 100644 --- a/packages/core.gbapp/services/GBAPIService.ts +++ b/packages/core.gbapp/services/GBAPIService.ts @@ -43,7 +43,7 @@ import { GBDeployer } from './GBDeployer'; const MicrosoftGraph = require('@microsoft/microsoft-graph-client'); import { Messages } from '../strings'; import { GBServer } from '../../../src/app'; -import { CollectionUtil } from 'pragmatismo-io-framework'; +import { SecService } from '../../security.gbapp/services/SecService'; const request = require('request-promise-native'); /** @@ -90,6 +90,15 @@ class SysClass { return data; } + public async gotoDialog(from: string, dialogName: string) { + let sec = new SecService(); + let user = await sec.getUserFromSystemId(from); + if (!user) { + user = await sec.ensureUser(this.min.instance.instanceId, from, from, null, 'whatsapp', 'from'); + } + await sec.updateUserHearOnDialog(user.userId, dialogName); + } + public async wait(seconds: number) { // tslint:disable-next-line no-string-based-set-timeout GBLog.info(`BASIC: Talking to a specific user (TALK TO).`); @@ -246,6 +255,7 @@ class SysClass { } } + public async find(file: string, ...args): Promise { let token = await this.min.adminService.acquireElevatedToken(this.min.instance.instanceId); @@ -358,6 +368,7 @@ class SysClass { ); } + /** * Generic function to call any REST API. */ @@ -469,14 +480,14 @@ export class DialogClass { } } public async isAffirmative(step, text) { - return text.toLowerCase().match(Messages[step.context.activity.locale].affirmative_sentences); + return text.toLowerCase().match(Messages[step.context.activity.locale].affirmative_sentences); } - + public async getNow(step) { const nowUTC = new Date(); const now = new Date((typeof nowUTC === "string" ? - new Date(nowUTC) : - nowUTC).toLocaleString("en-US", {timeZone: process.env.DEFAULT_TIMEZONE})); + new Date(nowUTC) : + nowUTC).toLocaleString("en-US", { timeZone: process.env.DEFAULT_TIMEZONE })); return now.getHours() + ':' + now.getMinutes(); } @@ -533,7 +544,7 @@ export class DialogClass { return await step.beginDialog('/t'); } - public async hear(step, promise, previousResolve) { + public async hear(step, promise, previousResolve, kind) { function random(low, high) { return Math.random() * (high - low) + low; } @@ -541,7 +552,7 @@ export class DialogClass { this.min.cbMap[idPromise] = {}; this.min.cbMap[idPromise].promise = promise; - const opts = { id: idPromise, previousResolve: previousResolve }; + const opts = { id: idPromise, previousResolve: previousResolve, kind: kind }; if (previousResolve !== undefined) { previousResolve(opts); } else { diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 291b9a2c..831f9d51 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -163,13 +163,21 @@ export class GBMinService { let sec = new SecService(); let user = await sec.getUserFromSystemId(id); - if (user === null) { + if (user === null || user.hearOnDialog) { user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName); - let startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); + let startDialog = user.hearOnDialog ? + user.hearOnDialog : + activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); + GBLog.info(`Auto start dialog is now being called: ${startDialog}...`); if (startDialog) { req.body.messages[0].body = `${startDialog}`; + + // Resets HEAR ON DIALOG value to none and passes + // current dialog to the direct line. + + await sec.updateUserHearOnDialog(user.userId, null); await (activeMin as any).whatsAppDirectLine.received(req, res); } else { @@ -759,6 +767,7 @@ export class GBMinService { await step.beginDialog(cmdOrDialogName, { args: args }); } } else if (globalQuit(step.context.activity.locale, context.activity.text)) { + // TODO: Hard-code additional languages. await step.cancelAllDialogs(); await min.conversationalService.sendText(min, step, Messages[step.context.activity.locale].canceled); @@ -768,6 +777,7 @@ export class GBMinService { // Checks for /menu JSON signature. } else if (context.activity.text.startsWith('{"title"')) { await step.beginDialog('/menu', JSON.parse(context.activity.text)); + // Otherwise, continue to the active dialog in the stack. } else if ( !(await this.deployer.getStoragePackageByName(min.instance.instanceId, `${min.instance.botId}.gbkb`)) && @@ -782,14 +792,14 @@ export class GBMinService { const originalText = text; text = text.replace(/<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi, ''); - // Spells check the input text before translating. + // Spells check the input text before translating, + // keeping fixed tokens as specified in Config. const keepText: string = min.core.getParam( min.instance, 'Keep Text', null ); - if (keepText) { const list = keepText.split(';'); let i = 0; @@ -807,6 +817,7 @@ export class GBMinService { text = text.replace(`KEEPTEXT${i}`, item.trim()); }); } + // Detects user typed language and updates their locale profile if applies. let locale = min.core.getParam( @@ -830,13 +841,15 @@ export class GBMinService { } } - const hasBadWord = wash.check(locale, context.activity.text); + // Checks for bad words on input text. + const hasBadWord = wash.check(locale, context.activity.text); if (hasBadWord) { return await step.beginDialog('/pleaseNoBadWords'); } - // Translates the input text if is turned on instance params. + // Translates text into content language, keeping + // reserved tokens specified in Config. if (keepText) { const list = keepText.split(';'); @@ -860,7 +873,6 @@ export class GBMinService { text = text.replace(new RegExp(item.trim(), 'gi'), `KEEPTEXT${i}`); }); } - text = await min.conversationalService.translate(min, text, contentLocale); if (keepText) { const list = keepText.split(';'); diff --git a/packages/core.gbapp/services/GBVMService.ts b/packages/core.gbapp/services/GBVMService.ts index 3307ea09..b4e1f229 100644 --- a/packages/core.gbapp/services/GBVMService.ts +++ b/packages/core.gbapp/services/GBVMService.ts @@ -168,6 +168,10 @@ export class GBVMService extends GBService { code = code.replace(/(hear email)/gi, `email = askEmail()`); + code = code.replace(/(hear on)(\s)(.*)/gi, ($0, $1, $2, $3) => { + return `sys().gotoDialog(${$3})\n`; + }); + code = code.replace(/(hear)\s*(\w+)/gi, ($0, $1, $2) => { return `${$2} = hear()`; }); @@ -316,7 +320,15 @@ export class GBVMService extends GBService { parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length); parsedCode += '});\n'; parsedCode += '}\n'; - parsedCode += `hear (step, ${promiseName}, resolve);\n`; + + + let kind = 'general'; + if (variable === "YES OR NO") { + kind = 'yesOrNo'; + } + + + parsedCode += `hear (step, ${promiseName}, resolve, '${kind}');\n`; parsedCode += code.substring(pos + match1[0].length); // A interaction will be made for each hear. @@ -324,8 +336,6 @@ export class GBVMService extends GBService { code = parsedCode; } - - parsedCode = this.handleThisAndAwait(parsedCode); parsedCode = parsedCode.replace(/(now)(?=(?:[^"]|"[^"]*")*$)/gi, 'await this.getNow(step)'); @@ -335,7 +345,7 @@ export class GBVMService extends GBService { fs.writeFileSync(jsfile, parsedCode); this.executeJS(min, deployer, parsedCode, mainName); - GBLog.info(`[GBVMService] Finished loading of ${filename}, JavaScript from Word: ${parsedCode}`); + GBLog.info(`[GBVMService] Finished loading of ${filename}, JavaScript from Word: \n ${parsedCode}`); } } @@ -386,20 +396,69 @@ export class GBVMService extends GBService { min.dialogs.add( new WaterfallDialog('/hear', [ async step => { - step.activeDialog.state.options = {}; - step.activeDialog.state.options.cbId = (step.options as any).id; + 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; - GBLog.info('BASIC: Asking for input (HEAR).'); + + if (step.options['kind'] === "yesOrNo") { + + GBLog.info('BASIC: Asking for input (HEAR YES OR NO).'); + } + else { + + GBLog.info('BASIC: Asking for input (HEAR).'); + } + return await min.conversationalService.prompt(min, step, null); }, async step => { - const cbId = step.activeDialog.state.options.cbId; - if (min.cbMap[cbId]) { - const promise = min.cbMap[cbId].promise; - delete min.cbMap[cbId]; + const isIntentYes = (locale, utterance) => { + return utterance.toLowerCase().match(Messages[locale].affirmative_sentences); + } + + let result = step.result; + if (step.activeDialog.state.options['boolean']) { + + if (isIntentYes(step.context.locale, step.result)) { + result = true; + } + else { + return await step.replaceDialog('/hear', step.activeDialog.state.options); + } + + } + else if (step.activeDialog.state.options['email']) { + // e@e.com + } + else if (step.activeDialog.state.options['number']) { + // MAX and MIN. + } + else if (step.activeDialog.state.options['date']) { + // 12/12/2020 OK + } + else if (step.activeDialog.state.options['hour']) { + // 12:12 + } + else if (step.activeDialog.state.options['money']) { + // 23,12 + } + else if (step.activeDialog.state.options['phone']) { + // +55 21 + } + else if (step.activeDialog.state.options['zipcode']) { + // 12333-222 + } + else if (step.activeDialog.state.options['menu']){ + // ['drums', 'guitar', 'bass']; kpmSearch + } + + 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, step.result); + const opts = await promise(step, result); return await step.replaceDialog('/hear', opts); } catch (error) { GBLog.error(`Error in BASIC code: ${error}`); diff --git a/packages/security.gbapp/models/index.ts b/packages/security.gbapp/models/index.ts index 83413c1a..558b88d7 100644 --- a/packages/security.gbapp/models/index.ts +++ b/packages/security.gbapp/models/index.ts @@ -91,6 +91,9 @@ export class GuaribasUser extends Model { @Column(DataType.TEXT) @Column conversationReference: string + + @Column(DataType.STRING(64)) + hearOnDialog: string; } /** diff --git a/packages/security.gbapp/services/SecService.ts b/packages/security.gbapp/services/SecService.ts index a657b5e3..b5c7c7a9 100644 --- a/packages/security.gbapp/services/SecService.ts +++ b/packages/security.gbapp/services/SecService.ts @@ -91,6 +91,16 @@ export class SecService extends GBService { return await user.save(); } + public async updateUserHearOnDialog(userId: number, dialogName: string): Promise { + let user = await GuaribasUser.findOne({ + where: { + userId: userId + } + }); + user.hearOnDialog = dialogName; + return await user.save(); + } + public async updateUserInstance(userSystemId: string, instanceId: number): Promise { let user = await GuaribasUser.findOne({ where: {