From cea3b54970bb23de8e556c12a2bead89528de8b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Rodriguez Date: Thu, 19 May 2022 10:22:22 -0300 Subject: [PATCH] new(all): Automation new keywords and WhatsApp transfer. --- package-lock.json | 217 ++++++-- package.json | 4 +- .../basic.gblib/services/DialogKeywords.ts | 47 +- packages/basic.gblib/services/GBVMService.ts | 20 +- .../basic.gblib/services/SystemKeywords.ts | 20 +- .../services/GBConversationalService.ts | 23 +- packages/core.gbapp/services/GBMinService.ts | 2 +- .../dialogs/FeedbackDialog.ts | 465 +++++++++--------- .../customer-satisfaction.gbapp/strings.ts | 4 +- packages/security.gbapp/models/index.ts | 2 +- .../security.gbapp/services/SecService.ts | 47 +- .../services/WhatsappDirectLine.ts | 29 +- 12 files changed, 544 insertions(+), 336 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f48ee73..8edec726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3761,6 +3761,15 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/puppeteer": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.6.tgz", + "integrity": "sha512-98Kghehs7+/GD9b56qryhqdqVCXUTbetTv3PlvDnmFRTHQH0j9DIp1f7rkAW3BAj4U3yoeSEQnKgdW8bDq0Y0Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/request": { "version": "2.48.5", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", @@ -3892,9 +3901,9 @@ "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==" }, "@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "optional": true, "requires": { "@types/node": "*" @@ -8752,9 +8761,9 @@ } }, "devtools-protocol": { - "version": "0.0.818844", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz", - "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==" + "version": "0.0.981744", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz", + "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==" }, "dezalgo": { "version": "1.0.3", @@ -9752,9 +9761,9 @@ }, "dependencies": { "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -18286,33 +18295,44 @@ } }, "puppeteer": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz", - "integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-13.7.0.tgz", + "integrity": "sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==", "requires": { - "debug": "^4.1.0", - "devtools-protocol": "0.0.818844", - "extract-zip": "^2.0.0", - "https-proxy-agent": "^4.0.0", - "node-fetch": "^2.6.1", - "pkg-dir": "^4.2.0", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^3.0.2", - "tar-fs": "^2.0.0", - "unbzip2-stream": "^1.3.3", - "ws": "^7.2.3" + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.981744", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "pkg-dir": "4.2.0", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.5.0" }, "dependencies": { "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -18327,11 +18347,11 @@ } }, "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "requires": { - "agent-base": "5", + "agent-base": "6", "debug": "4" } }, @@ -18343,6 +18363,14 @@ "p-locate": "^4.1.0" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -18376,6 +18404,11 @@ "requires": { "find-up": "^4.0.0" } + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==" } } }, @@ -20405,6 +20438,11 @@ "typescript": "^4.1.3" }, "dependencies": { + "agent-base": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -20423,11 +20461,103 @@ "supports-color": "^5.3.0" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "devtools-protocol": { + "version": "0.0.818844", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz", + "integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "https-proxy-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "requires": { + "agent-base": "5", + "debug": "4" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "puppeteer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz", + "integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==", + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.818844", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^4.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -21327,6 +21457,11 @@ "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -22349,6 +22484,11 @@ "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=", "dev": true }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, "websocket": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", @@ -22377,6 +22517,15 @@ } } }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 25d7b4d7..386855d3 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "pragmatismo-io-framework": "1.0.20", "prism-media": "1.3.1", "public-ip": "4.0.4", + "puppeteer": "13.7.0", "readline": "1.3.0", "reflect-metadata": "0.1.13", "request-promise": "4.2.5", @@ -116,7 +117,7 @@ "simple-git": "2.39.1", "speakingurl": "14.0.1", "sppull": "2.7.0", - "ssr-for-bots": "^1.0.1-c", + "ssr-for-bots": "1.0.1-c", "strict-password-generator": "1.1.2", "swagger-client": "2.1.18", "tedious": "14.0.0", @@ -128,6 +129,7 @@ "washyourmouthoutwithsoap": "1.0.2" }, "devDependencies": { + "@types/puppeteer": "5.4.6", "@types/url-join": "4.0.0", "@types/winston": "2.4.4", "ban-sensitive-files": "1.9.15", diff --git a/packages/basic.gblib/services/DialogKeywords.ts b/packages/basic.gblib/services/DialogKeywords.ts index c66f2892..206f8923 100644 --- a/packages/basic.gblib/services/DialogKeywords.ts +++ b/packages/basic.gblib/services/DialogKeywords.ts @@ -84,9 +84,6 @@ export class DialogKeywords { this.min = min; this.user = user; this.internalSys = new SystemKeywords(min, deployer, this); - (async () => { - this.browser = await puppeteer.launch(); - }); } /** @@ -103,6 +100,21 @@ export class DialogKeywords { * @example x = GET PAGE */ public async getPage(step, url) { + + if (!this.browser) + { + this.browser = await puppeteer.launch({ + args: [ + '--ignore-certificate-errors', + '--no-sandbox', + '--disable-setuid-sandbox', + '--window-size=1920,1080', + "--disable-accelerated-2d-canvas", + "--disable-gpu"], + ignoreHTTPSErrors: true, + headless: false, + }); + } const page = await this.browser.newPage(); await page.goto(url); return page; @@ -114,11 +126,14 @@ export class DialogKeywords { * @example GET page, "elementName", "text" */ private async getByIDOrName(page, elementName) { - return await page.$(`[name="${elementName}"]`); + + await page.waitForSelector(elementName) + let element = await page.$(elementName); + return element; } /** - * Returns the today data filled in dd/mm/yyyy or mm/dd/yyyy. + * Returns the today data filled in dd/mm/yyyy or mm/dd/yyyy. * * @example x = TODAY */ @@ -142,18 +157,6 @@ export class DialogKeywords { await e.screenshot({ path: localName }); } - /** - * Returns the screenshot of page or element - * - * @example file = SCREENSHOT page - */ - public async captcha(step, page, idOrName) { - const e = await this.getByIDOrName(page, idOrName); - - - - } - /** * Performs the download to the .gbdrive Download folder. * @@ -174,17 +177,14 @@ export class DialogKeywords { const path = await download.path(); console.log(path); - } - - /** * Types the text into the text field. * * @example TYPE page, "elementName", "text" */ - public async type(step, page, idOrName, text) { + public async type( step, page, idOrName, text) { const e = await this.getByIDOrName(page, idOrName); await e.type(text); } @@ -578,8 +578,8 @@ export class DialogKeywords { * @example TRANSFER * */ - public async transfer(step) { - return await step.beginDialog('/t'); + public async transfer(step, to: string = null) { + return await step.beginDialog('/t', {to: to}); } /** @@ -665,6 +665,7 @@ export class DialogKeywords { } private static getChannel(step): string { + if(!step) return 'whatsapp'; if (!isNaN(step.context.activity['mobile'])) { return 'webchat'; } else { diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index 3d130de7..ca7dee30 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -197,7 +197,7 @@ export class GBVMService extends GBService { // Keywords from General Bots BASIC. code = code.replace(/(\w+)\s*\=\s*get html\s*(.*)/gi, ($0, $1, $2, $3) => { - return `${$1} = sys().getPage ("${$2}")\n`; + return `${$1} = getPage(step, ${$2})\n`; }); code = code.replace(/(set hear on)(\s*)(.*)/gi, ($0, $1, $2, $3) => { return `hrOn = ${$3}\n`; @@ -313,7 +313,7 @@ export class GBVMService extends GBService { } else { if ($2.indexOf(',') !== -1) { const values = $2.split(','); - return `${$1} = getByIDOrName(${values[0]}, ${values[1]} )`; + return `${$1} = this.getByIDOrName(${values[0]}, ${values[1]} )`; } else { return `${$1} = sys().get (${$2})`; @@ -366,13 +366,17 @@ export class GBVMService extends GBService { }); code = code.replace(/(\w+)\s*\=\s*download\s*(.*),\s*(.*)/gi, ($0, $1, $2, $3) => { - return `${$1} = sys().download (${$2}, ${$3})`; + return `${$1} = download (${$2}, ${$3})`; }); code = code.replace(/(create a bot farm using)(\s)(.*)/gi, ($0, $1, $2, $3) => { return `sys().createABotFarmUsing (${$3})`; }); + code = code.replace(/(transfer to)(\s)(.*)/gi, ($0, $1, $2, $3) => { + return `transfer (step, ${$3})\n`; + }); + code = code.replace(/(transfer)(?=(?:[^"]|"[^"]*")*$)/gi, () => { return `transfer (step)\n`; }); @@ -610,7 +614,15 @@ export class GBVMService extends GBService { code = code.replace(/("[^"]*"|'[^']*')|\bmenu\b/gi, ($0, $1) => { return $1 === undefined ? 'this.menu' : $1; }); - + code = code.replace(/("[^"]*"|'[^']*')|\bgetPage\b/gi, ($0, $1) => { + return $1 === undefined ? 'this.getPage' : $1; + }); + code = code.replace(/("[^"]*"|'[^']*')|\bclick\b/gi, ($0, $1) => { + return $1 === undefined ? 'this.click' : $1; + }); + code = code.replace(/("[^"]*"|'[^']*')|\bdownload\b/gi, ($0, $1) => { + return $1 === undefined ? 'this.download' : $1; + }); // await insertion. code = code.replace(/this\./gm, 'await this.'); diff --git a/packages/basic.gblib/services/SystemKeywords.ts b/packages/basic.gblib/services/SystemKeywords.ts index dbdcb770..7fd42b30 100644 --- a/packages/basic.gblib/services/SystemKeywords.ts +++ b/packages/basic.gblib/services/SystemKeywords.ts @@ -240,12 +240,28 @@ export class SystemKeywords { } /** - * Defines a cell value in the tabular file. + * 1. Defines a cell value in the tabular file. + * 2. Defines an element text on HTML page. * * @example SET "file.xlsx", "A2", 4500 * + * @example SET page, "elementHTMLSelector", "text" + * */ - public async set(file: string, address: string, value: any): Promise { + public async set(file: any, address: string, value: any): Promise { + + // Handles calls for HTML stuff + + if (file._javascriptEnabled) + { + GBLog.info(`BASIC: Web automation setting ${file}' to '${value}' (SET). `); + + await this.dk.type(null, file, address, value ); + return; + } + + // Handles calls for BASIC persistence on sheet files. + GBLog.info(`BASIC: Defining '${address}' in '${file}' to '${value}' (SET). `); let [baseUrl, client] = await GBDeployer.internalGetDriveClient(this.min); diff --git a/packages/core.gbapp/services/GBConversationalService.ts b/packages/core.gbapp/services/GBConversationalService.ts index 35ccf1a8..df35237f 100644 --- a/packages/core.gbapp/services/GBConversationalService.ts +++ b/packages/core.gbapp/services/GBConversationalService.ts @@ -412,18 +412,21 @@ export class GBConversationalService { step: GBDialogStep, mobile: string ) { - const user = await min.userProfile.get(step.context, {}); + const user = step ? await min.userProfile.get(step.context, {}) : null; + let text = answer; // Calls language translator. - - let text = await min.conversationalService.translate( - min, - answer, - user.systemUser.locale - ? user.systemUser.locale - : min.core.getParam(min.instance, 'Locale', GBConfigService.get('LOCALE')) - ); - GBLog.info(`Translated text(playMarkdown): ${text}.`); + if (user) + { + text = await min.conversationalService.translate( + min, + answer, + user.systemUser.locale + ? user.systemUser.locale + : min.core.getParam(min.instance, 'Locale', GBConfigService.get('LOCALE')) + ); + GBLog.info(`Translated text(playMarkdown): ${text}.`); + } var renderer = new marked.Renderer(); renderer.oldImage = renderer.image; diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 5d99c82b..e5b67329 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -461,7 +461,7 @@ export class GBMinService { } } else { let minInstance = GBServer.globals.minInstances.filter( - p => p.instance.botId.toLowerCase() === botId + p => p.instance.botId.toLowerCase() === botId.toLowerCase() )[0]; diff --git a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts index 171b6f82..42223ff2 100644 --- a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts +++ b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts @@ -34,228 +34,245 @@ * @fileoverview General Bots server core. */ - 'use strict'; +'use strict'; - import { BotAdapter } from 'botbuilder'; - import { WaterfallDialog } from 'botbuilder-dialogs'; - import { GBMinInstance, IGBDialog } from 'botlib'; - import { GBMinService } from '../../core.gbapp/services/GBMinService'; - import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; - import { SecService } from '../../security.gbapp/services/SecService'; - import { CSService } from '../services/CSService'; - import { Messages } from '../strings'; - - /** - * Dialog for feedback collecting. - */ - export class FeedbackDialog extends IGBDialog { - /** - * Setup dialogs flows and define services call. - * - * @param bot The bot adapter. - * @param min The minimal bot instance data. - */ - public static setup(bot: BotAdapter, min: GBMinInstance) { - const service = new CSService(); - - min.dialogs.add( - new WaterfallDialog('/pleaseNoBadWords', [ - async step => { - const locale = step.context.activity.locale; - await min.conversationalService.sendText(min, step, Messages[locale].please_no_bad_words); - - return await step.next(); - } - ]) - ); - - min.dialogs.add( - new WaterfallDialog('/t', [ - async step => { - if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { - return await step.beginDialog('/auth'); - } - else{ - return await step.next(step.options); - } - }, - async step => { - - const locale = step.context.activity.locale; - - const sec = new SecService(); - let from = GBMinService.userMobile(step); - - await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering); - const agentSystemId = await sec.assignHumanAgent(min, from); - - const user = await min.userProfile.get(step.context, {}); - user.systemUser = await sec.getUserFromAgentSystemId(agentSystemId); - await min.userProfile.set(step.context, user); - - if (agentSystemId.charAt(2) === ":" || agentSystemId.indexOf("@") > -1) { // Agent is from Teams or Google Chat. - const agent = await sec.getUserFromSystemId(agentSystemId); - await min.conversationalService['sendOnConversation'](min, agent, - Messages[locale].notify_agent(step.context.activity.from.name)); - - } - else { - await min.whatsAppDirectLine.sendToDevice(agentSystemId, Messages[locale].notify_agent(step.context.activity.from.name)); - - } - return await step.next(); - } - ]) - ); - - min.dialogs.add( - new WaterfallDialog('/qt', [ - async step => { - if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { - return await step.beginDialog('/auth'); - } - else{ - return await step.next(step.options); - } - }, - async step => { - - const locale = step.context.activity.locale; - - const sec = new SecService(); - const userSystemId = GBMinService.userMobile(step); - const user = await min.userProfile.get(step.context, {}); - - if (user.systemUser.agentMode === 'self') { - const manualUser = await sec.getUserFromAgentSystemId(userSystemId); - - await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId, - Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); - - if (userSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. - await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); - } - else { - await min.whatsAppDirectLine.sendToDeviceEx(userSystemId, - Messages[locale].notify_end_transfer(min.instance.botId), locale - , step.context.activity.conversation.id); - } - - await sec.updateHumanAgent(userSystemId, min.instance.instanceId, null); - await sec.updateHumanAgent(manualUser.userSystemId, min.instance.instanceId, null); - - user.systemUser = await sec.getUserFromSystemId(userSystemId); - await min.userProfile.set(step.context, user); - - } - - else if (user.systemUser.agentMode === 'human') { - const agent = await sec.getUserFromSystemId(user.systemUser.agentSystemId); - - await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, - Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); - - - if (user.systemUser.agentSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. - await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); - } - else { - await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.agentSystemId, - Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); - } - - await sec.updateHumanAgent(user.systemUser.userSystemId, min.instance.instanceId, null); - await sec.updateHumanAgent(agent.userSystemId, min.instance.instanceId, null); - - user.systemUser = await sec.getUserFromSystemId(userSystemId); - await min.userProfile.set(step.context, user); - - } - else - { - if (user.systemUser.userSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. - await min.conversationalService.sendText(min, step, 'Nenhum atendimento em andamento.'); - } - else { - await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, - 'Nenhum atendimento em andamento.', locale, step.context.activity.conversation.id); - } - } - - return await step.next(); - } - ]) - ); - - min.dialogs.add( - new WaterfallDialog('/feedbackNumber', [ - async step => { - if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { - return await step.beginDialog('/auth'); - } - else{ - return await step.next(step.options); - } - }, - async step => { - const locale = step.context.activity.locale; - - return await step.next(); - }, - async step => { - const locale = step.context.activity.locale; - const rate = step.result.entity; - const user = await min.userProfile.get(step.context, {}); - await service.updateConversationRate(user.conversation, rate); - await min.conversationalService.sendText(min, step, Messages[locale].thanks); - - return await step.next(); - } - ]) - ); - - min.dialogs.add( - new WaterfallDialog('/feedback', [ - async step => { - if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { - return await step.beginDialog('/auth'); - } - else{ - return await step.next(step.options); - } - }, - async step => { - const locale = step.context.activity.locale; - - await min.conversationalService.sendText(min, step, Messages[locale].about_suggestions); - step.activeDialog.state.cbId = (step.options as any).id; - - return await min.conversationalService.prompt(min, step, Messages[locale].what_about_service); - }, - async step => { - const fixedLocale = 'en-US'; - const user = await min.userProfile.get(step.context, {}); - - // Updates values to perform Bot Analytics. - - const analytics = new AnalyticsService(); - const rate = await analytics.updateConversationSuggestion( - min.instance.instanceId, user.conversation.conversationId, step.result, user.systemUser.locale); - - if (rate > 0.5) { - await min.conversationalService.sendText(min, step, Messages[fixedLocale].glad_you_liked); - } else { - - const message = min.core.getParam(min.instance, 'Feedback Improve Message', - Messages[fixedLocale].we_will_improve); // TODO: Improve to be multi-language. - - await min.conversationalService.sendText(min, step, message); - } - - return await step.replaceDialog('/ask', { isReturning: true }); - } - ]) - ); - } - } - \ No newline at end of file +import { BotAdapter } from 'botbuilder'; +import { WaterfallDialog } from 'botbuilder-dialogs'; +import { GBMinInstance, IGBDialog } from 'botlib'; +import { GBMinService } from '../../core.gbapp/services/GBMinService'; +import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; +import { SecService } from '../../security.gbapp/services/SecService'; +import { CSService } from '../services/CSService'; +import { Messages } from '../strings'; + +/** + * Dialog for feedback collecting. + */ +export class FeedbackDialog extends IGBDialog { + /** + * Setup dialogs flows and define services call. + * + * @param bot The bot adapter. + * @param min The minimal bot instance data. + */ + public static setup(bot: BotAdapter, min: GBMinInstance) { + const service = new CSService(); + + min.dialogs.add( + new WaterfallDialog('/pleaseNoBadWords', [ + async step => { + const locale = step.context.activity.locale; + await min.conversationalService.sendText(min, step, Messages[locale].please_no_bad_words); + + return await step.next(); + } + ]) + ); + + min.dialogs.add( + new WaterfallDialog('/t', [ + async step => { + if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { + return await step.beginDialog('/auth'); + } + else { + return await step.next(step.options); + } + }, + async step => { + + const locale = step.context.activity.locale; + const sec = new SecService(); + let from = GBMinService.userMobile(step); + const user = await min.userProfile.get(step.context, {}); + + const args = step.activeDialog.state.options.args; + + // Transfer to... + + if (args && args.to) { + + + // An user from Teams willing to transfer to a WhatsApp user. + + await sec.assignHumanAgent(min, args.to, user.userSystemId); + await min.conversationalService.sendText(min, step, + Messages[locale].notify_agent_transfer_done(min.instance.botId)); + + } + else { + + + await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering); + const agentSystemId = await sec.assignHumanAgent(min, from); + user.systemUser = await sec.getUserFromAgentSystemId(agentSystemId); + await min.userProfile.set(step.context, user); + + if (agentSystemId.charAt(2) === ":" || agentSystemId.indexOf("@") > -1) { // Agent is from Teams or Google Chat. + + const agent = await sec.getUserFromSystemId(agentSystemId); + await min.conversationalService['sendOnConversation'](min, agent, + Messages[locale].notify_agent(step.context.activity.from.name)); + + } + else { + + await min.whatsAppDirectLine.sendToDevice(agentSystemId, Messages[locale].notify_agent(step.context.activity.from.name)); + + } + } + return await step.next(); + + } + ]) + ); + + min.dialogs.add( + new WaterfallDialog('/qt', [ + async step => { + if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { + return await step.beginDialog('/auth'); + } + else { + return await step.next(step.options); + } + }, + async step => { + + const locale = step.context.activity.locale; + + const sec = new SecService(); + const userSystemId = GBMinService.userMobile(step); + const user = await min.userProfile.get(step.context, {}); + + if (user.systemUser.agentMode === 'self') { + const manualUser = await sec.getUserFromAgentSystemId(userSystemId); + + await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId, + Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); + + if (userSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. + await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); + } + else { + await min.whatsAppDirectLine.sendToDeviceEx(userSystemId, + Messages[locale].notify_end_transfer(min.instance.botId), locale + , step.context.activity.conversation.id); + } + + await sec.updateHumanAgent(userSystemId, min.instance.instanceId, null); + await sec.updateHumanAgent(manualUser.userSystemId, min.instance.instanceId, null); + + user.systemUser = await sec.getUserFromSystemId(userSystemId); + await min.userProfile.set(step.context, user); + + } + + else if (user.systemUser.agentMode === 'human') { + const agent = await sec.getUserFromSystemId(user.systemUser.agentSystemId); + + await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, + Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); + + + if (user.systemUser.agentSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. + await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); + } + else { + await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.agentSystemId, + Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); + } + + await sec.updateHumanAgent(user.systemUser.userSystemId, min.instance.instanceId, null); + await sec.updateHumanAgent(agent.userSystemId, min.instance.instanceId, null); + + user.systemUser = await sec.getUserFromSystemId(userSystemId); + await min.userProfile.set(step.context, user); + + } + else { + if (user.systemUser.userSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. + await min.conversationalService.sendText(min, step, 'Nenhum atendimento em andamento.'); + } + else { + await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, + 'Nenhum atendimento em andamento.', locale, step.context.activity.conversation.id); + } + } + + return await step.next(); + } + ]) + ); + + min.dialogs.add( + new WaterfallDialog('/feedbackNumber', [ + async step => { + if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { + return await step.beginDialog('/auth'); + } + else { + return await step.next(step.options); + } + }, + async step => { + const locale = step.context.activity.locale; + + return await step.next(); + }, + async step => { + const locale = step.context.activity.locale; + const rate = step.result.entity; + const user = await min.userProfile.get(step.context, {}); + await service.updateConversationRate(user.conversation, rate); + await min.conversationalService.sendText(min, step, Messages[locale].thanks); + + return await step.next(); + } + ]) + ); + + min.dialogs.add( + new WaterfallDialog('/feedback', [ + async step => { + if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { + return await step.beginDialog('/auth'); + } + else { + return await step.next(step.options); + } + }, + async step => { + const locale = step.context.activity.locale; + + await min.conversationalService.sendText(min, step, Messages[locale].about_suggestions); + step.activeDialog.state.cbId = (step.options as any).id; + + return await min.conversationalService.prompt(min, step, Messages[locale].what_about_service); + }, + async step => { + const fixedLocale = 'en-US'; + const user = await min.userProfile.get(step.context, {}); + + // Updates values to perform Bot Analytics. + + const analytics = new AnalyticsService(); + const rate = await analytics.updateConversationSuggestion( + min.instance.instanceId, user.conversation.conversationId, step.result, user.systemUser.locale); + + if (rate > 0.5) { + await min.conversationalService.sendText(min, step, Messages[fixedLocale].glad_you_liked); + } else { + + const message = min.core.getParam(min.instance, 'Feedback Improve Message', + Messages[fixedLocale].we_will_improve); // TODO: Improve to be multi-language. + + await min.conversationalService.sendText(min, step, message); + } + + return await step.replaceDialog('/ask', { isReturning: true }); + } + ]) + ); + } +} diff --git a/packages/customer-satisfaction.gbapp/strings.ts b/packages/customer-satisfaction.gbapp/strings.ts index 8227e239..63a54aab 100644 --- a/packages/customer-satisfaction.gbapp/strings.ts +++ b/packages/customer-satisfaction.gbapp/strings.ts @@ -12,7 +12,8 @@ export const Messages = { please_no_bad_words: 'Please, no bad words.', please_wait_transfering: 'Please, wait while I find an agent to answer you.', notify_agent: (name) => `New call available for *${name}*, you can answer right here when you are finished, type /qt.`, - notify_end_transfer: (botName) => `Now talking to ${botName} again.`, + notify_end_transfer: (botName) => `All messages will be now routed to user ${botName}.`, + notify_agent_transfer_done: (person) => `Now talking directly to ${person}.`, check_whatsapp_ok: 'If you are seeing this message, WhatsApp API is OK.', }, 'pt-BR': { @@ -28,6 +29,7 @@ export const Messages = { please_wait_transfering: 'Por favor, aguarde enquanto eu localizo alguém para te atender.', notify_agent: (name) => `Existe um novo atendimento para *${name}*, por favor, responda aqui mesmo para a pessoa. Para finalizar, digite /qt.`, notify_end_transfer: (botName) => `Falando novamente com o bot ${botName}.`, + notify_agent_transfer_done: (person) => `Todas as mensagens agora sendo transmitidas para ${person}.`, check_whatsapp_ok: 'Se você está recebendo esta mensagem, significa que a API do WhatsApp está OK.', } }; diff --git a/packages/security.gbapp/models/index.ts b/packages/security.gbapp/models/index.ts index f82b7b1f..1083564c 100644 --- a/packages/security.gbapp/models/index.ts +++ b/packages/security.gbapp/models/index.ts @@ -89,7 +89,7 @@ export class GuaribasUser extends Model { @Column(DataType.DATE) public agentContacted: Date; - + @Column(DataType.STRING(16)) public agentMode: string; diff --git a/packages/security.gbapp/services/SecService.ts b/packages/security.gbapp/services/SecService.ts index 1dc47568..9b0c65d4 100644 --- a/packages/security.gbapp/services/SecService.ts +++ b/packages/security.gbapp/services/SecService.ts @@ -5,7 +5,7 @@ import { ConversationReference } from 'botbuilder'; import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib'; import { CollectionUtil } from 'pragmatismo-io-framework'; import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models'; -import {FindOptions} from 'sequelize'; +import { FindOptions } from 'sequelize'; /** * Security service layer. @@ -45,7 +45,7 @@ export class SecService extends GBService { * Retrives a conversation reference from contact phone. */ public async getConversationReference(phone: string): Promise { - const options = { rejectOnEmpty:true, where: { phone: phone } }; + const options = { rejectOnEmpty: true, where: { phone: phone } }; const user = await GuaribasUser.findOne(options); return JSON.parse(user.conversationReference); @@ -118,7 +118,7 @@ export class SecService extends GBService { } }); - if (agentSystemId === null && user.agentSystemId !== undefined ) { + if (agentSystemId === null && user.agentSystemId !== undefined) { const agent = await GuaribasUser.findOne({ where: { userSystemId: user.agentSystemId @@ -167,33 +167,34 @@ export class SecService extends GBService { return user.agentMode === 'self'; } - public async assignHumanAgent(min: GBMinInstance, userSystemId: string): Promise { - let agentSystemId; - - let list = min.core.getParam( - min.instance, - 'Transfer To', - process.env.TRANSFER_TO - ); + public async assignHumanAgent(min: GBMinInstance, userSystemId: string, agentSystemId: string = null): Promise { - if (list){ - list = list.split(';') - } + if (!agentSystemId) { + let list = min.core.getParam( + min.instance, + 'Transfer To', + process.env.TRANSFER_TO + ); - await CollectionUtil.asyncForEach(list, async item => { - if ( - item !== undefined && + if (list) { + list = list.split(';') + } + + await CollectionUtil.asyncForEach(list, async item => { + if ( + item !== undefined && agentSystemId === undefined && item !== userSystemId && !await this.isAgentSystemId(item) - ) { - // TODO: Optimize loop. - agentSystemId = item; - } - }); + ) { + // TODO: Optimize loop. + agentSystemId = item; + } + }); + } GBLog.info(`Selected agentId: ${agentSystemId}`); await this.updateHumanAgent(userSystemId, min.instance.instanceId, agentSystemId); GBLog.info(`Updated agentId to: ${agentSystemId}`); - + return agentSystemId; } diff --git a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts index a066d66d..74b28584 100644 --- a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts +++ b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts @@ -41,7 +41,7 @@ import { GBServer } from '../../../src/app'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { SecService } from '../../security.gbapp/services/SecService'; import { Messages } from '../strings'; -import { KBService } from '../../kb.gbapp/services/KBService'; +import { GuaribasUser } from '../../security.gbapp/models'; /** * Support for Whatsapp. @@ -166,6 +166,7 @@ export class WhatsappDirectLine extends GBService { const message = req.body.messages[0]; let group = ""; + const to = req.body.to; let answerText = null; @@ -345,17 +346,7 @@ export class WhatsappDirectLine extends GBService { await this.sendToDeviceEx(user.userSystemId, `Você já está sendo atendido por ${agent.userSystemId}.`, locale, null); } else if (text === '/qt' || text === 'Sair' || text === 'Fechar') { // TODO: Transfers only in pt-br for now. - await this.sendToDeviceEx(id, - Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); - - if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams. - await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId)); - } - else { - await this.sendToDeviceEx(user.agentSystemId, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); - } - - await sec.updateHumanAgent(id, this.min.instance.instanceId, null); + await this.endTransfer(id, locale, user, agent, sec); } else { GBLog.info(`USER (${id}) TO AGENT ${agent.userSystemId}: ${text}`); @@ -395,6 +386,20 @@ export class WhatsappDirectLine extends GBService { } + private async endTransfer(id: any, locale: string, user: GuaribasUser, agent: GuaribasUser, sec: SecService) { + await this.sendToDeviceEx(id, + Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); + + if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams. + await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId)); + } + else { + await this.sendToDeviceEx(user.agentSystemId, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); + } + + await sec.updateHumanAgent(id, this.min.instance.instanceId, null); + } + public inputMessage(client, conversationId, text, from, fromName, group) { return client.Conversations.Conversations_PostActivity({ conversationId: conversationId,