Vm3 alpha debugger #295

Merged
rodrigorodriguez merged 29 commits from vm3-alpha-debugger into master 2022-11-29 22:05:10 +00:00
91 changed files with 83185 additions and 29818 deletions

1
.gitignore vendored
View file

@ -22,3 +22,4 @@
GB.log GB.log
gb.log gb.log
GB.log.json GB.log.json
yarn-error.log

View file

@ -6,7 +6,7 @@
"request": "launch", "request": "launch",
"sourceMaps": true, "sourceMaps": true,
"name": "Debug Program", "name": "Debug Program",
"program": "${workspaceRoot}/boot.js", "program": "${workspaceRoot}/boot.mjs",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"

View file

@ -136,3 +136,8 @@ CREATE TABLE [dbo].[GuaribasSchedule]
[updatedAt] [datetimeoffset](7) NULL [updatedAt] [datetimeoffset](7) NULL
GO GO
# 3.0.0
ALTER TABLE dbo.GuaribasInstance ADD botKey nvarchar(64) NULL;

77
WARNINGS.md Normal file
View file

@ -0,0 +1,77 @@
# default.gbui
https://github.com/microsoft/BotFramework-WebChat/pull/4524
warning botframework-directlinejs > core-js@3.15.2: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-component > @emotion/css > @emotion/babel-plugin > @babel/plugin-syntax-jsx@7.18.6" has unmet peer dependency "@babel/core@^7.0.0-0".
warning botframework-webchat > botframework-webchat-component > @emotion/css > @emotion/babel-plugin@11.10.5" has unmet peer dependency "@babel/core@^7.0.0".
warning botframework-webchat > botframework-webchat-component > react-film > core-js@3.12.1: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-component > react-scroll-to-bottom > core-js@3.18.3: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-core > redux-devtools-extension@2.13.9: Package moved to @redux-devtools/extension.
warning botframework-webchat > microsoft-cognitiveservices-speech-sdk > asn1.js-rfc2560@5.0.1" has unmet peer dependency "asn1.js@^5.0.0".
warning botframework-webchat > web-speech-cognitive-services@7.1.2" has incorrect peer dependency "microsoft-cognitiveservices-speech-sdk@~1.17.0".
https://github.com/microsoft/powerbi-client-react
warning react-powerbi@0.9.1" has incorrect peer dependency "react@^16.8.0".
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo > stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x.
warning react-scripts > css-minimizer-webpack-plugin > cssnano > cssnano-preset-default > postcss-svgo > svgo > stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
warning react-scripts > eslint-config-react-app > @typescript-eslint/eslint-plugin > tsutils@3.21.0" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta".
warning react-scripts > eslint-config-react-app > eslint-plugin-flowtype@8.0.3" has unmet peer dependency "@babel/plugin-syntax-flow@^7.14.5".
warning react-scripts > eslint-config-react-app > eslint-plugin-flowtype@8.0.3" has unmet peer dependency "@babel/plugin-transform-react-jsx@^7.14.9".
warning react-scripts > jest > @jest/core > jest-config > jest-environment-jsdom > jsdom > w3c-hr-time@1.0.2: Use your platform's native performance.now() and performance.timeOrigin.
warning react-scripts > react-dev-utils > fork-ts-checker-webpack-plugin@6.5.2" has unmet peer dependency "typescript@>= 2.7".
# BotServer
nodejs/node-gyp#2756
warning npm > node-gyp > make-fetch-happen > cacache > @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
vasyas/typescript-rest-rpc#20
warning typescript-rest-rpc > ts-morph > globby > fast-glob > micromatch > snapdragon > source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
#279
warning tslint@6.1.3: TSLint has been deprecated in favor of ESLint. Please see palantir/tslint#4534 for more information.
AlaSQL/alasql#1541
warning alasql > request@2.88.2: request has been deprecated, see request/request#3142
#281
warning c3-chart-maker > data-forge > promised-mongo > mongodb-core > bson@0.4.23: Fixed a critical issue with BSON serialization documented in CVE-2019-2391, see https://bit.ly/2KcpXdo for more details
#280
warning swagger-client > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
bahmutov/ggit#157
warning ban-sensitive-files > ggit > debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (debug-js/debug#797)
#283
warning nexmo > uuid@2.0.3: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
https://github.com/microsoft/botbuilder-js/issues/4370
warning botbuilder-ai > @azure/cognitiveservices-luis-runtime > @azure/ms-rest-js > uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
https://github.com/Azure/azure-sdk-for-node/issues/5221
warning ms-rest-azure > request@2.88.2: request has been deprecated, see request/request#3142
https://github.com/MontassarLaribi/ssr-for-bots/issues/1
warning ssr-for-bots > tslint@6.1.3: TSLint has been deprecated in favor of ESLint. Please see palantir/tslint#4534 for more information.
https://github.com/vasyas/typescript-rest-rpc/issues/20
warning typescript-rest-rpc > ts-morph > globby > fast-glob > micromatch > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
https://github.com/ash-developer/winston-logs-display/issues/8
warning winston-logs-display > jade > transformers@2.1.0: Deprecated, use jstransformer
https://github.com/softwarescales/git-issues/issues/29
warning git-issues > request@2.88.2: request has been deprecated, see request/request#3142
https://github.com/GeneralBots/BotServer/issues/284
warning license-checker > read-installed > readdir-scoped-modules@1.1.0: This functionality has been moved to @npmcli/fs
https://github.com/semantic-release/semantic-release/issues/1260
warning semantic-release > @semantic-release/npm > npm > readdir-scoped-modules@1.1.0: This functionality has been moved to @npmcli/fs
https://github.com/GeneralBots/BotServer/issues/277
warning travis-deploy-once@3.3.0: We recommend to use Travis Build Stages instead

View file

@ -1,9 +1,10 @@
#!/usr/bin/env node #!/usr/bin/env node
const Fs = require('fs'); import Fs from 'fs';
const Path = require('path'); import Path from 'path';
const { exec } = require('child_process'); import { exec } from 'child_process';
var pjson = require('./package.json'); import pjson from './package.json' assert { type: "json" };
import * as GBServer from "./dist/src/app.js";
// Displays version of Node JS being used at runtime and others attributes. // Displays version of Node JS being used at runtime and others attributes.
@ -19,15 +20,14 @@ console.log(`[GB Runtime] debugPort = ${process.debugPort}`);
var now = () => { var now = () => {
return (new Date()).toISOString().replace(/T/, ' ').replace(/\..+/, '') + ' UTC'; return (new Date()).toISOString().replace(/T/, ' ').replace(/\..+/, '') + ' UTC';
} }
var __dirname = process.env.PWD;
try { try {
var run = () => { var run = () => {
console.log(`[GB Runtime] Initializing General Bots (BotServer)...`); console.log(`[GB Runtime] Initializing General Bots (BotServer)...`);
const GBServer = require("./dist/src/app").GBServer console.log(`[GB Runtime] ${now()} - Running on '${import.meta.url}'`);
console.log(`[GB Runtime] ${now()} - Running '${GBServer.name}' on '${__dirname}' directory`); GBServer.GBServer.run();
process.env.PWD = __dirname;
GBServer.run();
} }
var processDist = () => { var processDist = () => {
if (!Fs.existsSync('dist')) { if (!Fs.existsSync('dist')) {

View file

@ -12,4 +12,4 @@ ECHO Compiling...
CALL node_modules\.bin\tsc CALL node_modules\.bin\tsc
:ALLSET :ALLSET
node boot.js node boot.cjs

53651
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,9 @@
{ {
"name": "botserver", "name": "botserver",
"version": "2.0.179", "version": "3.0.0",
"type": "module",
"description": "General Bot Community Edition open-core server.", "description": "General Bot Community Edition open-core server.",
"main": "./boot.js", "main": "./boot.mjs",
"bugs": "https://github.com/pragmatismo-io/BotServer/issues", "bugs": "https://github.com/pragmatismo-io/BotServer/issues",
"homepage": "https://github.com/pragmatismo-io/BotServer/#readme", "homepage": "https://github.com/pragmatismo-io/BotServer/#readme",
"contributors": [ "contributors": [
@ -13,13 +14,13 @@
"Dário Vieira <dario.junior3@gmail.com>" "Dário Vieira <dario.junior3@gmail.com>"
], ],
"engines": { "engines": {
"node": "=14.19.3" "node": "=19.1.0"
}, },
"license": "AGPL-3.0", "license": "AGPL-3.0",
"preferGlobal": true, "preferGlobal": true,
"private": false, "private": false,
"bin": { "bin": {
"gbot": "./boot.js" "gbot": "./boot.cjs"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -33,7 +34,7 @@
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build", "build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
"build-docs": "typedoc --options typedoc.json src/", "build-docs": "typedoc --options typedoc.json src/",
"test": "node test.js", "test": "node test.js",
"start": "node ./boot.js", "start": "node ./boot.cjs",
"reverse-proxy": "node_modules/.bin/ngrok http 4242", "reverse-proxy": "node_modules/.bin/ngrok http 4242",
"watch:build": "tsc --watch", "watch:build": "tsc --watch",
"posttypedoc": "shx cp .nojekyll docs/reference/.nojekyll", "posttypedoc": "shx cp .nojekyll docs/reference/.nojekyll",
@ -49,131 +50,129 @@
"commit": "git-cz" "commit": "git-cz"
}, },
"dependencies": { "dependencies": {
"@azure/cognitiveservices-computervision": "8.1.0", "@azure/cognitiveservices-computervision": "8.2.0",
"@azure/ms-rest-js": "2.5.1", "@azure/keyvault-keys": "4.6.0",
"@google-cloud/pubsub": "2.13.0", "@azure/ms-rest-js": "2.6.2",
"@google-cloud/translate": "6.2.6", "@azure/msal-node": "1.14.3",
"@hubspot/api-client": "5.0.0", "@azure/search-documents": "^11.3.1",
"@microsoft/microsoft-graph-client": "2.2.1", "@google-cloud/pubsub": "3.2.1",
"@semantic-release/changelog": "5.0.1", "@google-cloud/translate": "7.0.4",
"@semantic-release/exec": "5.0.0", "@hubspot/api-client": "7.1.2",
"@semantic-release/git": "9.0.0", "@microsoft/microsoft-graph-client": "3.0.4",
"@sendgrid/mail": "7.4.4", "@nosferatu500/textract": "3.1.2",
"@types/node": "8.0.0", "@semantic-release/changelog": "6.0.1",
"@types/validator": "13.1.4", "@semantic-release/exec": "6.0.3",
"adal-node": "0.2.2", "@semantic-release/git": "10.0.1",
"adm-zip": "0.5.6", "@sendgrid/mail": "7.7.0",
"alasql": "^1.7.3", "@types/node": "18.11.9",
"@types/validator": "13.7.10",
"adm-zip": "0.5.9",
"alasql": "2.1.6",
"any-shell-escape": "0.1.1", "any-shell-escape": "0.1.1",
"async-promises": "0.2.3", "async-promises": "0.2.3",
"azure-arm-cognitiveservices": "3.0.0",
"azure-arm-resource": "7.4.0",
"azure-arm-search": "1.3.0-preview",
"azure-arm-sql": "5.7.0",
"azure-arm-website": "5.7.0",
"azure-search-client": "3.1.5",
"basic-auth": "2.0.1", "basic-auth": "2.0.1",
"billboard.js": "^3.4.1", "billboard.js": "3.6.3",
"bluebird": "3.7.2", "bluebird": "3.7.2",
"body-parser": "1.19.0", "body-parser": "1.20.1",
"botbuilder": "4.11.0", "botbuilder": "4.18.0",
"botbuilder-adapter-facebook": "1.0.11", "botbuilder-adapter-facebook": "1.0.12",
"botbuilder-ai": "4.11.0", "botbuilder-ai": "4.18.0",
"botbuilder-dialogs": "4.11.0", "botbuilder-dialogs": "4.18.0",
"botframework-connector": "4.11.0", "botframework-connector": "4.18.0",
"botlib": "1.10.9", "botlib": "3.0.0",
"c3-chart-maker": "^0.2.8", "c3-chart-maker": "0.2.8",
"cli-progress": "^3.11.2", "chrome-remote-interface": "0.31.3",
"cli-progress": "3.11.2",
"cli-spinner": "0.2.10", "cli-spinner": "0.2.10",
"core-js": "3.14.0", "core-js": "3.26.1",
"data-forge": "^1.9.5", "data-forge": "1.9.6",
"date-diff": "0.2.2", "date-diff": "1.0.2",
"docxtemplater": "^3.31.1", "docxtemplater": "3.32.4",
"dotenv-extended": "2.9.0", "dotenv-extended": "2.9.0",
"exceljs": "4.2.1", "exceljs": "4.3.0",
"express": "4.17.1", "express": "4.18.2",
"express-remove-route": "1.0.0", "express-remove-route": "1.0.0",
"ffmpeg-static": "4.3.0", "ffmpeg-static": "5.1.0",
"google-libphonenumber": "3.2.21", "google-libphonenumber": "3.2.31",
"googleapis": "75.0.0", "googleapis": "109.0.1",
"ibm-watson": "6.1.1", "ibm-watson": "7.1.2",
"js-beautify": "1.13.13", "indent.js": "0.3.5",
"keyv": "^4.5.0", "js-beautify": "1.14.7",
"lodash": "^4.17.21", "keyv": "4.5.2",
"luxon": "2.0.2", "koa": "2.13.4",
"mammoth": "^1.4.21", "koa-body": "6.0.1",
"marked": "2.0.7", "koa-router": "12.0.0",
"momentjs": "2.0.0", "lodash": "4.17.21",
"ms-rest-azure": "3.0.0", "luxon": "3.1.0",
"mammoth": "1.5.1",
"marked": "4.2.2",
"moment": "1.3.0",
"ms-rest-azure": "3.0.2",
"nexmo": "2.9.1", "nexmo": "2.9.1",
"node-cron": "3.0.0", "node-cron": "3.0.2",
"node-nlp": "^3.10.2", "node-nlp": "^4.24.0",
"node-tesseract-ocr": "^2.2.1", "node-tesseract-ocr": "2.2.1",
"npm": "7.21.0", "npm": "9.1.2",
"opn": "6.0.0", "open": "8.4.0",
"pdf-extraction": "1.0.2", "pdf-extraction": "1.0.2",
"pdfkit": "^0.13.0", "pdfkit": "0.13.0",
"phone": "2.4.21", "phone": "3.1.30",
"pizzip": "^3.0.6", "pizzip": "3.1.3",
"pptxtemplater": "1.0.5", "pptxtemplater": "1.0.5",
"pragmatismo-io-framework": "1.0.20", "pragmatismo-io-framework": "1.0.20",
"prism-media": "1.3.1", "prism-media": "1.3.4",
"public-ip": "4.0.4", "public-ip": "6.0.1",
"puppeteer": "13.7.0", "punycode": "2.1.1",
"puppeteer-extra": "^3.3.4", "puppeteer": "19.2.2",
"puppeteer-extra-plugin-stealth": "2.4.5", "puppeteer-extra": "3.3.4",
"qrcode": "^1.5.0", "puppeteer-extra-plugin-stealth": "2.11.1",
"qrcode": "1.5.1",
"qrcode-terminal": "0.12.0", "qrcode-terminal": "0.12.0",
"readline": "1.3.0", "readline": "1.3.0",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"request-promise": "4.2.5",
"request-promise-native": "1.0.8",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"safe-buffer": "5.2.1", "safe-buffer": "5.2.1",
"scanf": "1.1.2", "scanf": "1.1.2",
"sequelize": "6.5.0", "sequelize": "6.25.7",
"sequelize-cli": "6.2.0", "sequelize-cli": "6.5.2",
"sequelize-typescript": "2.1.0", "sequelize-typescript": "2.1.5",
"simple-git": "2.39.1", "simple-git": "3.15.0",
"speakingurl": "14.0.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", "strict-password-generator": "1.1.2",
"swagger-client": "2.1.18", "swagger-client": "^3.18.5",
"tabulator-tables": "^5.2.6", "tabulator-tables": "5.4.2",
"tedious": "14.0.0", "tedious": "15.1.2",
"textract": "2.5.0", "twitter-api-v2": "1.12.9",
"twitter-api-v2": "1.12.7", "typescript": "4.9.3",
"typescript": "3.6.4", "typescript-rest-rpc": "^1.0.7",
"url-join": "4.0.1", "url-join": "5.0.0",
"vbscript-to-typescript": "1.0.8", "vbscript-to-typescript": "1.0.8",
"vhost": "^3.0.2", "vhost": "3.0.2",
"vm2": "^3.9.11", "vm2": "3.9.11",
"vm2-process": "2.1.1",
"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": "1.18.3",
"winston": "^2.4.2", "winston": "3.8.2",
"winston-logs-display": "^1.0.0", "winston-logs-display": "^1.0.0",
"yarn": "^1.22.19" "yarn": "^1.22.19"
}, },
"devDependencies": { "devDependencies": {
"@types/puppeteer": "5.4.6", "@types/url-join": "4.0.1",
"@types/url-join": "4.0.0", "ban-sensitive-files": "1.9.18",
"@types/winston": "2.4.4", "commitizen": "4.2.5",
"ban-sensitive-files": "1.9.15",
"commitizen": "4.2.4",
"cz-conventional-changelog": "3.3.0", "cz-conventional-changelog": "3.3.0",
"dependency-check": "4.1.0", "dependency-check": "4.1.0",
"git-issues": "1.3.1", "git-issues": "^1.0.0",
"license-checker": "25.0.1", "license-checker": "25.0.1",
"ngrok": "4.0.1", "ngrok": "4.3.3",
"nsp": "3.2.1", "prettier-standard": "^15.0.1",
"prettier-standard": "16.4.1", "semantic-release": "19.0.5",
"semantic-release": "17.4.3", "simple-commit-message": "^1.1.0",
"simple-commit-message": "4.1.2", "travis-deploy-once": "^3.0.0",
"travis-deploy-once": "5.0.11", "ts-node": "10.9.1",
"ts-node": "10.0.0", "tslint": "6.1.3"
"tslint": "6.1.2"
}, },
"eslintConfig": { "eslintConfig": {
"env": { "env": {

View file

@ -36,14 +36,14 @@
'use strict'; 'use strict';
const crypto = require('crypto'); import crypto from 'crypto';
import urlJoin from 'url-join';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog, GBLog, IGBPackage } from 'botlib'; import { GBMinInstance, IGBDialog, GBLog, IGBPackage } from 'botlib';
const urlJoin = require('url-join'); import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService'; import { Messages } from '../strings.js';
import { Messages } from '../strings'; import { GBAdminService } from '../services/GBAdminService.js';
import { GBAdminService } from '../services/GBAdminService';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
/** /**
@ -77,8 +77,7 @@ export class AdminDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -109,8 +108,7 @@ export class AdminDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -200,8 +198,7 @@ export class AdminDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -237,17 +234,15 @@ export class AdminDialog extends IGBDialog {
min.dialogs.add( min.dialogs.add(
new WaterfallDialog('/publish', [ new WaterfallDialog('/publish', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
async step => { async step => {
if (step.activeDialog.state.options.confirm || process.env.ADMIN_OPEN_PUBLISH === "true") { if (step.activeDialog.state.options.confirm || process.env.ADMIN_OPEN_PUBLISH === 'true') {
return await step.next('sim'); return await step.next('sim');
} else { } else {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
@ -266,7 +261,7 @@ export class AdminDialog extends IGBDialog {
if (step.activeDialog.state.options.firstTime) { if (step.activeDialog.state.options.firstTime) {
canPublish = true; canPublish = true;
} else { } else {
canPublish = AdminDialog.canPublish(min, from) || process.env.ADMIN_OPEN_PUBLISH === "true"; canPublish = AdminDialog.canPublish(min, from) || process.env.ADMIN_OPEN_PUBLISH === 'true';
} }
if (!canPublish) { if (!canPublish) {
@ -313,11 +308,14 @@ export class AdminDialog extends IGBDialog {
try { try {
let cmd1; let cmd1;
if (packageName.indexOf('.') !== -1) { if (packageName.indexOf('.') !== -1) {
cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${process.env.STORAGE_LIBRARY}/${botId}.gbai/${packageName}`; cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${
process.env.STORAGE_LIBRARY
}/${botId}.gbai/${packageName}`;
} else { } else {
cmd1 = `deployPackage ${packageName}`; cmd1 = `deployPackage ${packageName}`;
} }
if ((await (deployer as any).getStoragePackageByName(min.instance.instanceId, packageName)) !== null && if (
(await (deployer as any).getStoragePackageByName(min.instance.instanceId, packageName)) !== null &&
!process.env.DONT_DOWNLOAD !process.env.DONT_DOWNLOAD
) { ) {
const cmd2 = `undeployPackage ${packageName}`; const cmd2 = `undeployPackage ${packageName}`;
@ -354,8 +352,7 @@ export class AdminDialog extends IGBDialog {
if (process.env.SECURITY_CAN_PUBLISH !== undefined) { if (process.env.SECURITY_CAN_PUBLISH !== undefined) {
let list = process.env.SECURITY_CAN_PUBLISH.split(';'); let list = process.env.SECURITY_CAN_PUBLISH.split(';');
const canPublish = const canPublish = min.core.getParam(min.instance, 'Can Publish', null);
min.core.getParam(min.instance, 'Can Publish', null);
if (canPublish) { if (canPublish) {
list = list.concat(canPublish.split(';')); list = list.concat(canPublish.split(';'));
} }
@ -376,8 +373,7 @@ export class AdminDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -398,10 +394,8 @@ export class AdminDialog extends IGBDialog {
async step => { async step => {
step.activeDialog.state.authenticatorAuthorityHostUrl = step.result; step.activeDialog.state.authenticatorAuthorityHostUrl = step.result;
min.instance.authenticatorTenant = min.instance.authenticatorTenant = step.activeDialog.state.authenticatorTenant;
step.activeDialog.state.authenticatorTenant; min.instance.authenticatorAuthorityHostUrl = step.activeDialog.state.authenticatorAuthorityHostUrl;
min.instance.authenticatorAuthorityHostUrl =
step.activeDialog.state.authenticatorAuthorityHostUrl;
await min.adminService.updateSecurityInfo( await min.adminService.updateSecurityInfo(
min.instance.instanceId, min.instance.instanceId,
@ -415,15 +409,12 @@ export class AdminDialog extends IGBDialog {
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state); min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
const redirectUri = urlJoin( const redirectUri = urlJoin(min.instance.botEndpoint, min.instance.botId, '/token');
min.instance.botEndpoint, const url = `https://login.microsoftonline.com/${
min.instance.botId, step.activeDialog.state.authenticatorTenant
'/token' }/oauth2/authorize?client_id=${
); min.instance.marketplaceId
const url = `https://login.microsoftonline.com/${step.activeDialog.state.authenticatorTenant }&response_type=code&redirect_uri=${redirectUri}&scope=https://graph.microsoft.com/.default&state=${state}&response_mode=query`;
}/oauth2/authorize?client_id=${min.instance.marketplaceId
}&response_type=code&redirect_uri=${redirectUri
}&scope=https://graph.microsoft.com/.default&state=${state}&response_mode=query`;
await min.conversationalService.sendText(min, step, Messages[locale].consent(url)); await min.conversationalService.sendText(min, step, Messages[locale].consent(url));

View file

@ -38,8 +38,8 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { AdminDialog } from './dialogs/AdminDialog'; import { AdminDialog } from './dialogs/AdminDialog.js';
import { GuaribasAdmin } from './models/AdminModel'; import { GuaribasAdmin } from './models/AdminModel.js';
/** /**
* The package for admin.gbapp. * The package for admin.gbapp.
@ -70,5 +70,4 @@ export class GBAdminPackage implements IGBPackage {
public async loadBot (min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {
AdminDialog.setup(min); AdminDialog.setup(min);
} }
} }

View file

@ -36,36 +36,27 @@
'use strict'; 'use strict';
import { import { Column, CreatedAt, DataType, Model, Table, UpdatedAt } from 'sequelize-typescript';
Column,
CreatedAt,
DataType,
Model,
Table,
UpdatedAt
} from 'sequelize-typescript';
/** /**
* General settings store. * General settings store.
*/ */
@Table @Table
export class GuaribasAdmin extends Model<GuaribasAdmin> { export class GuaribasAdmin extends Model<GuaribasAdmin> {
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public key: string; declare key: string;
@Column(DataType.STRING(4000)) @Column(DataType.STRING(4000))
public value: string; declare value: string;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
} }

View file

@ -39,18 +39,18 @@
import { AuthenticationContext, TokenResponse } from 'adal-node'; import { AuthenticationContext, TokenResponse } from 'adal-node';
import { GBLog, GBMinInstance, IGBAdminService, IGBCoreService, IGBDeployer, IGBInstance } from 'botlib'; import { GBLog, GBMinInstance, IGBAdminService, IGBCoreService, IGBDeployer, IGBInstance } from 'botlib';
import { FindOptions } from 'sequelize/types'; import { FindOptions } from 'sequelize/types';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService'; import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService'; import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointService'; import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointService.js';
import { GuaribasAdmin } from '../models/AdminModel'; import { GuaribasAdmin } from '../models/AdminModel.js';
const Path = require('path'); import msRestAzure from 'ms-rest-azure';
const msRestAzure = require('ms-rest-azure'); import Path from 'path';
const PasswordGenerator = require('strict-password-generator').default; import PasswordGenerator from 'strict-password-generator';
const crypto = require("crypto"); import crypto from 'crypto';
/** /**
* Services for server administration. * Services for server administration.
@ -68,21 +68,23 @@ export class GBAdminService implements IGBAdminService {
} }
public static generateUuid (): string { public static generateUuid (): string {
return msRestAzure.generateUuid(); return crypto.randomUUID();
} }
public static getNodeVersion () { public static getNodeVersion () {
return '19.1.0';
const packageJson = urlJoin(process.cwd(), 'package.json'); const packageJson = urlJoin(process.cwd(), 'package.json');
// tslint:disable-next-line: non-literal-require // tslint:disable-next-line: non-literal-require
const pjson = require(packageJson); // TODO
// const pjson = require(packageJson);
return pjson.engines.node.replace('=', ''); // return pjson.engines.node.replace('=', '');
} }
public static async getADALTokenFromUsername (username: string, password: string) { public static async getADALTokenFromUsername (username: string, password: string) {
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password); const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
return credentials.tokenCache._entries[0].accessToken; return (credentials as any).tokenCache._entries[0].accessToken;
} }
public static async getADALCredentialsFromUsername (username: string, password: string) { public static async getADALCredentialsFromUsername (username: string, password: string) {
@ -151,7 +153,8 @@ export class GBAdminService implements IGBAdminService {
* @see https://stackoverflow.com/a/52171480 * @see https://stackoverflow.com/a/52171480
*/ */
public static getHash (str, seed = 0) { public static getHash (str, seed = 0) {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) { for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i); ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761); h1 = Math.imul(h1 ^ ch, 2654435761);
@ -193,9 +196,7 @@ export class GBAdminService implements IGBAdminService {
// .gbot packages are handled using storage API, so no download // .gbot packages are handled using storage API, so no download
// of local resources is required. // of local resources is required.
await deployer['downloadFolder'](min, await deployer['downloadFolder'](min, Path.join('work', `${min.instance.botId}.gbai`), Path.basename(folderName));
Path.join('work', `${min.instance.botId}.gbai`),
Path.basename(folderName));
await deployer.deployPackage(min, localFolder); await deployer.deployPackage(min, localFolder);
} }
} }

View file

@ -3,7 +3,7 @@ export const Messages = {
authenticate: 'Please, authenticate:', authenticate: 'Please, authenticate:',
welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.', welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.',
which_task: 'Which task do you wanna run now?', which_task: 'Which task do you wanna run now?',
working: (command) => `I'm working on ${command}...`, working: command => `I'm working on ${command}...`,
finished_working: 'Done.', finished_working: 'Done.',
unknown_command: text => unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`, `Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
@ -12,7 +12,7 @@ export const Messages = {
deployPackage: text => `Deploying package ${text}...`, deployPackage: text => `Deploying package ${text}...`,
redeployPackage: text => `Redeploying package ${text}...`, redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `√ Package ${text} undeployed...`, packageUndeployed: text => `√ Package ${text} undeployed...`,
consent: (url) => `Please, consent access to this app at: [Microsoft Online](${url}).`, consent: url => `Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: 'Sorry, wrong password. Please, try again.', wrong_password: 'Sorry, wrong password. Please, try again.',
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):', enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ', enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
@ -28,7 +28,7 @@ export const Messages = {
authenticate: 'Please, authenticate:', authenticate: 'Please, authenticate:',
welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.', welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.',
which_task: 'Which task do you wanna run now?', which_task: 'Which task do you wanna run now?',
working: (command) => `I'm working on ${command}...`, working: command => `I'm working on ${command}...`,
finished_working: 'Done.', finished_working: 'Done.',
unknown_command: text => unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`, `Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
@ -37,7 +37,7 @@ export const Messages = {
deployPackage: text => `Deploying package ${text}...`, deployPackage: text => `Deploying package ${text}...`,
redeployPackage: text => `Redeploying package ${text}...`, redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `Package ${text} undeployed...`, packageUndeployed: text => `Package ${text} undeployed...`,
consent: (url) => `Please, consent access to this app at: [Microsoft Online](${url}).`, consent: url => `Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: 'Sorry, wrong password. Please, try again.', wrong_password: 'Sorry, wrong password. Please, try again.',
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):', enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ', enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',

View file

@ -38,7 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { GuaribasConversation, GuaribasConversationMessage } from './models'; import { GuaribasConversation, GuaribasConversationMessage } from './models/index.js';
/** /**
* .gblib Package handler. * .gblib Package handler.
@ -51,7 +51,6 @@ export class GBAnalyticsPackage implements IGBPackage {
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> { public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
GBLog.verbose(`loadPackage called.`); GBLog.verbose(`loadPackage called.`);
core.sequelize.addModels([GuaribasConversation, GuaribasConversationMessage]); core.sequelize.addModels([GuaribasConversation, GuaribasConversationMessage]);
} }
public async unloadPackage (core: IGBCoreService): Promise<void> { public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`); GBLog.verbose(`unloadPackage called.`);
@ -68,5 +67,4 @@ export class GBAnalyticsPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -54,58 +54,57 @@ import {
UpdatedAt UpdatedAt
} from 'sequelize-typescript'; } from 'sequelize-typescript';
import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
import { GuaribasSubject } from '../../kb.gbapp/models'; import { GuaribasSubject } from '../../kb.gbapp/models/index.js';
import { GuaribasUser } from '../../security.gbapp/models'; import { GuaribasUser } from '../../security.gbapp/models/index.js';
/** /**
* A conversation that groups many messages. * A conversation that groups many messages.
*/ */
@Table @Table
export class GuaribasConversation extends Model<GuaribasConversation> { export class GuaribasConversation extends Model<GuaribasConversation> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public conversationId: number; conversationId: number;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@ForeignKey(() => GuaribasSubject) @ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public startSubjectId: number; startSubjectId: number;
@BelongsTo(() => GuaribasSubject) @BelongsTo(() => GuaribasSubject)
public startSubject: GuaribasSubject; startSubject: GuaribasSubject;
@ForeignKey(() => GuaribasChannel) @ForeignKey(() => GuaribasChannel)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public channelId: string; channelId: string;
@Column(DataType.DATE) @Column(DataType.DATE)
public rateDate: Date; rateDate: Date;
@Column(DataType.FLOAT) @Column(DataType.FLOAT)
public rate: number; rate: number;
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public feedback: string; feedback: string;
@CreatedAt @CreatedAt
@Column(DataType.DATE) @Column(DataType.DATE)
public createdAt: Date; createdAt: Date;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public text: string; text: string;
@ForeignKey(() => GuaribasUser) @ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public startedByUserId: number; startedByUserId: number;
@BelongsTo(() => GuaribasUser) @BelongsTo(() => GuaribasUser)
public startedBy: GuaribasUser; startedBy: GuaribasUser;
} }
/** /**
@ -113,45 +112,43 @@ export class GuaribasConversation extends Model<GuaribasConversation> {
*/ */
@Table @Table
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> { export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public conversationMessageId: number; conversationMessageId: number;
@ForeignKey(() => GuaribasSubject) @ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public subjectId: number; subjectId: number;
@Column(DataType.TEXT) @Column(DataType.TEXT)
public content: string; content: string;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; updatedAt: Date;
//tslint:disable-next-line:no-use-before-declare //tslint:disable-next-line:no-use-before-declare
@ForeignKey(() => GuaribasConversation) @ForeignKey(() => GuaribasConversation)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public conversationId: number; conversationId: number;
//tslint:disable-next-line:no-use-before-declare //tslint:disable-next-line:no-use-before-declare
@BelongsTo(() => GuaribasConversation) @BelongsTo(() => GuaribasConversation)
public conversation: GuaribasConversation; conversation: GuaribasConversation;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@ForeignKey(() => GuaribasUser) @ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public userId: number; userId: number;
@BelongsTo(() => GuaribasUser) @BelongsTo(() => GuaribasUser)
public user: GuaribasUser; user: GuaribasUser;
} }

View file

@ -36,18 +36,15 @@
import { AzureText } from 'pragmatismo-io-framework'; import { AzureText } from 'pragmatismo-io-framework';
import { FindOptions } from 'sequelize/types'; import { FindOptions } from 'sequelize/types';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GuaribasUser } from '../../security.gbapp/models'; import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { GuaribasConversation, GuaribasConversationMessage } from '../models'; import { GuaribasConversation, GuaribasConversationMessage } from '../models/index.js';
/** /**
* Base services for Bot Analytics. * Base services for Bot Analytics.
*/ */
export class AnalyticsService { export class AnalyticsService {
public async createConversation (user: GuaribasUser): Promise<GuaribasConversation> {
public async createConversation(
user: GuaribasUser
): Promise<GuaribasConversation> {
const conversation = new GuaribasConversation(); const conversation = new GuaribasConversation();
conversation.startedBy = user; conversation.startedBy = user;
conversation.startedByUserId = user.userId; conversation.startedByUserId = user.userId;
@ -56,15 +53,18 @@ export class AnalyticsService {
return await conversation.save(); return await conversation.save();
} }
public async updateConversationSuggestion(instanceId: number, public async updateConversationSuggestion (
conversationId: string, feedback: string, locale: string): Promise<number> { instanceId: number,
conversationId: string,
feedback: string,
locale: string
): Promise<number> {
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
const rate = await AzureText.getSentiment( const rate = await AzureText.getSentiment(
minBoot.instance.textAnalyticsKey ? minBoot.instance.textAnalyticsKey : minBoot.instance.textAnalyticsKey ? minBoot.instance.textAnalyticsKey : minBoot.instance.textAnalyticsKey,
minBoot.instance.textAnalyticsKey, minBoot.instance.textAnalyticsEndpoint
minBoot.instance.textAnalyticsEndpoint ? minBoot.instance.textAnalyticsEndpoint : ? minBoot.instance.textAnalyticsEndpoint
minBoot.instance.textAnalyticsEndpoint, : minBoot.instance.textAnalyticsEndpoint,
locale, locale,
feedback feedback
); );
@ -79,7 +79,6 @@ export class AnalyticsService {
await item.save(); await item.save();
return rate; return rate;
} }
public async createMessage ( public async createMessage (
@ -88,9 +87,8 @@ export class AnalyticsService {
userId: number, userId: number,
content: string content: string
): Promise<GuaribasConversationMessage> { ): Promise<GuaribasConversationMessage> {
const message = GuaribasConversationMessage.build(); const message = GuaribasConversationMessage.build();
message.content = typeof (content) === 'object' ? JSON.stringify(content) : content; message.content = typeof content === 'object' ? JSON.stringify(content) : content;
message.instanceId = instanceId; message.instanceId = instanceId;
message.userId = userId; message.userId = userId;
message.conversationId = conversation.conversationId; message.conversationId = conversation.conversationId;

View file

@ -37,10 +37,10 @@
'use strict'; 'use strict';
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib'; import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
import * as fs from 'fs'; import * as Fs from 'fs';
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService.js';
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService.js';
const scanf = require('scanf'); import scanf from 'scanf';
/** /**
* Handles command-line dialog for getting info for Boot Bot. * Handles command-line dialog for getting info for Boot Bot.
@ -49,7 +49,7 @@ export class StartDialog {
public static async createBaseInstance (installationDeployer: IGBInstallationDeployer) { public static async createBaseInstance (installationDeployer: IGBInstallationDeployer) {
// No .env so asks for cloud credentials to start a new farm. // No .env so asks for cloud credentials to start a new farm.
if (!fs.existsSync(`.env`)) { if (!Fs.existsSync(`.env`)) {
process.stdout.write( process.stdout.write(
'A empty enviroment is detected. To start automatic deploy, please enter some information:\n' 'A empty enviroment is detected. To start automatic deploy, please enter some information:\n'
); );
@ -146,7 +146,6 @@ cannot start or end with or contain consecutive dashes and having 4 to 42 charac
return botId; return botId;
} }
/** /**
* *
* Update Manifest in Azure: "signInAudience": "AzureADandPersonalMicrosoftAccount" and "accessTokenAcceptedVersion": 2. * Update Manifest in Azure: "signInAudience": "AzureADandPersonalMicrosoftAccount" and "accessTokenAcceptedVersion": 2.

View file

@ -65,5 +65,4 @@ export class GBAzureDeployerPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -36,33 +36,27 @@
'use strict'; 'use strict';
import { HttpHeaders, HttpMethods, ServiceClient, WebResource } from '@azure/ms-rest-js'; import urlJoin from 'url-join';
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices'; import { HttpMethods, ServiceClient, WebResource } from '@azure/ms-rest-js';
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource'; import { CognitiveServicesManagementClient } from '@azure/arm-cognitiveservices';
import { SearchManagementClient } from 'azure-arm-search'; import { ResourceManagementClient } from '@azure/arm-resources';
import { SqlManagementClient } from 'azure-arm-sql'; import { SubscriptionClient } from '@azure/arm-subscriptions';
import { WebSiteManagementClient } from 'azure-arm-website'; import { SearchManagementClient } from '@azure/arm-search';
//tslint:disable-next-line:no-submodule-imports import { SqlManagementClient } from '@azure/arm-sql';
import { AppServicePlan, Site, SiteConfigResource, SiteLogsConfig, SiteSourceControl } from 'azure-arm-website/lib/models'; import { WebSiteManagementClient } from '@azure/arm-appservice';
import { AppServicePlan, Site, SiteLogsConfig, SiteSourceControl } from '@azure/arm-appservice';
import { GBLog, IGBInstallationDeployer, IGBInstance, IGBDeployer, IGBCoreService } from 'botlib'; import { GBLog, IGBInstallationDeployer, IGBInstance, IGBDeployer, IGBCoreService } from 'botlib';
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService.js';
import { GBCorePackage } from '../../../packages/core.gbapp'; import { GBCorePackage } from '../../../packages/core.gbapp/index.js';
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService.js';
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer'; import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer.js';
const MicrosoftGraph = require("@microsoft/microsoft-graph-client"); import { Account } from '@azure/arm-cognitiveservices';
import MicrosoftGraph from '@microsoft/microsoft-graph-client';
import Spinner from 'cli-spinner';
import * as publicIp from 'public-ip';
const Spinner = require('cli-spinner').Spinner;
// tslint:disable-next-line: no-submodule-imports
import * as simplegit from 'simple-git/promise';
const git = simplegit();
// tslint:disable-next-line:no-submodule-imports
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
const urlJoin = require('url-join');
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
const publicIp = require('public-ip');
const WebSiteResponseTimeout = 900; const WebSiteResponseTimeout = 900;
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
/** /**
* Deployer for Microsoft cloud. * Deployer for Microsoft cloud.
*/ */
@ -70,13 +64,13 @@ export class AzureDeployerService implements IGBInstallationDeployer {
public apiVersion = '2017-12-01'; public apiVersion = '2017-12-01';
public defaultEndPoint = 'http://localhost:4242'; public defaultEndPoint = 'http://localhost:4242';
public instance: IGBInstance; public instance: IGBInstance;
public cloud: ResourceManagementClient.ResourceManagementClient; public cloud: ResourceManagementClient;
public webSiteClient: WebSiteManagementClient; public webSiteClient: WebSiteManagementClient;
public storageClient: SqlManagementClient; public storageClient: SqlManagementClient;
public cognitiveClient: CognitiveServicesManagementClient; public cognitiveClient: CognitiveServicesManagementClient;
public searchClient: SearchManagementClient; public searchClient: SearchManagementClient;
public provider = 'Microsoft.BotService'; public provider = 'Microsoft.BotService';
public subscriptionClient: SubscriptionClient.SubscriptionClient; public subscriptionClient: SubscriptionClient;
public accessToken: string; public accessToken: string;
public location: string; public location: string;
public subscriptionId: string; public subscriptionId: string;
@ -95,7 +89,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
public static async createInstance (deployer: GBDeployer): Promise<AzureDeployerService> { public static async createInstance (deployer: GBDeployer): Promise<AzureDeployerService> {
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD'); const password = GBConfigService.get('CLOUD_PASSWORD');
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password); const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
@ -121,7 +114,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
public async getSubscriptions (credentials) { public async getSubscriptions (credentials) {
const subscriptionClient = new SubscriptionClient.default(credentials); const subscriptionClient = new SubscriptionClient(credentials);
return subscriptionClient.subscriptions.list(); return subscriptionClient.subscriptions.list();
} }
@ -238,22 +231,19 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
public async botExists (botId) { public async botExists (botId) {
const baseUrl = `https://management.azure.com/`; const baseUrl = `https://management.azure.com/`;
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD'); const password = GBConfigService.get('CLOUD_PASSWORD');
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password); const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
const httpClient = new ServiceClient(); const httpClient = new ServiceClient();
const query = `providers/${this.provider const query = `providers/${this.provider}/checkNameAvailability/Action?api-version=${this.apiVersion}`;
}/checkNameAvailability/Action?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query); const url = urlJoin(baseUrl, query);
const body = { const body = {
name: botId, name: botId,
type: "botServices" type: 'botServices'
}; };
const req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(body)); const req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(body));
@ -277,7 +267,8 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
}; };
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`; }/botServices/${botId}?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query); const url = urlJoin(baseUrl, query);
const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters)); const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
@ -289,8 +280,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
GBLog.info(`Bot proxy updated at: ${endpoint}.`); GBLog.info(`Bot proxy updated at: ${endpoint}.`);
} }
public async updateBot(botId: string, group: string, name: string, public async updateBot (botId: string, group: string, name: string, description: string, endpoint: string) {
description: string, endpoint: string) {
const baseUrl = `https://management.azure.com/`; const baseUrl = `https://management.azure.com/`;
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
const password = GBConfigService.get('CLOUD_PASSWORD'); const password = GBConfigService.get('CLOUD_PASSWORD');
@ -308,7 +298,8 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
}; };
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`; }/botServices/${botId}?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query); const url = urlJoin(baseUrl, query);
const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters)); const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
@ -329,13 +320,14 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password); const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
const httpClient = new ServiceClient(); const httpClient = new ServiceClient();
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`; }/botServices/${botId}?api-version=${this.apiVersion}`;
const url = urlJoin(baseUrl, query); const url = urlJoin(baseUrl, query);
const req = AzureDeployerService.createRequestObject(url, accessToken, 'DELETE', undefined); const req = AzureDeployerService.createRequestObject(url, accessToken, 'DELETE', undefined);
const res = await httpClient.sendRequest(req); const res = await httpClient.sendRequest(req);
if (res.bodyAsText !== "") { if (res.bodyAsText !== '') {
throw res.bodyAsText; throw res.bodyAsText;
} }
GBLog.info(`Bot ${botId} was deleted from the provider.`); GBLog.info(`Bot ${botId} was deleted from the provider.`);
@ -347,15 +339,14 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID'); const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password); const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
const storageClient = new SqlManagementClient(credentials, subscriptionId); const storageClient = new SqlManagementClient(credentials as any, subscriptionId);
const ip = await publicIp.v4(); const ip = await publicIp.publicIpv4();
let params = { let params = {
startIpAddress: ip, startIpAddress: ip,
endIpAddress: ip endIpAddress: ip
}; };
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params); await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
} }
public async deployFarm ( public async deployFarm (
@ -373,11 +364,9 @@ export class AzureDeployerService implements IGBInstallationDeployer {
let keys: any; let keys: any;
const name = instance.botId; const name = instance.botId;
GBLog.info(`Enabling resource providers...`); GBLog.info(`Enabling resource providers...`);
await this.enableResourceProviders('Microsoft.BotService'); await this.enableResourceProviders('Microsoft.BotService');
GBLog.info(`Deploying Deploy Group (It may take a few minutes)...`); GBLog.info(`Deploying Deploy Group (It may take a few minutes)...`);
await this.createDeployGroup(name, instance.cloudLocation); await this.createDeployGroup(name, instance.cloudLocation);
@ -391,8 +380,13 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const administratorPassword = GBAdminService.getRndPassword(); const administratorPassword = GBAdminService.getRndPassword();
const storageServer = `${name.toLowerCase()}-storage-server`; const storageServer = `${name.toLowerCase()}-storage-server`;
const storageName = `${name}-storage`; const storageName = `${name}-storage`;
await this.createStorageServer(name, storageServer, administratorLogin, await this.createStorageServer(
administratorPassword, storageServer, instance.cloudLocation name,
storageServer,
administratorLogin,
administratorPassword,
storageServer,
instance.cloudLocation
); );
await this.createStorage(name, storageServer, storageName, instance.cloudLocation); await this.createStorage(name, storageServer, storageName, instance.cloudLocation);
instance.storageUsername = administratorLogin; instance.storageUsername = administratorLogin;
@ -406,28 +400,28 @@ export class AzureDeployerService implements IGBInstallationDeployer {
await this.createSearch(name, searchName, instance.cloudLocation); await this.createSearch(name, searchName, instance.cloudLocation);
const searchKeys = await this.searchClient.adminKeys.get(name, searchName); const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
instance.searchHost = `${searchName}.search.windows.net`; instance.searchHost = `${searchName}.search.windows.net`;
instance.searchIndex = 'azuresql-index'; instance.searchIndex = 'azuresql-index.js';
instance.searchIndexer = 'azuresql-indexer'; instance.searchIndexer = 'azuresql-indexer';
instance.searchKey = searchKeys.primaryKey; instance.searchKey = searchKeys.primaryKey;
GBLog.info(`Deploying Speech...`); GBLog.info(`Deploying Speech...`);
const speech = await this.createSpeech(name, `${name}speech`, instance.cloudLocation); const speech = await this.createSpeech(name, `${name}speech`, instance.cloudLocation);
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name); keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
instance.speechEndpoint = speech.endpoint; instance.speechEndpoint = speech.properties.endpoint;
instance.speechKey = keys.key1; instance.speechKey = keys.key1;
GBLog.info(`Deploying Text Analytics...`); GBLog.info(`Deploying Text Analytics...`);
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation); const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
instance.textAnalyticsEndpoint = textAnalytics.endpoint.replace(`/text/analytics/v2.0`, ''); instance.textAnalyticsEndpoint = textAnalytics.properties.endpoint.replace(`/text/analytics/v2.0`, '');
GBLog.info(`Deploying SpellChecker...`); GBLog.info(`Deploying SpellChecker...`);
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`); const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`);
instance.spellcheckerEndpoint = spellChecker.endpoint; instance.spellcheckerEndpoint = spellChecker.properties.endpoint;
GBLog.info(`Deploying NLP...`); GBLog.info(`Deploying NLP...`);
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation); const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
const nlpa = await this.createNLPAuthoring(name, `${name}-nlpa`, instance.cloudLocation); const nlpa = await this.createNLPAuthoring(name, `${name}-nlpa`, instance.cloudLocation);
instance.nlpEndpoint = nlp.endpoint; instance.nlpEndpoint = nlp.properties.endpoint;
const sleep = ms => { const sleep = ms => {
return new Promise(resolve => { return new Promise(resolve => {
@ -435,18 +429,25 @@ export class AzureDeployerService implements IGBInstallationDeployer {
}); });
}; };
GBLog.info(`Deploying Bot...`); GBLog.info(`Deploying Bot...`);
instance.botEndpoint = this.defaultEndPoint; instance.botEndpoint = this.defaultEndPoint;
instance = await this.internalDeployBot( instance = await this.internalDeployBot(
instance, this.accessToken, name, name, name, 'General BootBot', instance,
`${proxyAddress}/api/messages/${name}`, 'global', this.accessToken,
instance.nlpAppId, instance.nlpKey, instance.marketplaceId, instance.marketplacePassword, name,
name,
name,
'General BootBot',
`${proxyAddress}/api/messages/${name}`,
'global',
instance.nlpAppId,
instance.nlpKey,
instance.marketplaceId,
instance.marketplacePassword,
instance.cloudSubscriptionId instance.cloudSubscriptionId
); );
GBLog.info(`Waiting one minute to finishing NLP service and keys creation...`); GBLog.info(`Waiting one minute to finishing NLP service and keys creation...`);
await sleep(60000); await sleep(60000);
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name); keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
@ -461,7 +462,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey); const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
instance.nlpAppId = nlpAppId; instance.nlpAppId = nlpAppId;
GBLog.info('Updating server environment variables...'); GBLog.info('Updating server environment variables...');
await this.updateWebisteConfig(name, serverName, serverFarm.id, instance); await this.updateWebisteConfig(name, serverName, serverFarm.id, instance);
@ -540,13 +540,14 @@ export class AzureDeployerService implements IGBInstallationDeployer {
luisKey: nlpKey, luisKey: nlpKey,
msaAppId: appId, msaAppId: appId,
msaAppPassword: appPassword, msaAppPassword: appPassword,
enabledChannels: ['webchat', "skype"],//, "facebook"], enabledChannels: ['webchat', 'skype'], //, "facebook"],
configuredChannels: ['webchat', "skype"]//, "facebook"] configuredChannels: ['webchat', 'skype'] //, "facebook"]
} }
}; };
const httpClient = new ServiceClient(); const httpClient = new ServiceClient();
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
this.provider
}/botServices/${botId}?api-version=${this.apiVersion}`; }/botServices/${botId}?api-version=${this.apiVersion}`;
let url = urlJoin(baseUrl, query); let url = urlJoin(baseUrl, query);
let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters)); let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
@ -557,10 +558,10 @@ export class AzureDeployerService implements IGBInstallationDeployer {
return; return;
} }
try { try {
//tslint:disable-next-line:max-line-length //tslint:disable-next-line:max-line-length
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${this.apiVersion query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
this.apiVersion
}`; }`;
url = urlJoin(baseUrl, query); url = urlJoin(baseUrl, query);
req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters)); req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters));
@ -572,7 +573,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} catch (error) { } catch (error) {
reject(error); reject(error);
} }
}); });
} }
@ -581,7 +581,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
public initServices (credentials: any, subscriptionId: string) { public initServices (credentials: any, subscriptionId: string) {
this.cloud = new ResourceManagementClient.default(credentials, subscriptionId); this.cloud = new ResourceManagementClient(credentials, subscriptionId);
this.webSiteClient = new WebSiteManagementClient(credentials, subscriptionId); this.webSiteClient = new WebSiteManagementClient(credentials, subscriptionId);
this.storageClient = new SqlManagementClient(credentials, subscriptionId); this.storageClient = new SqlManagementClient(credentials, subscriptionId);
this.cognitiveClient = new CognitiveServicesManagementClient(credentials, subscriptionId); this.cognitiveClient = new CognitiveServicesManagementClient(credentials, subscriptionId);
@ -597,7 +597,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
fullyQualifiedDomainName: serverName fullyQualifiedDomainName: serverName
}; };
const database = await this.storageClient.servers.createOrUpdate(group, name, params); const database = await this.storageClient.servers.beginCreateOrUpdateAndWait(group, name, params);
// AllowAllWindowsAzureIps must be created that way, so the Azure Search can // AllowAllWindowsAzureIps must be created that way, so the Azure Search can
// access SQL Database to index its contents. // access SQL Database to index its contents.
@ -624,9 +624,8 @@ export class AzureDeployerService implements IGBInstallationDeployer {
client.api(`/applications`).post(app, (err, res) => { client.api(`/applications`).post(app, (err, res) => {
if (err) { if (err) {
reject(err) reject(err);
} } else {
else {
resolve(res); resolve(res);
} }
}); });
@ -642,15 +641,14 @@ export class AzureDeployerService implements IGBInstallationDeployer {
}); });
const body = { const body = {
passwordCredential: { passwordCredential: {
displayName: "General Bots Generated" displayName: 'General Bots Generated'
} }
}; };
client.api(`/applications/${appId}/addPassword`).post(body, (err, res) => { client.api(`/applications/${appId}/addPassword`).post(body, (err, res) => {
if (err) { if (err) {
reject(err) reject(err);
} } else {
else {
resolve(res.secretText); resolve(res.secretText);
} }
}); });
@ -688,13 +686,12 @@ export class AzureDeployerService implements IGBInstallationDeployer {
let app = null; let app = null;
if (apps.bodyAsText && apps.bodyAsText !== '[]') { if (apps.bodyAsText && apps.bodyAsText !== '[]') {
const result = JSON.parse(apps.bodyAsText) const result = JSON.parse(apps.bodyAsText);
if (result.error) { if (result.error) {
if (result.error.code !== "401") { if (result.error.code !== '401') {
throw new Error(result.error); throw new Error(result.error);
} }
} } else {
else {
app = result.filter(x => x.name === name)[0]; app = result.filter(x => x.name === name)[0];
} }
} }
@ -728,14 +725,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
return await httpClient.sendRequest(req); return await httpClient.sendRequest(req);
} }
public async refreshEntityList( public async refreshEntityList (location: string, nlpAppId: string, clEntityId: string, nlpKey: string, data: any) {
location: string,
nlpAppId: string,
clEntityId: string,
nlpKey: string,
data: any,
) {
const req = new WebResource(); const req = new WebResource();
req.method = 'PUT'; req.method = 'PUT';
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/closedlists/${clEntityId}`; req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/closedlists/${clEntityId}`;
@ -748,12 +738,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
return await httpClient.sendRequest(req); return await httpClient.sendRequest(req);
} }
public async trainNLP( public async trainNLP (location: string, nlpAppId: string, nlpAuthoringKey: string) {
location: string,
nlpAppId: string,
nlpAuthoringKey: string,
) {
const req = new WebResource(); const req = new WebResource();
req.method = 'POST'; req.method = 'POST';
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/train`; req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/train`;
@ -765,16 +750,12 @@ export class AzureDeployerService implements IGBInstallationDeployer {
return await httpClient.sendRequest(req); return await httpClient.sendRequest(req);
} }
public async publishNLP( public async publishNLP (location: string, nlpAppId: string, nlpAuthoringKey: string) {
location: string,
nlpAppId: string,
nlpAuthoringKey: string,
) {
const body = { const body = {
versionId: "0.1", versionId: '0.1',
isStaging: false, isStaging: false,
directVersionPublish: false directVersionPublish: false
} };
const req = new WebResource(); const req = new WebResource();
req.method = 'POST'; req.method = 'POST';
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/publish`; req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/publish`;
@ -790,13 +771,12 @@ export class AzureDeployerService implements IGBInstallationDeployer {
private async createSearch (group, name, location) { private async createSearch (group, name, location) {
const params = { const params = {
sku: { sku: {
name: name: this.freeTier ? 'free' : 'standard'
this.freeTier ? 'free' : 'standard'
}, },
location: location location: location
}; };
return await this.searchClient.services.createOrUpdate(group, name, params); return await this.searchClient.services.beginCreateOrUpdateAndWait(group, name, params as any);
} }
private async createStorage (group, serverName, name, location) { private async createStorage (group, serverName, name, location) {
@ -806,10 +786,10 @@ export class AzureDeployerService implements IGBInstallationDeployer {
location: location location: location
}; };
return await this.storageClient.databases.createOrUpdate(group, serverName, name, params); return await this.storageClient.databases.beginCreateOrUpdateAndWait(group, serverName, name, params);
} }
private async createCognitiveServices(group, name, location, kind): Promise<CognitiveServicesAccount> { private async createCognitiveServices (group, name, location, kind): Promise<Account> {
const params = { const params = {
sku: { sku: {
name: name name: name
@ -832,26 +812,26 @@ export class AzureDeployerService implements IGBInstallationDeployer {
params.sku.name = this.freeTier ? 'F0' : 'S0'; params.sku.name = this.freeTier ? 'F0' : 'S0';
} }
return await this.cognitiveClient.accounts.create(group, name, params); return await this.cognitiveClient.accounts.beginCreateAndWait(group, name, params);
} }
private async createSpeech(group, name, location): Promise<CognitiveServicesAccount> { private async createSpeech (group, name, location): Promise<Account> {
return await this.createCognitiveServices(group, name, location, 'SpeechServices'); return await this.createCognitiveServices(group, name, location, 'SpeechServices');
} }
private async createNLP(group, name, location): Promise<CognitiveServicesAccount> { private async createNLP (group, name, location): Promise<Account> {
return await this.createCognitiveServices(group, name, location, 'LUIS'); return await this.createCognitiveServices(group, name, location, 'LUIS');
} }
private async createNLPAuthoring(group, name, location): Promise<CognitiveServicesAccount> { private async createNLPAuthoring (group, name, location): Promise<Account> {
return await this.createCognitiveServices(group, name, location, 'LUIS.Authoring'); return await this.createCognitiveServices(group, name, location, 'LUIS.Authoring');
} }
private async createSpellChecker(group, name): Promise<CognitiveServicesAccount> { private async createSpellChecker (group, name): Promise<Account> {
return await this.createCognitiveServices(group, name, 'westus', 'CognitiveServices'); return await this.createCognitiveServices(group, name, 'westus', 'CognitiveServices');
} }
private async createTextAnalytics(group, name, location): Promise<CognitiveServicesAccount> { private async createTextAnalytics (group, name, location): Promise<Account> {
return await this.createCognitiveServices(group, name, location, 'TextAnalytics'); return await this.createCognitiveServices(group, name, location, 'TextAnalytics');
} }
@ -863,7 +843,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
private async enableResourceProviders (name) { private async enableResourceProviders (name) {
const ret = await this.cloud.providers.get(name); const ret = await this.cloud.providers.get(name);
if (ret.registrationState === "NotRegistered") { if (ret.registrationState === 'NotRegistered') {
await this.cloud.providers.register(name); await this.cloud.providers.register(name);
} }
} }
@ -879,14 +859,12 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
}; };
return await this.webSiteClient.appServicePlans.createOrUpdate(group, name, params); return await this.webSiteClient.appServicePlans.beginCreateOrUpdateAndWait(group, name, params);
} }
private async createServer (farmId, group, name, location) { private async createServer (farmId, group, name, location) {
let tryed = false; let tryed = false;
const create = async () => { const create = async () => {
const parameters: Site = { const parameters: Site = {
location: location, location: location,
serverFarmId: farmId, serverFarmId: farmId,
@ -897,7 +875,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
requestTracingEnabled: true requestTracingEnabled: true
} }
}; };
const server = await this.webSiteClient.webApps.createOrUpdate(group, name, parameters); const server = await this.webSiteClient.webApps.beginCreateOrUpdateAndWait(group, name, parameters);
const siteLogsConfig: SiteLogsConfig = { const siteLogsConfig: SiteLogsConfig = {
applicationLogs: { applicationLogs: {
@ -914,7 +892,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
deploymentRollbackEnabled: false deploymentRollbackEnabled: false
}; };
await this.webSiteClient.webApps.createOrUpdateSourceControl(group, name, souceControlConfig); await this.webSiteClient.webApps.beginCreateOrUpdateSourceControlAndWait(group, name, souceControlConfig);
return server; return server;
}; };
@ -935,7 +913,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
} }
private async updateWebisteConfig (group, name, serverFarmId, instance: IGBInstance) { private async updateWebisteConfig (group, name, serverFarmId, instance: IGBInstance) {
const parameters: Site = { const parameters: Site = {
location: instance.cloudLocation, location: instance.cloudLocation,
serverFarmId: serverFarmId, serverFarmId: serverFarmId,
@ -958,12 +935,11 @@ export class AzureDeployerService implements IGBInstallationDeployer {
{ name: 'STORAGE_NAME', value: `${instance.storageName}` }, { name: 'STORAGE_NAME', value: `${instance.storageName}` },
{ name: 'STORAGE_USERNAME', value: `${instance.storageUsername}` }, { name: 'STORAGE_USERNAME', value: `${instance.storageUsername}` },
{ name: 'STORAGE_PASSWORD', value: `${instance.storagePassword}` }, { name: 'STORAGE_PASSWORD', value: `${instance.storagePassword}` },
{ name: 'STORAGE_SYNC', value: `true` }] { name: 'STORAGE_SYNC', value: `true` }
]
} }
}; };
return await this.webSiteClient.webApps.createOrUpdate(group, name, parameters); return await this.webSiteClient.webApps.beginCreateOrUpdateAndWait(group, name, parameters);
} }
} }

View file

@ -37,18 +37,30 @@
'use strict'; 'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { GuaribasSchedule } from '../core.gbapp/models/GBModel'; import { GuaribasSchedule } from '../core.gbapp/models/GBModel.js';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { createServerRouter } from 'typescript-rest-rpc/lib/server.js';
import { DialogKeywords } from './services/DialogKeywords.js';
import * as koaBody from 'koa-body';
import { SystemKeywords } from './services/SystemKeywords.js';
import { WebAutomationKeywords } from './services/WebAutomationKeywords.js';
import { DebuggerService } from './services/DebuggerService.js';
import Koa from 'koa';
const app = new Koa();
/** /**
* Package for core.gbapp. * Package for core.gbapp.
*/ */
export class GBBasicPackage implements IGBPackage { export class GBBasicPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public CurrentEngineName = "guaribas-1.0.0"; public CurrentEngineName = 'guaribas-1.0.0';
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> { public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasSchedule]); core.sequelize.addModels([GuaribasSchedule]);
app.use(koaBody.koaBody({ multipart: true }));
app.listen(1111);
} }
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
@ -67,6 +79,19 @@ export class GBBasicPackage implements IGBPackage {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
public async loadBot (min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {
const dk = new DialogKeywords(min, null, null);
const wa = new WebAutomationKeywords(min, null, dk);
const sys = new SystemKeywords(min, null, dk, wa);
const dbg = new DebuggerService(min, null, dk);
dk.wa = wa;
wa.sys = sys;
const dialogRouter = createServerRouter(`/api/v2/${min.botId}/dialog`, dk);
const waRouter = createServerRouter(`/api/v2/${min.botId}/webautomation`, wa);
const sysRouter = createServerRouter(`/api/v2/${min.botId}/system`, sys);
const dbgRouter = createServerRouter(`/api/v2/${min.botId}/debugger`, dbg);
app.use(dialogRouter.routes());
app.use(sysRouter.routes());
app.use(waRouter.routes());
app.use(dbgRouter.routes());
} }
} }

View file

@ -49,31 +49,29 @@ import {
UpdatedAt UpdatedAt
} from 'sequelize-typescript'; } from 'sequelize-typescript';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
@Table @Table
//tslint:disable-next-line:max-classes-per-file //tslint:disable-next-line:max-classes-per-file
export class GuaribasSchedule extends Model<GuaribasSchedule> { export class GuaribasSchedule extends Model<GuaribasSchedule> {
@Column(DataType.STRING(255))
name: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public name: string; schedule: string;
@Column (DataType.STRING(255))
public schedule: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; updatedAt: Date;
} }

View file

@ -32,11 +32,9 @@
'use strict'; 'use strict';
import { createBrowser } from '../../core.gbapp/services/GBSSR.js';
import { createBrowser } from '../../core.gbapp/services/GBSSR';
export class ChartServices { export class ChartServices {
/** /**
* Generate chart image screenshot * Generate chart image screenshot
* @param {object} options billboard.js generation option object * @param {object} options billboard.js generation option object
@ -47,12 +45,12 @@ export class ChartServices {
const page = await browser.newPage(); const page = await browser.newPage();
// load billboard.js assets from CDN. // load billboard.js assets from CDN.
await page.addStyleTag({ url: "https://cdn.jsdelivr.net/npm/billboard.js/dist/theme/datalab.min.css" }); await page.addStyleTag({ url: 'https://cdn.jsdelivr.net/npm/billboard.js/dist/theme/datalab.min.css' });
await page.addScriptTag({ url: "https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.pkgd.min.js" }); await page.addScriptTag({ url: 'https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.pkgd.min.js' });
await page.evaluate(`bb.generate(${JSON.stringify(args)});`); await page.evaluate(`bb.generate(${JSON.stringify(args)});`);
const content = await page.$(".bb"); const content = await page.$('.bb');
await content.screenshot({ await content.screenshot({
path, path,

View file

@ -0,0 +1,328 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' 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 { GBLog, GBMinInstance } from 'botlib';
import { GBServer } from '../../../src/app.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { DialogKeywords } from './DialogKeywords.js';
import Fs from 'fs';
import Swagger from 'swagger-client';
import { spawn } from 'child_process';
/**
* Web Automation services of conversation to be called by BASIC.
*/
export class DebuggerService {
/**
* Reference to minimal bot instance.
*/
public min: GBMinInstance;
/**
* Reference to the base system keywords functions to be called.
*/
public dk: DialogKeywords;
/**
* Current user object to get BASIC properties read.
*/
public user;
/**
* HTML browser for conversation over page interaction.
*/
browser: any;
sys: any;
/**
* The number used in this execution for HEAR calls (useful for SET SCHEDULE).
*/
hrOn: string;
userId: GuaribasUser;
debugWeb: boolean;
lastDebugWeb: Date;
/**
* SYSTEM account maxLines,when used with impersonated contexts (eg. running in SET SCHEDULE).
*/
maxLines: number = 2000;
conversationsMap = {};
watermarkMap = {};
static systemVariables = [
'AggregateError',
'Array',
'ArrayBuffer',
'Atomics',
'BigInt',
'BigInt64Array',
'BigUint64Array',
'Boolean',
'DataView',
'Date',
'Error',
'EvalError',
'FinalizationRegistry',
'Float32Array',
'Float64Array',
'Function',
'Headers',
'Infinity',
'Int16Array',
'Int32Array',
'Int8Array',
'Intl',
'JSON',
'Map',
'Math',
'NaN',
'Number',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'Reflect',
'RegExp',
'Request',
'Response',
'Set',
'SharedArrayBuffer',
'String',
'Symbol',
'SyntaxError',
'TypeError',
'URIError',
'Uint16Array',
'Uint32Array',
'Uint8Array',
'Uint8ClampedArray',
'VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL',
'WeakMap',
'WeakRef',
'WeakSet',
'WebAssembly',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__',
'clearImmediate',
'clearInterval',
'clearTimeout',
'console',
'constructor',
'decodeURI',
'decodeURIComponent',
'dss',
'encodeURI',
'encodeURIComponent',
'escape',
'eval',
'fetch',
'global',
'globalThis',
'hasOwnProperty',
'isFinite',
'isNaN',
'isPrototypeOf',
'parseFloat',
'parseInt',
'process',
'propertyIsEnumerable',
'setImmediate',
'setInterval',
'setTimeout',
'toLocaleString',
'toString',
'undefined',
'unescape',
'valueOf'
];
/**
* When creating this keyword facade,a bot instance is
* specified among the deployer service.
*/
constructor (min: GBMinInstance, user, dk) {
this.min = min;
this.user = user;
this.dk = dk;
this.debugWeb = this.min.core.getParam<boolean>(this.min.instance, 'Debug Web Automation', false);
const botId = min.botId;
GBServer.globals.debuggers[botId] = {};
GBServer.globals.debuggers[botId].state = 0;
GBServer.globals.debuggers[botId].breaks = [];
GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
GBServer.globals.debuggers[botId].childProcess = null;
}
private client;
public async breakpoint ({ botId, line }) {
GBLog.info(`BASIC: Enabled breakpoint for ${botId} on ${line}.`);
GBServer.globals.debuggers[botId].breaks.push(Number.parseInt(line));
}
public async resume ({ botId }) {
if (GBServer.globals.debuggers[botId].state === 2) {
const client = GBServer.globals.debuggers[botId].client;
await client.Debugger.resume();
GBServer.globals.debuggers[botId].state = 1;
GBServer.globals.debuggers[botId].stateInfo = 'Running (Debug)';
return { status: 'OK' };
} else {
const error = 'Invalid call to resume and state not being debug(2).';
return { error: error };
}
}
public async stop ({ botId }) {
GBServer.globals.debuggers[botId].state = 0;
GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
const kill = ref => {
spawn('sh', ['-c', `pkill -9 -f ${ref}`]);
};
kill(GBServer.globals.debuggers[botId].childProcess);
return { status: 'OK' };
}
public async step ({ botId }) {
if (GBServer.globals.debuggers[botId].state === 2) {
GBServer.globals.debuggers[botId].stateInfo = 'Break';
const client = GBServer.globals.debuggers[botId].client;
await client.Debugger.stepOver();
return { status: 'OK' };
} else {
const error = 'Invalid call to stepOver and state not being debug(2).';
return { error: error };
}
}
public async context ({ botId }) {
const conversationId = this.conversationsMap[botId];
let messages = [];
if (this.client) {
const response = await this.client.Conversations.Conversations_GetActivities({
conversationId: conversationId,
watermark: this.watermarkMap[botId]
});
this.watermarkMap[botId] = response.obj.watermark;
let activities = response.obj.activites;
if (activities && activities.length) {
activities = activities.filter(m => m.from.id === botId && m.type === 'message');
if (activities.length) {
activities.forEach(activity => {
messages.push({ text: activity.text });
GBLog.info(`Debugger sending text to API: ${activity.text}`);
});
}
}
}
let messagesText = messages.join('\n');
return {
status: 'OK',
state: GBServer.globals.debuggers[botId].state,
messages: messagesText,
scope: GBServer.globals.debuggers[botId].scope,
scopeInfo: GBServer.globals.debuggers[botId].stateInfo
};
}
public async getRunning ({ botId, botApiKey, scriptName }) {
let error;
botId = botId[0]; // TODO: Handle call in POST.
if (!GBServer.globals.debuggers[botId]) {
GBServer.globals.debuggers[botId] = {};
}
if (!scriptName) {
scriptName = 'start';
}
if (GBServer.globals.debuggers[botId].state === 1) {
error = `Cannot DEBUG an already running process. ${botId}`;
return { error: error };
} else if (GBServer.globals.debuggers[botId].state === 2) {
GBLog.info(`BASIC: Releasing execution ${botId} in DEBUG mode.`);
await this.resume({ botId });
return { status: 'OK' };
} else {
GBLog.info(`BASIC: Running ${botId} in DEBUG mode.`);
GBServer.globals.debuggers[botId].state = 1;
GBServer.globals.debuggers[botId].stateInfo = 'Running (Debug)';
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
this.client = await new Swagger({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
usePromise: true
});
this.client.clientAuthorizations.add(
'AuthorizationBotConnector',
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${min.instance.webchatKey}`, 'header')
);
const response = await this.client.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
this.conversationsMap[botId] = conversationId;
GBServer.globals.debugConversationId = conversationId;
this.client.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: `/calldbg ${scriptName}`,
type: 'message',
from: {
id: 'test',
name: 'test'
}
}
});
return { status: 'OK' };
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -33,13 +33,12 @@
'use strict'; 'use strict';
import { GBLog, GBMinInstance, GBService } from 'botlib'; import { GBLog, GBMinInstance, GBService } from 'botlib';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { GuaribasSchedule } from '../../core.gbapp/models/GBModel'; import { GuaribasSchedule } from '../../core.gbapp/models/GBModel.js';
import { FindOptions } from 'sequelize/types';
const cron = require('node-cron'); import cron from 'node-cron';
/** /**
* @fileoverview Schedule Services. * @fileoverview Schedule Services.
@ -49,14 +48,12 @@ const cron = require('node-cron');
* Basic services for BASIC manipulation. * Basic services for BASIC manipulation.
*/ */
export class ScheduleServices extends GBService { export class ScheduleServices extends GBService {
public async deleteScheduleIfAny (min: GBMinInstance, name: string) { public async deleteScheduleIfAny (min: GBMinInstance, name: string) {
const task = min['scheduleMap'] ? min['scheduleMap'][name] : null;
const task = min["scheduleMap"] ? min["scheduleMap"][name] : null;
if (task) { if (task) {
task.destroy(); task.destroy();
delete min["scheduleMap"][name]; delete min['scheduleMap'][name];
} }
const count = await GuaribasSchedule.destroy({ const count = await GuaribasSchedule.destroy({
@ -74,11 +71,7 @@ export class ScheduleServices extends GBService {
/** /**
* Finds and update user agent information to a next available person. * Finds and update user agent information to a next available person.
*/ */
public async createOrUpdateSchedule( public async createOrUpdateSchedule (min: GBMinInstance, schedule: string, name: string): Promise<GuaribasSchedule> {
min: GBMinInstance,
schedule: string,
name: string
): Promise<GuaribasSchedule> {
let record = await GuaribasSchedule.findOne({ let record = await GuaribasSchedule.findOne({
where: { where: {
instanceId: min.instance.instanceId, instanceId: min.instance.instanceId,
@ -102,12 +95,10 @@ export class ScheduleServices extends GBService {
return record; return record;
} }
/** /**
* Load all cached schedule from BASIC SET SCHEDULE keyword. * Load all cached schedule from BASIC SET SCHEDULE keyword.
*/ */
public async scheduleAll () { public async scheduleAll () {
let schedules; let schedules;
try { try {
schedules = await GuaribasSchedule.findAll(); schedules = await GuaribasSchedule.findAll();
@ -134,13 +125,13 @@ export class ScheduleServices extends GBService {
timezone: 'America/Sao_Paulo' timezone: 'America/Sao_Paulo'
}; };
const task = min["scheduleMap"][item.name]; const task = min['scheduleMap'][item.name];
if (task) { if (task) {
task.stop(); task.stop();
min["scheduleMap"][item.name] = null; min['scheduleMap'][item.name] = null;
} }
min["scheduleMap"][item.name] = cron.schedule( min['scheduleMap'][item.name] = cron.schedule(
item.schedule, item.schedule,
function () { function () {
const finalData = async () => { const finalData = async () => {
@ -148,12 +139,13 @@ export class ScheduleServices extends GBService {
let min: GBMinInstance = GBServer.globals.minInstances.filter( let min: GBMinInstance = GBServer.globals.minInstances.filter(
p => p.instance.instanceId === item.instanceId p => p.instance.instanceId === item.instanceId
)[0]; )[0];
await GBVMService.callVM(script, min, null, null); await GBVMService.callVM(script, min, null, null, false);
}; };
(async () => { (async () => {
await finalData(); await finalData();
})(); })();
}, options },
options
); );
GBLog.info(`Running .gbdialog word ${item.name} on:${item.schedule}...`); GBLog.info(`Running .gbdialog word ${item.name} on:${item.schedule}...`);
} catch (error) {} } catch (error) {}

File diff suppressed because it is too large Load diff

View file

@ -43,15 +43,15 @@ import * as ts from 'typescript';
* Wrapper for a TypeScript compiler. * Wrapper for a TypeScript compiler.
*/ */
export class TSCompiler { export class TSCompiler {
private static shouldIgnoreError (diagnostic) { private static shouldIgnoreError (diagnostic) {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
if (message.indexOf('Cannot find name') >= 0 if (
|| message.indexOf('Cannot find module') >= 0 message.indexOf('Cannot find name') >= 0 ||
|| message.indexOf('implicitly has an') >= 0 message.indexOf('Cannot find module') >= 0 ||
|| message.indexOf('Cannot invoke an') >= 0 message.indexOf('implicitly has an') >= 0 ||
|| message.indexOf('Cannot use imports, exports, or module') >= 0 message.indexOf('Cannot invoke an') >= 0 ||
message.indexOf('Cannot use imports, exports, or module') >= 0
) { ) {
return true; return true;
} }
@ -66,7 +66,7 @@ export class TSCompiler {
noImplicitUseStrict: true, noImplicitUseStrict: true,
noEmitOnError: false, noEmitOnError: false,
noImplicitAny: true, noImplicitAny: true,
target: ts.ScriptTarget.ES5, target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.None, module: ts.ModuleKind.None,
moduleResolution: ts.ModuleResolutionKind.Classic, moduleResolution: ts.ModuleResolutionKind.Classic,
noEmitHelpers: true, noEmitHelpers: true,
@ -99,5 +99,4 @@ export class TSCompiler {
return emitResult; return emitResult;
} }
} }

View file

@ -0,0 +1,386 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' 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 { GBLog, GBMinInstance } from 'botlib';
import { GBServer } from '../../../src/app.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { createBrowser } from '../../core.gbapp/services/GBSSR.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { DialogKeywords } from './DialogKeywords.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import urlJoin from 'url-join';
import Fs from 'fs';
import Path from 'path';
import url from 'url';
/**
* Web Automation services of conversation to be called by BASIC.
*/
export class WebAutomationKeywords {
/**
* Reference to minimal bot instance.
*/
public min: GBMinInstance;
/**
* Reference to the base system keywords functions to be called.
*/
public dk: DialogKeywords;
/**
* Current user object to get BASIC properties read.
*/
public user;
/**
* HTML browser for conversation over page interaction.
*/
browser: any;
sys: any;
/**
* The number used in this execution for HEAR calls (useful for SET SCHEDULE).
*/
hrOn: string;
userId: GuaribasUser;
debugWeb: boolean;
lastDebugWeb: Date;
/**
* SYSTEM account maxLines,when used with impersonated contexts (eg. running in SET SCHEDULE).
*/
maxLines: number = 2000;
pageMap = {};
cyrb53 = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
/**
* When creating this keyword facade,a bot instance is
* specified among the deployer service.
*/
constructor (min: GBMinInstance, user, dk) {
this.min = min;
this.user = user;
this.dk = dk;
this.debugWeb = this.min.core.getParam<boolean>(this.min.instance, 'Debug Web Automation', false);
}
/**
* Returns the page object.
*
* @example x = GET PAGE
*/
public async getPage ({ url, username, password }) {
GBLog.info(`BASIC: Web Automation GET PAGE ${url}.`);
if (!this.browser) {
this.browser = await createBrowser(null);
}
const page = (await this.browser.pages())[0];
if (username || password) {
await page.authenticate({ username: username, password: password });
}
await page.goto(url);
const handle = this.cyrb53(this.min.botId + url);
this.pageMap[handle] = page;
return handle;
}
public getPageByHandle (hash) {
return this.pageMap[hash];
}
/**
* Find element on page DOM.
*
* @example GET page,"selector"
*/
public async getBySelector ({ handle, selector }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation GET element: ${selector}.`);
await page.waitForSelector(selector);
let elements = await page.$$(selector);
if (elements && elements.length > 1) {
return elements;
} else {
const el = elements[0];
el['originalSelector'] = selector;
el['href'] = await page.evaluate(e => e.getAttribute('href'), el);
el['value'] = await page.evaluate(e => e.getAttribute('value'), el);
el['name'] = await page.evaluate(e => e.getAttribute('name'), el);
el['class'] = await page.evaluate(e => e.getAttribute('class'), el);
return el;
}
}
/**
* Find element on page DOM.
*
* @example GET page,"frameSelector,"elementSelector"
*/
public async getByFrame ({ handle, frame, selector }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation GET element by frame: ${selector}.`);
await page.waitForSelector(frame);
let frameHandle = await page.$(frame);
const f = await frameHandle.contentFrame();
await f.waitForSelector(selector);
const element = await f.$(selector);
element['originalSelector'] = selector;
element['href'] = await f.evaluate(e => e.getAttribute('href'), element);
element['value'] = await f.evaluate(e => e.getAttribute('value'), element);
element['name'] = await f.evaluate(e => e.getAttribute('name'), element);
element['class'] = await f.evaluate(e => e.getAttribute('class'), element);
element['frame'] = f;
return element;
}
/**
* Simulates a mouse hover an web page element.
*/
public async hover ({ handle, selector }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation HOVER element: ${selector}.`);
await this.getBySelector({ handle, selector: selector });
await page.hover(selector);
await this.debugStepWeb(page);
}
/**
* Clicks on an element in a web page.
*
* @example CLICK page,"#idElement"
*/
public async click ({ handle, frameOrSelector, selector }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation CLICK element: ${frameOrSelector}.`);
if (selector) {
await page.waitForSelector(frameOrSelector);
let frameHandle = await page.$(frameOrSelector);
const f = await frameHandle.contentFrame();
await f.waitForSelector(selector);
await f.click(selector);
} else {
await page.waitForSelector(frameOrSelector);
await page.click(frameOrSelector);
}
await this.debugStepWeb(page);
}
private async debugStepWeb (page) {
let refresh = true;
if (this.lastDebugWeb) {
refresh = new Date().getTime() - this.lastDebugWeb.getTime() > 5000;
}
if (this.debugWeb && refresh) {
const mobile = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
const filename = page;
if (mobile) {
await this.dk.sendFileTo({ mobile, filename, caption: 'General Bots Debugger' });
}
this.lastDebugWeb = new Date();
}
}
/**
* Press ENTER in a web page,useful for logins.
*
* @example PRESS ENTER ON page
*/
public async pressKey ({ handle, char, frame }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation PRESS ${char} ON element: ${frame}.`);
if (char.toLowerCase() === 'enter') {
char = '\n';
}
if (frame) {
await page.waitForSelector(frame);
let frameHandle = await page.$(frame);
const f = await frameHandle.contentFrame();
await f.keyboard.press(char);
} else {
await page.keyboard.press(char);
}
}
public async linkByText ({ handle, text, index }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation CLICK LINK TEXT: ${text} ${index}.`);
if (!index) {
index = 1;
}
const els = await page.$x(`//a[contains(.,'${text}')]`);
await els[index - 1].click();
await this.debugStepWeb(page);
}
/**
* Returns the screenshot of page or element
*
* @example file = SCREENSHOT page
*/
public async screenshot ({ handle, selector }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation SCREENSHOT ${selector}.`);
const gbaiName = `${this.min.botId}.gbai`;
const localName = Path.join('work', gbaiName, 'cache', `screen-${GBAdminService.getRndReadableIdentifier()}.jpg`);
await page.screenshot({ path: localName });
const url = urlJoin(GBServer.globals.publicAddress, this.min.botId, 'cache', Path.basename(localName));
GBLog.info(`BASIC: WebAutomation: Screenshot captured at ${url}.`);
return url;
}
/**
* Types the text into the text field.
*
* @example SET page,"selector","text"
*/
public async setElementText ({ handle, selector, text }) {
const page = this.getPageByHandle(handle);
GBLog.info(`BASIC: Web Automation TYPE on ${selector}: ${text}.`);
const e = await this.getBySelector({ handle, selector });
await e.click({ clickCount: 3 });
await page.keyboard.press('Backspace');
await e.type(text, { delay: 200 });
await this.debugStepWeb(page);
}
/**
* Performs the download to the .gbdrive Download folder.
*
* @example file = DOWNLOAD element, folder
*/
public async download ({ handle, selector, folder }) {
const page = this.getPageByHandle(handle);
const container = page; // TODO: element['_frame'] ? element['_frame'] : element['_page'];
const element = await this.getBySelector({ handle, selector });
await page.setRequestInterception(true);
await container.click(element.originalSelector);
const xRequest = await new Promise(resolve => {
page.on('request', interceptedRequest => {
interceptedRequest.abort(); //stop intercepting requests
resolve(interceptedRequest);
});
});
const options = {
encoding: null,
method: xRequest['._method'],
uri: xRequest['_url'],
body: xRequest['_postData'],
headers: xRequest['_headers']
};
const cookies = await page.cookies();
options.headers.Cookie = cookies.map(ck => ck.name + '=' + ck.value).join(';');
GBLog.info(`BASIC: DOWNLOADING '${options.uri}...'`);
let local;
let filename;
if (options.uri.indexOf('file://') != -1) {
local = url.fileURLToPath(options.uri);
filename = Path.basename(local);
} else {
const getBasenameFormUrl = urlStr => {
const url = new URL(urlStr);
return Path.basename(url.pathname);
};
filename = getBasenameFormUrl(options.uri);
}
let result: Buffer;
if (local) {
result = Fs.readFileSync(local);
} else {
const res = await fetch(options.uri, options);
result = Buffer.from(await res.arrayBuffer());
}
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(this.min);
const botId = this.min.instance.botId;
// Normalizes all slashes.
folder = folder.replace(/\\/gi, '/');
// Determines full path at source and destination.
const root = urlJoin(`/${botId}.gbai/${botId}.gbdrive`);
const dstPath = urlJoin(root, folder, filename);
// Checks if the destination contains subfolders that
// need to be created.
folder = await this.sys.createFolder(folder);
// Performs the conversion operation getting a reference
// to the source and calling /content on drive API.
let file;
try {
file = await client.api(`${baseUrl}/drive/root:/${dstPath}:/content`).put(result);
} catch (error) {
if (error.code === 'nameAlreadyExists') {
GBLog.info(`BASIC: DOWNLOAD destination file already exists: ${dstPath}.`);
}
throw error;
}
return file;
}
}

View file

@ -1,31 +1,32 @@
// Source: https://github.com/uweg/vbscript-to-typescript // Source: https://github.com/uweg/vbscript-to-typescript
"use strict"; 'use strict';
exports.__esModule = true;
var fs_1 = require("fs"); import fs_1 from 'fs';
var path = require("path"); import path from 'path';
function convertFile(file) {
export function convertFile (file) {
var extension = path.extname(file); var extension = path.extname(file);
var withoutExtension = file.substr(0, file.length - extension.length); var withoutExtension = file.substr(0, file.length - extension.length);
var targetFile = withoutExtension + ".ts"; var targetFile = withoutExtension + '.ts';
var baseName = path.basename(file, extension); var baseName = path.basename(file, extension);
var content = fs_1.readFileSync(file, 'utf8'); var content = fs_1.readFileSync(file, 'utf8');
var result = convert(content, baseName); var result = convert(content, baseName);
console.log("Writing to \"" + targetFile + "\"..."); console.log('Writing to "' + targetFile + '"...');
fs_1.writeFileSync(targetFile, result); fs_1.writeFileSync(targetFile, result);
} }
exports.convertFile = convertFile;
function convert(input, name) { export function convert (input, name) {
var result = convertImports(input, name); var result = convertImports(input, name);
return result; return result;
} }
exports.convert = convert;
function convertImports (input, name) { function convertImports (input, name) {
var items = []; var items = [];
var result = input.replace(/<!-- #include file="(.*?\/)?(.*?).asp" -->/gi, function (input, group1, group2) { var result = input.replace(/<!-- #include file="(.*?\/)?(.*?).asp" -->/gi, function (input, group1, group2) {
var path = group1 || './'; var path = group1 || './';
var file = "" + path + group2; var file = '' + path + group2;
items.push({ name: group2, path: file }); items.push({ name: group2, path: file });
return "<%\n" + group2 + "();\n%>"; return '<%\n' + group2 + '();\n%>';
}); });
result = convertCode(result); result = convertCode(result);
result = convertExpressions(result); result = convertExpressions(result);
@ -33,11 +34,11 @@ function convertImports(input, name) {
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;
} }
return result; return result;
} }
exports.convertImports = convertImports;
function convertCode (input) { function convertCode (input) {
var result = input.replace(/<%([^=][\s\S]*?)%>/gi, function (input, group1) { var result = input.replace(/<%([^=][\s\S]*?)%>/gi, function (input, group1) {
var code = group1; var code = group1;
@ -49,109 +50,107 @@ function convertCode(input) {
code = convertLoops(code); code = convertLoops(code);
code = convertPRec(code); code = convertPRec(code);
code = convertPLan(code); code = convertPLan(code);
return "<%" + code + "%>"; return '<%' + code + '%>';
}); });
return result; return result;
} }
exports.convertCode = convertCode;
function convertExpressions (input) { function convertExpressions (input) {
var result = input.replace(/<%=([\s\S]*?)%>/gi, function (input, group1) { var result = input.replace(/<%=([\s\S]*?)%>/gi, function (input, group1) {
var content = convertPRec(group1); var content = convertPRec(group1);
content = convertPLan(content); content = convertPLan(content);
return "${" + content + "}"; return '${' + content + '}';
}); });
return result; return result;
} }
exports.convertExpressions = convertExpressions;
function convertStrings (input) { function convertStrings (input) {
var result = input.replace(/%>([\s\S]+?)<%/gi, "\nResponse.Write(`$1`);\n"); var result = input.replace(/%>([\s\S]+?)<%/gi, '\nResponse.Write(`$1`);\n');
// Entire document is a string // Entire document is a string
if (result.indexOf("<%") === -1) { if (result.indexOf('<%') === -1) {
result = "Response.Write(`" + result + "`);"; result = 'Response.Write(`' + result + '`);';
} }
// Start of the document is a string // Start of the document is a string
var firstIndex = result.indexOf("<%"); var firstIndex = result.indexOf('<%');
if (firstIndex > 0) { if (firstIndex > 0) {
result = "Response.Write(`" + result.substr(0, firstIndex) + "`);\n" + result.substring(firstIndex + 2); result = 'Response.Write(`' + result.substr(0, firstIndex) + '`);\n' + result.substring(firstIndex + 2);
} }
result = result.replace(/%>$/, ""); result = result.replace(/%>$/, '');
// End of the document is a string // End of the document is a string
var lastIndex = result.lastIndexOf("%>"); var lastIndex = result.lastIndexOf('%>');
if (lastIndex > -1 && lastIndex < result.length - 2) { if (lastIndex > -1 && lastIndex < result.length - 2) {
result = result.substr(0, lastIndex) + "\nResponse.Write(`" + result.substr(lastIndex + 3) + "`);"; result = result.substr(0, lastIndex) + '\nResponse.Write(`' + result.substr(lastIndex + 3) + '`);';
} }
result = result.replace(/^<%/, ""); result = result.replace(/^<%/, '');
return result; return result;
} }
exports.convertStrings = convertStrings;
function convertComments (input) { function convertComments (input) {
var result = ''; var result = '';
var splitted = input.split(/(".*")/gim); var splitted = input.split(/(".*")/gim);
for (var _i = 0, splitted_1 = splitted; _i < splitted_1.length; _i++) { for (var _i = 0, splitted_1 = splitted; _i < splitted_1.length; _i++) {
var part = splitted_1[_i]; var part = splitted_1[_i];
if (part.indexOf("\"") === 0) { if (part.indexOf('"') === 0) {
result += part; result += part;
} } else {
else { result += part.replace(/'/gi, '//');
result += part.replace(/'/gi, "//");
} }
} }
return result; return result;
} }
exports.convertComments = convertComments;
function convertIfStatements (input) { function convertIfStatements (input) {
var result = input.replace(/if +(.*?) +then/gi, function (input, group1) { var result = input.replace(/if +(.*?) +then/gi, function (input, group1) {
var condition = convertConditions(group1); var condition = convertConditions(group1);
return "\nif (" + condition + ") {\n"; return '\nif (' + condition + ') {\n';
}); });
result = result.replace(/end if/gi, "\n}\n"); result = result.replace(/end if/gi, '\n}\n');
result = result.replace(/else(?!{)/gi, "\n}\nelse {\n"); result = result.replace(/else(?!{)/gi, '\n}\nelse {\n');
return result; return result;
} }
exports.convertIfStatements = convertIfStatements;
function convertSwitchStatements (input) { function convertSwitchStatements (input) {
var result = input.replace(/select case +(.*)/gi, "\nswitch ($1) {\n"); var result = input.replace(/select case +(.*)/gi, '\nswitch ($1) {\n');
result = result.replace(/end select/gi, "\n}\n"); result = result.replace(/end select/gi, '\n}\n');
return result; return result;
} }
exports.convertSwitchStatements = convertSwitchStatements;
function convertFunctions (input) { function convertFunctions (input) {
var result = input.replace(/function +(.*)\((.*)\)/gi, "\n$1 = ($2) => {\n"); var result = input.replace(/function +(.*)\((.*)\)/gi, '\n$1 = ($2) => {\n');
result = result.replace(/end function/gi, "\n}\n"); result = result.replace(/end function/gi, '\n}\n');
return result; return result;
} }
exports.convertFunctions = convertFunctions;
function convertForStatements (input) { function convertForStatements (input) {
var result = input.replace(/for +(.*to.*)/gi, "\nfor ($1) {\n"); var result = input.replace(/for +(.*to.*)/gi, '\nfor ($1) {\n');
result = result.replace(/^ *next *$/gim, "}\n"); result = result.replace(/^ *next *$/gim, '}\n');
return result; return result;
} }
exports.convertForStatements = convertForStatements;
function convertConditions (input) { function convertConditions (input) {
var result = input.replace(/ +and +/gi, " && "); var result = input.replace(/ +and +/gi, ' && ');
result = result.replace(/ +or +/gi, " || "); result = result.replace(/ +or +/gi, ' || ');
result = result.replace(/ +<> +/gi, " !== "); result = result.replace(/ +<> +/gi, ' !== ');
result = result.replace(/ += +/gi, " === "); result = result.replace(/ += +/gi, ' === ');
return result; return result;
} }
exports.convertConditions = convertConditions;
function convertLoops (input) { function convertLoops (input) {
var result = input.replace(/do while +(.*)/gi, function (input, group1) { var result = input.replace(/do while +(.*)/gi, function (input, group1) {
var condition = convertConditions(group1); var condition = convertConditions(group1);
return "\nwhile (" + condition + ") {\n"; return '\nwhile (' + condition + ') {\n';
}); });
result = result.replace(/^ *loop *$/gim, "}\n"); result = result.replace(/^ *loop *$/gim, '}\n');
return result; return result;
} }
exports.convertLoops = convertLoops;
function convertPRec (input) { function convertPRec (input) {
var result = input.replace(/(p_rec\("\S+?"\))/gi, "$1.Value"); var result = input.replace(/(p_rec\("\S+?"\))/gi, '$1.Value');
return result; return result;
} }
exports.convertPRec = convertPRec;
function convertPLan (input) { function convertPLan (input) {
var result = input.replace(/(l_\S+?)\(p_lan\)/gi, "$1[p_lan]"); var result = input.replace(/(l_\S+?)\(p_lan\)/gi, '$1[p_lan]');
return result; return result;
} }
exports.convertPLan = convertPLan;

View file

@ -0,0 +1,213 @@
import crypto2 from 'crypto';
import { spawn } from 'child_process';
import CDP from 'chrome-remote-interface';
import {} from 'child_process';
import net from 'net';
import { GBLog } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBServer } from '../../../../src/app.js';
import { DebuggerService } from '../DebuggerService.js';
import finalStream from 'final-stream';
const waitUntil = condition => {
if (condition()) {
return Promise.resolve();
}
return new Promise(resolve => {
const interval = setInterval(() => {
if (!condition()) {
return;
}
clearInterval(interval);
resolve(0);
}, 0);
});
};
export const createVm2Pool = ({ min, max, ...limits }) => {
limits = Object.assign(
{
cpu: 100,
memory: 2000,
time: 4000
},
limits
);
let limitError = null;
const ref = crypto2.randomBytes(20).toString('hex');
const kill = x => {
spawn('sh', ['-c', `pkill -9 -f ${ref}`]);
};
let stderrCache = '';
const run = async (code, scope) => {
const childProcess = spawn(
'cpulimit',
[
'-ql',
limits.cpu,
'--',
'node',
`${limits.debug ? '--inspect=' + limits.debuggerPort : ''}`,
`--experimental-fetch`,
`--max-old-space-size=${limits.memory}`,
limits.script,
ref
],
{ cwd: limits.cwd, shell: false }
);
childProcess.stdout.on('data', data => {
childProcess['socket'] = childProcess['socket'] || data.toString().trim();
});
childProcess.stderr.on('data', data => {
stderrCache = stderrCache + data.toString();
if (stderrCache.includes('failed: address already in use')) {
limitError = stderrCache;
kill(process);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = stderrCache;
} else if (
stderrCache.includes('FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory')
) {
limitError = 'code execution exceeed allowed memory';
kill(process);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Fail';
} else if (stderrCache.includes('Debugger attached.')) {
GBLog.info(`BASIC: General Bots Debugger attached to Node .gbdialog process for ${limits.botId}.`);
}
});
let socket = null;
await waitUntil(() => childProcess['socket']);
GBServer.globals.debuggers[limits.botId].childProcess = ref;
// Only attach if called by debugger/run.
if (GBServer.globals.debuggers[limits.botId]) {
const debug = async () => {
return new Promise((resolve, reject) => {
CDP(async client => {
const { Debugger, Runtime } = client;
try {
GBServer.globals.debuggers[limits.botId].client = client;
await client.Debugger.paused(async ({ callFrames, reason, hitBreakpoints }) => {
const frame = callFrames[0];
// Build variable list ignoring system variables of script.
const scopeObjectId = frame.scopeChain[2].object.objectId;
const variables = await Runtime.getProperties({ objectId: scopeObjectId });
let variablesText = '';
if (variables && variables.result) {
await CollectionUtil.asyncForEach(variables.result, async v => {
if (!DebuggerService.systemVariables.filter(x => x === v.name)[0]) {
if (v.value.value) {
variablesText = `${variablesText} \n ${v.name}: ${v.value.value}`;
}
}
});
}
GBServer.globals.debuggers[limits.botId].scope = variablesText;
GBLog.info(`BASIC: Breakpoint variables: ${variablesText}`); // (zero-based)
// Processes breakpoint hits.
if (hitBreakpoints.length >= 1) {
GBLog.info(`BASIC: Break at line ${frame.location.lineNumber + 1}`); // (zero-based)
GBServer.globals.debuggers[limits.botId].state = 2;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Break';
} else {
GBLog.verbose(`BASIC: Configuring breakpoints if any for ${limits.botId}...`);
// Waits for debugger and setup breakpoints.
await CollectionUtil.asyncForEach(GBServer.globals.debuggers[limits.botId].breaks, async brk => {
try {
const { breakpointId } = await client.Debugger.setBreakpoint({
location: {
scriptId: frame.location.scriptId,
lineNumber: brk
}
});
GBLog.info(`BASIC break defined ${breakpointId} for ${limits.botId}`);
} catch (error) {
GBLog.info(`BASIC error defining defining ${brk} for ${limits.botId}. ${error}`);
}
});
await client.Debugger.resume();
}
});
await client.Runtime.runIfWaitingForDebugger();
await client.Debugger.enable();
await client.Runtime.enable();
resolve(1);
} catch (err) {
GBLog.error(err);
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
}
}).on('error', err => {
console.error(err);
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
reject(err);
});
});
};
await debug();
}
socket = net.createConnection(childProcess['socket']);
socket.write(JSON.stringify({ code, scope }) + '\n');
const timer = setTimeout(() => {
limitError = 'code execution took too long and was killed';
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = limitError;
}, limits.time);
try {
let data = await finalStream(socket);
data = JSON.parse(data);
if (!data.length) {
return null;
}
if (data.error) {
throw new Error(data.error);
}
return data.result;
} catch (error) {
throw new Error(limitError || error);
} finally {
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
clearTimeout(timer);
}
};
return {
run
};
};

View file

@ -0,0 +1,59 @@
import { VMScript, NodeVM } from 'vm2';
import crypto1 from 'crypto';
import net1 from 'net';
const evaluate = async (script, scope) => {
const vm = new NodeVM({
allowAsync: true,
sandbox: {},
console: 'inherit',
wrapper: 'none',
require: {
builtin: ['stream', 'http', 'https', 'url', 'buffer', 'zlib', 'isomorphic-fetch', 'punycode', 'encoding'],
root: ['./'],
external: true,
context: 'sandbox'
}
});
const s = new VMScript(script, scope);
return await vm.run(script, scope);
};
const socketName = crypto1.randomBytes(20).toString('hex');
const server = net1.createServer(socket => {
const buffer = [];
const sync = async () => {
const request = buffer.join('').toString();
if (request.includes('\n')) {
try {
const { code, scope } = JSON.parse(request);
const result = await evaluate(code, {
...scope,
module: null
});
console.log(JSON.stringify({ result }));
socket.write(JSON.stringify({ result }) + '\n');
socket.end();
} catch (error) {
console.log(`BASIC: RUNTIME: ${error.message}, ${error.stack}`);
socket.write(JSON.stringify({ error: error.message }) + '\n');
socket.end();
}
}
};
socket.on('data', data => {
buffer.push(data);
sync();
});
});
server.on('listening', () => {
console.log(`/tmp/vm2-${socketName}.sock`);
});
server.listen(`/tmp/vm2-${socketName}.sock`);

View file

@ -1,4 +1,3 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i, affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,

View file

@ -38,7 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { ConsoleDirectLine } from './services/ConsoleDirectLine'; import { ConsoleDirectLine } from './services/ConsoleDirectLine.js';
/** /**
* Package for console.glib. * Package for console.glib.

View file

@ -1,5 +1,5 @@
const Swagger = require('swagger-client'); import Swagger from 'swagger-client';
const rp = require('request-promise'); import rp from 'request-promise';
import { GBLog, GBService } from 'botlib'; import { GBLog, GBService } from 'botlib';
/** /**

View file

@ -39,10 +39,10 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService'; import { GBConversationalService } from '../services/GBConversationalService.js';
/** /**
* Dialog for the bot explains about itself. * Dialog for the bot explains about itself.
*/ */
@ -59,8 +59,7 @@ export class BroadcastDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },

View file

@ -39,10 +39,10 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService'; import { GBConversationalService } from '../services/GBConversationalService.js';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
/** /**
* Dialog for the bot explains about itself. * Dialog for the bot explains about itself.
@ -55,12 +55,12 @@ export class LanguageDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog('/language', [ min.dialogs.add(
new WaterfallDialog('/language', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -68,11 +68,9 @@ export class LanguageDialog extends IGBDialog {
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, return await min.conversationalService.prompt(min, step, Messages[locale].which_language);
Messages[locale].which_language);
}, },
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const user = await min.userProfile.get(step.context, {}); const user = await min.userProfile.get(step.context, {});
@ -96,8 +94,10 @@ export class LanguageDialog extends IGBDialog {
const text = step.context.activity['originalText']; const text = step.context.activity['originalText'];
await CollectionUtil.asyncForEach(list, async item => { await CollectionUtil.asyncForEach(list, async item => {
if (GBConversationalService.kmpSearch(text.toLowerCase(), item.name.toLowerCase()) != -1 || if (
GBConversationalService.kmpSearch(text.toLowerCase(), item.code.toLowerCase()) != -1) { GBConversationalService.kmpSearch(text.toLowerCase(), item.name.toLowerCase()) != -1 ||
GBConversationalService.kmpSearch(text.toLowerCase(), item.code.toLowerCase()) != -1
) {
translatorLocale = item.code; translatorLocale = item.code;
} }
}); });
@ -106,11 +106,11 @@ export class LanguageDialog extends IGBDialog {
user.systemUser = await sec.updateUserLocale(user.systemUser.userId, translatorLocale); user.systemUser = await sec.updateUserLocale(user.systemUser.userId, translatorLocale);
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
await min.conversationalService.sendText(min, step, await min.conversationalService.sendText(min, step, Messages[locale].language_chosen);
Messages[locale].language_chosen);
await step.replaceDialog('/ask', { firstTime: true }); await step.replaceDialog('/ask', { firstTime: true });
} }
])); ])
);
} }
} }

View file

@ -39,10 +39,10 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBConversationalService } from '../services/GBConversationalService'; import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog for the bot explains about itself. * Dialog for the bot explains about itself.
*/ */
@ -54,12 +54,12 @@ export class SwitchBotDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog('/bot', [ min.dialogs.add(
new WaterfallDialog('/bot', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -79,6 +79,7 @@ export class SwitchBotDialog extends IGBDialog {
return await step.next(); return await step.next();
} }
])); ])
);
} }
} }

View file

@ -39,9 +39,9 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, IGBDialog } from 'botlib'; import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService'; import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog for Welcoming people. * Dialog for Welcoming people.
@ -54,29 +54,32 @@ export class WelcomeDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
min.dialogs.add(new WaterfallDialog('/', [ new WaterfallDialog('/', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
async step => { async step => {
if (
if (GBServer.globals.entryPointDialog !== null && GBServer.globals.entryPointDialog !== null &&
min.instance.botId === process.env.BOT_ID && min.instance.botId === process.env.BOT_ID &&
step.context.activity.channelId === 'webchat') { step.context.activity.channelId === 'webchat'
) {
return step.replaceDialog(GBServer.globals.entryPointDialog); return step.replaceDialog(GBServer.globals.entryPointDialog);
} }
const user = await min.userProfile.get(step.context, {}); const user = await min.userProfile.get(step.context, {});
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
if (!user.once && step.context.activity.channelId === 'webchat' if (
&& min.core.getParam<boolean>(min.instance, 'HelloGoodX', true) === "true") { !user.once &&
step.context.activity.channelId === 'webchat' &&
min.core.getParam<boolean>(min.instance, 'HelloGoodX', true) === 'true'
) {
user.once = true; user.once = true;
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
const a = new Date(); const a = new Date();
@ -104,6 +107,7 @@ export class WelcomeDialog extends IGBDialog {
return await step.next(); return await step.next();
} }
])); ])
);
} }
} }

View file

@ -39,8 +39,8 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBConversationalService } from '../services/GBConversationalService'; import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog for the bot explains about itself. * Dialog for the bot explains about itself.
*/ */
@ -52,12 +52,12 @@ export class WhoAmIDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(new WaterfallDialog('/whoAmI', [ min.dialogs.add(
new WaterfallDialog('/whoAmI', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -78,6 +78,7 @@ export class WhoAmIDialog extends IGBDialog {
return await step.next(); return await step.next();
} }
])); ])
);
} }
} }

View file

@ -38,12 +38,12 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { BroadcastDialog } from './dialogs/BroadcastDialog'; import { BroadcastDialog } from './dialogs/BroadcastDialog.js';
import { LanguageDialog } from './dialogs/LanguageDialog'; import { LanguageDialog } from './dialogs/LanguageDialog.js';
import { SwitchBotDialog } from './dialogs/SwitchBot'; import { SwitchBotDialog } from './dialogs/SwitchBot.js';
import { WelcomeDialog } from './dialogs/WelcomeDialog'; import { WelcomeDialog } from './dialogs/WelcomeDialog.js';
import { WhoAmIDialog } from './dialogs/WhoAmIDialog'; import { WhoAmIDialog } from './dialogs/WhoAmIDialog.js';
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel'; import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel.js';
/** /**
* Package for core.gbapp. * Package for core.gbapp.

View file

@ -47,4 +47,4 @@ import {
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript'; } from 'sequelize-typescript';
import { GuaribasInstance } from './GBModel'; import { GuaribasInstance } from './GBModel.js';

View file

@ -55,218 +55,219 @@ import { IGBInstance } from 'botlib';
* Base instance data for a bot. * Base instance data for a bot.
*/ */
@Table @Table
export class GuaribasInstance extends Model<GuaribasInstance> export class GuaribasInstance extends Model<GuaribasInstance> implements IGBInstance {
implements IGBInstance {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public botEndpoint: string; declare botEndpoint: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public whoAmIVideo: string; declare whoAmIVideo: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public botId: string; declare botId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public title: string; declare title: string;
@Column({ type: DataType.STRING(16) }) @Column({ type: DataType.STRING(16) })
public activationCode: string; declare activationCode: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public description: string; declare description: string;
@Column({ type: DataType.STRING(16) }) @Column({ type: DataType.STRING(16) })
public state: string; declare state: string;
public version: string; declare version: string;
@Column(DataType.STRING(64))
declare botKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public enabledAdmin: boolean; declare enabledAdmin: boolean;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public engineName: string; declare engineName: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public marketplaceId: string; declare marketplaceId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public textAnalyticsKey: string; declare textAnalyticsKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public textAnalyticsEndpoint: string; declare textAnalyticsEndpoint: string;
@Column({ type: DataType.STRING(64) }) @Column({ type: DataType.STRING(64) })
public translatorKey: string; declare translatorKey: string;
@Column({ type: DataType.STRING(128) }) @Column({ type: DataType.STRING(128) })
public translatorEndpoint: string; declare translatorEndpoint: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public marketplacePassword: string; declare marketplacePassword: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public webchatKey: string; declare webchatKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public authenticatorTenant: string; declare authenticatorTenant: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public authenticatorAuthorityHostUrl: string; declare authenticatorAuthorityHostUrl: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public cloudSubscriptionId: string; declare cloudSubscriptionId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public cloudUsername: string; declare cloudUsername: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public cloudPassword: string; declare cloudPassword: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public cloudLocation: string; declare cloudLocation: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public googleBotKey: string; declare googleBotKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public googleChatApiKey: string; declare googleChatApiKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public googleChatSubscriptionName: string; declare googleChatSubscriptionName: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public googleClientEmail: string; declare googleClientEmail: string;
@Column({ type: DataType.STRING(4000) }) @Column({ type: DataType.STRING(4000) })
public googlePrivateKey: string; declare googlePrivateKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public googleProjectId: string; declare googleProjectId: string;
@Column({ type: DataType.STRING(255) }) @Column({ type: DataType.STRING(255) })
facebookWorkplaceVerifyToken: string; declare facebookWorkplaceVerifyToken: string;
@Column({ type: DataType.STRING(255) }) @Column({ type: DataType.STRING(255) })
facebookWorkplaceAppSecret: string; declare facebookWorkplaceAppSecret: string;
@Column({ type: DataType.STRING(512) }) @Column({ type: DataType.STRING(512) })
facebookWorkplaceAccessToken: string; declare facebookWorkplaceAccessToken: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public whatsappBotKey: string; declare whatsappBotKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public whatsappServiceKey: string; declare whatsappServiceKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public whatsappServiceNumber: string; declare whatsappServiceNumber: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public whatsappServiceUrl: string; declare whatsappServiceUrl: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public smsKey: string; declare smsKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public smsSecret: string; declare smsSecret: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public smsServiceNumber: string; declare smsServiceNumber: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public speechKey: string; declare speechKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public speechEndpoint: string; declare speechEndpoint: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public spellcheckerKey: string; declare spellcheckerKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public spellcheckerEndpoint: string; declare spellcheckerEndpoint: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public theme: string; declare theme: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public ui: string; declare ui: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public kb: string; declare kb: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public nlpAppId: string; declare nlpAppId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public nlpKey: string; declare nlpKey: string;
@Column({ type: DataType.STRING(512) }) @Column({ type: DataType.STRING(512) })
public nlpEndpoint: string; declare nlpEndpoint: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public nlpAuthoringKey: string; declare nlpAuthoringKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public deploymentPaths: string; declare deploymentPaths: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public searchHost: string; declare searchHost: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public searchKey: string; declare searchKey: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public searchIndex: string; declare searchIndex: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public searchIndexer: string; declare searchIndexer: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storageUsername: string; declare storageUsername: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storagePassword: string; declare storagePassword: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storageName: string; declare storageName: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storageServer: string; declare storageServer: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storageDialect: string; declare storageDialect: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public storagePath: string; declare storagePath: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public adminPass: string; declare adminPass: string;
@Column(DataType.FLOAT) @Column(DataType.FLOAT)
public nlpVsSearch: number; // TODO: Remove field. declare nlpVsSearch: number; // TODO: Remove field.
@Column(DataType.FLOAT) @Column(DataType.FLOAT)
public searchScore: number; declare searchScore: number;
@Column(DataType.FLOAT) @Column(DataType.FLOAT)
public nlpScore: number; declare nlpScore: number;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
@Column(DataType.STRING(4000)) @Column(DataType.STRING(4000))
public params: string; declare params: string;
} }
/** /**
@ -277,28 +278,28 @@ export class GuaribasPackage extends Model<GuaribasPackage> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public packageId: number; declare packageId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public packageName: string; declare packageName: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; declare instance: GuaribasInstance;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
@Column({ type: DataType.STRING(512) }) @Column({ type: DataType.STRING(512) })
public custom: string; declare custom: string;
} }
/** /**
@ -309,18 +310,18 @@ export class GuaribasChannel extends Model<GuaribasChannel> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public channelId: number; declare channelId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public title: string; declare title: string;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
} }
/** /**
@ -332,72 +333,70 @@ export class GuaribasException extends Model<GuaribasException> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public exceptionId: number; declare exceptionId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public message: string; declare message: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; declare instance: GuaribasInstance;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
} }
@Table @Table
//tslint:disable-next-line:max-classes-per-file //tslint:disable-next-line:max-classes-per-file
export class GuaribasApplications extends Model<GuaribasApplications> { export class GuaribasApplications extends Model<GuaribasApplications> {
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public name: string; declare name: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; declare instance: GuaribasInstance;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
} }
@Table @Table
//tslint:disable-next-line:max-classes-per-file //tslint:disable-next-line:max-classes-per-file
export class GuaribasSchedule extends Model<GuaribasSchedule> { export class GuaribasSchedule extends Model<GuaribasSchedule> {
@Column(DataType.STRING(255))
declare name: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public name: string; declare schedule: string;
@Column(DataType.STRING(255))
public schedule: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; declare instance: GuaribasInstance;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
} }

View file

@ -33,6 +33,7 @@
'use strict'; 'use strict';
import { GBLog } from 'botlib'; import { GBLog } from 'botlib';
import * as en from 'dotenv-extended';
/** /**
* @fileoverview General Bots server core. * @fileoverview General Bots server core.
@ -43,7 +44,7 @@ import { GBLog } from 'botlib';
*/ */
export class GBConfigService { export class GBConfigService {
public static getBoolean (value: string): boolean { public static getBoolean (value: string): boolean {
return this.get(value) as unknown as boolean; return (this.get(value) as unknown) as boolean;
} }
public static getServerPort (): string { public static getServerPort (): string {
if (process.env.PORT) { if (process.env.PORT) {
@ -58,7 +59,7 @@ export class GBConfigService {
public static init (): any { public static init (): any {
try { try {
require('dotenv-extended').load({ en.load({
encoding: 'utf8', encoding: 'utf8',
silent: true, silent: true,
path: '.env', path: '.env',
@ -169,7 +170,6 @@ export class GBConfigService {
value = process.env[key]; value = process.env[key];
} }
return value; return value;
} }
} }

View file

@ -39,38 +39,36 @@
import { MessageFactory, RecognizerResult, TurnContext } from 'botbuilder'; import { MessageFactory, RecognizerResult, TurnContext } from 'botbuilder';
import { LuisRecognizer } from 'botbuilder-ai'; import { LuisRecognizer } from 'botbuilder-ai';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService } from 'botlib';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { MicrosoftAppCredentials } from 'botframework-connector'; import { MicrosoftAppCredentials } from 'botframework-connector';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService.js';
import { Messages } from '../strings';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework'; import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { GuaribasUser } from '../../security.gbapp/models'; import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { GBMinService } from './GBMinService'; import { GBMinService } from './GBMinService.js';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
const PasswordGenerator = require('strict-password-generator').default; import Fs from 'fs';
const Nexmo = require('nexmo'); import PasswordGenerator from 'strict-password-generator';
const { join } = require('path'); import Nexmo from 'nexmo';
const shell = require('any-shell-escape'); import { join } from 'path';
const { exec } = require('child_process'); import shell from 'any-shell-escape';
const prism = require('prism-media'); import { exec } from 'child_process';
const request = require('request-promise-native'); import prism from 'prism-media';
const fs = require('fs'); import request from 'request-promise-native';
const SpeechToTextV1 = require('ibm-watson/speech-to-text/v1'); import SpeechToTextV1 from 'ibm-watson/speech-to-text/v1.js';
const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1'); import TextToSpeechV1 from 'ibm-watson/text-to-speech/v1.js';
const { IamAuthenticator } = require('ibm-watson/auth'); import { IamAuthenticator } from 'ibm-watson/auth/index.js';
const marked = require('marked'); import * as marked from 'marked';
const { Translate } = require('@google-cloud/translate').v2; import Translate from '@google-cloud/translate';
/** /**
* Provides basic services for handling messages and dispatching to back-end * Provides basic services for handling messages and dispatching to back-end
* services like NLP or Search. * services like NLP or Search.
*/ */
export class GBConversationalService { export class GBConversationalService {
/** /**
* Reference to the core service. * Reference to the core service.
*/ */
@ -85,104 +83,191 @@ export class GBConversationalService {
} }
static defaultDiacriticsRemovalMap = [ static defaultDiacriticsRemovalMap = [
{ 'base': 'A', 'letters': '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F' }, {
{ 'base': 'AA', 'letters': '\uA732' }, base: 'A',
{ 'base': 'AE', 'letters': '\u00C6\u01FC\u01E2' }, letters:
{ 'base': 'AO', 'letters': '\uA734' }, '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F'
{ 'base': 'AU', 'letters': '\uA736' }, },
{ 'base': 'AV', 'letters': '\uA738\uA73A' }, { base: 'AA', letters: '\uA732' },
{ 'base': 'AY', 'letters': '\uA73C' }, { base: 'AE', letters: '\u00C6\u01FC\u01E2' },
{ 'base': 'B', 'letters': '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181' }, { base: 'AO', letters: '\uA734' },
{ 'base': 'C', 'letters': '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E' }, { base: 'AU', letters: '\uA736' },
{ 'base': 'D', 'letters': '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0' }, { base: 'AV', letters: '\uA738\uA73A' },
{ 'base': 'DZ', 'letters': '\u01F1\u01C4' }, { base: 'AY', letters: '\uA73C' },
{ 'base': 'Dz', 'letters': '\u01F2\u01C5' }, { base: 'B', letters: '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181' },
{ 'base': 'E', 'letters': '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E' }, { base: 'C', letters: '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E' },
{ 'base': 'F', 'letters': '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B' }, {
{ 'base': 'G', 'letters': '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E' }, base: 'D',
{ 'base': 'H', 'letters': '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D' }, letters: '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0'
{ 'base': 'I', 'letters': '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197' }, },
{ 'base': 'J', 'letters': '\u004A\u24BF\uFF2A\u0134\u0248' }, { base: 'DZ', letters: '\u01F1\u01C4' },
{ 'base': 'K', 'letters': '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2' }, { base: 'Dz', letters: '\u01F2\u01C5' },
{ 'base': 'L', 'letters': '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780' }, {
{ 'base': 'LJ', 'letters': '\u01C7' }, base: 'E',
{ 'base': 'Lj', 'letters': '\u01C8' }, letters:
{ 'base': 'M', 'letters': '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C' }, '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E'
{ 'base': 'N', 'letters': '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4' }, },
{ 'base': 'NJ', 'letters': '\u01CA' }, { base: 'F', letters: '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B' },
{ 'base': 'Nj', 'letters': '\u01CB' }, {
{ 'base': 'O', 'letters': '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C' }, base: 'G',
{ 'base': 'OI', 'letters': '\u01A2' }, letters: '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'
{ 'base': 'OO', 'letters': '\uA74E' }, },
{ 'base': 'OU', 'letters': '\u0222' }, { base: 'H', letters: '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D' },
{ 'base': 'OE', 'letters': '\u008C\u0152' }, {
{ 'base': 'oe', 'letters': '\u009C\u0153' }, base: 'I',
{ 'base': 'P', 'letters': '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754' }, letters:
{ 'base': 'Q', 'letters': '\u0051\u24C6\uFF31\uA756\uA758\u024A' }, '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'
{ 'base': 'R', 'letters': '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782' }, },
{ 'base': 'S', 'letters': '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784' }, { base: 'J', letters: '\u004A\u24BF\uFF2A\u0134\u0248' },
{ 'base': 'T', 'letters': '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786' }, { base: 'K', letters: '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2' },
{ 'base': 'TZ', 'letters': '\uA728' }, {
{ 'base': 'U', 'letters': '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244' }, base: 'L',
{ 'base': 'V', 'letters': '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245' }, letters:
{ 'base': 'VY', 'letters': '\uA760' }, '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'
{ 'base': 'W', 'letters': '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72' }, },
{ 'base': 'X', 'letters': '\u0058\u24CD\uFF38\u1E8A\u1E8C' }, { base: 'LJ', letters: '\u01C7' },
{ 'base': 'Y', 'letters': '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE' }, { base: 'Lj', letters: '\u01C8' },
{ 'base': 'Z', 'letters': '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762' }, { base: 'M', letters: '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C' },
{ 'base': 'a', 'letters': '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250' }, {
{ 'base': 'aa', 'letters': '\uA733' }, base: 'N',
{ 'base': 'ae', 'letters': '\u00E6\u01FD\u01E3' }, letters: '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'
{ 'base': 'ao', 'letters': '\uA735' }, },
{ 'base': 'au', 'letters': '\uA737' }, { base: 'NJ', letters: '\u01CA' },
{ 'base': 'av', 'letters': '\uA739\uA73B' }, { base: 'Nj', letters: '\u01CB' },
{ 'base': 'ay', 'letters': '\uA73D' }, {
{ 'base': 'b', 'letters': '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253' }, base: 'O',
{ 'base': 'c', 'letters': '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184' }, letters:
{ 'base': 'd', 'letters': '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A' }, '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C'
{ 'base': 'dz', 'letters': '\u01F3\u01C6' }, },
{ 'base': 'e', 'letters': '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD' }, { base: 'OI', letters: '\u01A2' },
{ 'base': 'f', 'letters': '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C' }, { base: 'OO', letters: '\uA74E' },
{ 'base': 'g', 'letters': '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F' }, { base: 'OU', letters: '\u0222' },
{ 'base': 'h', 'letters': '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265' }, { base: 'OE', letters: '\u008C\u0152' },
{ 'base': 'hv', 'letters': '\u0195' }, { base: 'oe', letters: '\u009C\u0153' },
{ 'base': 'i', 'letters': '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131' }, { base: 'P', letters: '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754' },
{ 'base': 'j', 'letters': '\u006A\u24D9\uFF4A\u0135\u01F0\u0249' }, { base: 'Q', letters: '\u0051\u24C6\uFF31\uA756\uA758\u024A' },
{ 'base': 'k', 'letters': '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3' }, {
{ 'base': 'l', 'letters': '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747' }, base: 'R',
{ 'base': 'lj', 'letters': '\u01C9' }, letters: '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'
{ 'base': 'm', 'letters': '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F' }, },
{ 'base': 'n', 'letters': '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5' }, {
{ 'base': 'nj', 'letters': '\u01CC' }, base: 'S',
{ 'base': 'o', 'letters': '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275' }, letters: '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'
{ 'base': 'oi', 'letters': '\u01A3' }, },
{ 'base': 'ou', 'letters': '\u0223' }, {
{ 'base': 'oo', 'letters': '\uA74F' }, base: 'T',
{ 'base': 'p', 'letters': '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755' }, letters: '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'
{ 'base': 'q', 'letters': '\u0071\u24E0\uFF51\u024B\uA757\uA759' }, },
{ 'base': 'r', 'letters': '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783' }, { base: 'TZ', letters: '\uA728' },
{ 'base': 's', 'letters': '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B' }, {
{ 'base': 't', 'letters': '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787' }, base: 'U',
{ 'base': 'tz', 'letters': '\uA729' }, letters:
{ 'base': 'u', 'letters': '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289' }, '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244'
{ 'base': 'v', 'letters': '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C' }, },
{ 'base': 'vy', 'letters': '\uA761' }, { base: 'V', letters: '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245' },
{ 'base': 'w', 'letters': '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73' }, { base: 'VY', letters: '\uA760' },
{ 'base': 'x', 'letters': '\u0078\u24E7\uFF58\u1E8B\u1E8D' }, { base: 'W', letters: '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72' },
{ 'base': 'y', 'letters': '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF' }, { base: 'X', letters: '\u0058\u24CD\uFF38\u1E8A\u1E8C' },
{ 'base': 'z', 'letters': '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763' } {
base: 'Y',
letters: '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'
},
{ base: 'Z', letters: '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762' },
{
base: 'a',
letters:
'\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250'
},
{ base: 'aa', letters: '\uA733' },
{ base: 'ae', letters: '\u00E6\u01FD\u01E3' },
{ base: 'ao', letters: '\uA735' },
{ base: 'au', letters: '\uA737' },
{ base: 'av', letters: '\uA739\uA73B' },
{ base: 'ay', letters: '\uA73D' },
{ base: 'b', letters: '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253' },
{ base: 'c', letters: '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184' },
{ base: 'd', letters: '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A' },
{ base: 'dz', letters: '\u01F3\u01C6' },
{
base: 'e',
letters:
'\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD'
},
{ base: 'f', letters: '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C' },
{
base: 'g',
letters: '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'
},
{
base: 'h',
letters: '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'
},
{ base: 'hv', letters: '\u0195' },
{
base: 'i',
letters:
'\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'
},
{ base: 'j', letters: '\u006A\u24D9\uFF4A\u0135\u01F0\u0249' },
{ base: 'k', letters: '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3' },
{
base: 'l',
letters:
'\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'
},
{ base: 'lj', letters: '\u01C9' },
{ base: 'm', letters: '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F' },
{
base: 'n',
letters: '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'
},
{ base: 'nj', letters: '\u01CC' },
{
base: 'o',
letters:
'\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275'
},
{ base: 'oi', letters: '\u01A3' },
{ base: 'ou', letters: '\u0223' },
{ base: 'oo', letters: '\uA74F' },
{ base: 'p', letters: '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755' },
{ base: 'q', letters: '\u0071\u24E0\uFF51\u024B\uA757\uA759' },
{
base: 'r',
letters: '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'
},
{
base: 's',
letters:
'\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'
},
{
base: 't',
letters: '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'
},
{ base: 'tz', letters: '\uA729' },
{
base: 'u',
letters:
'\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289'
},
{ base: 'v', letters: '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C' },
{ base: 'vy', letters: '\uA761' },
{ base: 'w', letters: '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73' },
{ base: 'x', letters: '\u0078\u24E7\uFF58\u1E8B\u1E8D' },
{
base: 'y',
letters: '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'
},
{ base: 'z', letters: '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763' }
]; ];
// "what?" version ... http://jsperf.com/diacritics/12 // "what?" version ... http://jsperf.com/diacritics/12
public static removeDiacriticsAndPunctuation (str) { public static removeDiacriticsAndPunctuation (str) {
str = GBConversationalService.removeDiacritics(str); str = GBConversationalService.removeDiacritics(str);
return str.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, ""); return str.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, '');
} }
public static removeDiacritics (str) { public static removeDiacritics (str) {
var diacriticsMap = {}; var diacriticsMap = {};
for (var i = 0; i < GBConversationalService.defaultDiacriticsRemovalMap.length; i++) { for (var i = 0; i < GBConversationalService.defaultDiacriticsRemovalMap.length; i++) {
var letters = GBConversationalService.defaultDiacriticsRemovalMap[i].letters; var letters = GBConversationalService.defaultDiacriticsRemovalMap[i].letters;
@ -196,8 +281,6 @@ export class GBConversationalService {
return str; return str;
} }
public getNewMobileCode () { public getNewMobileCode () {
const passwordGenerator = new PasswordGenerator(); const passwordGenerator = new PasswordGenerator();
const options = { const options = {
@ -228,7 +311,6 @@ export class GBConversationalService {
caption: string caption: string
): Promise<any> { ): Promise<any> {
if (step !== null) { if (step !== null) {
mobile = this.userMobile(step); mobile = this.userMobile(step);
if (mobile) { if (mobile) {
@ -255,10 +337,10 @@ export class GBConversationalService {
} }
public async sendEvent (min: GBMinInstance, step: GBDialogStep, name: string, value: Object): Promise<any> { public async sendEvent (min: GBMinInstance, step: GBDialogStep, name: string, value: Object): Promise<any> {
if (!this.userMobile(step) && if (!this.userMobile(step) && step.context.activity.channelId !== 'msteams') {
step.context.activity.channelId !== 'msteams') { GBLog.info(
GBLog.info(`Sending event ${name}:${typeof value === 'object' ? JSON.stringify(value) : `Sending event ${name}:${typeof value === 'object' ? JSON.stringify(value) : value ? value : ''} to client...`
value ? value : ''} to client...`); );
const msg = MessageFactory.text(''); const msg = MessageFactory.text('');
msg.value = value; msg.value = value;
msg.type = 'event'; msg.type = 'event';
@ -277,16 +359,16 @@ export class GBConversationalService {
method: 'POST', method: 'POST',
url: 'http://sms-api.megaconecta.com.br/mt', url: 'http://sms-api.megaconecta.com.br/mt',
headers: { headers: {
"content-type": "application/json", 'content-type': 'application/json',
"authorization": `Bearer ${min.instance.smsSecret}` authorization: `Bearer ${min.instance.smsSecret}`
}, },
body: [ body: [
{ {
"numero": `${mobile}`, numero: `${mobile}`,
"servico": "short", servico: 'short',
"mensagem": text, mensagem: text,
"parceiro_id": "", parceiro_id: '',
"codificacao": "0" codificacao: '0'
} }
], ],
json: true json: true
@ -301,15 +383,15 @@ export class GBConversationalService {
return Promise.reject(new Error(msg)); return Promise.reject(new Error(msg));
} }
} } else {
else { return new Promise(
return new Promise((resolve: any, reject: any): any => { (resolve: any, reject: any): any => {
const nexmo = new Nexmo({ const nexmo = new Nexmo({
apiKey: min.instance.smsKey, apiKey: min.instance.smsKey,
apiSecret: min.instance.smsSecret apiSecret: min.instance.smsSecret
}); });
// tslint:disable-next-line:no-unsafe-any // tslint:disable-next-line:no-unsafe-any
nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, (err, data) => { nexmo.message.sendSms(min.instance.smsServiceNumber, mobile, text, {}, (err, data) => {
const message = data.messages ? data.messages[0] : {}; const message = data.messages ? data.messages[0] : {};
if (err || message['error-text']) { if (err || message['error-text']) {
GBLog.error(`BASIC: error sending SMS to ${mobile}: ${message['error-text']}`); GBLog.error(`BASIC: error sending SMS to ${mobile}: ${message['error-text']}`);
@ -318,7 +400,8 @@ export class GBConversationalService {
resolve(data); resolve(data);
} }
}); });
}); }
);
} }
} }
@ -327,13 +410,11 @@ export class GBConversationalService {
await min.whatsAppDirectLine.sendToDevice(mobile, message, conversationId); await min.whatsAppDirectLine.sendToDevice(mobile, message, conversationId);
} }
public static async getAudioBufferFromText (text): Promise<string> { public static async getAudioBufferFromText (text): Promise<string> {
return new Promise<string>(async (resolve, reject) => { return new Promise<string>(async (resolve, reject) => {
const name = GBAdminService.getRndReadableIdentifier(); const name = GBAdminService.getRndReadableIdentifier();
try { try {
const textToSpeech = new TextToSpeechV1({ const textToSpeech = new TextToSpeechV1({
authenticator: new IamAuthenticator({ apikey: process.env.WATSON_TTS_KEY }), authenticator: new IamAuthenticator({ apikey: process.env.WATSON_TTS_KEY }),
url: process.env.WATSON_TTS_URL url: process.env.WATSON_TTS_URL
@ -350,21 +431,21 @@ export class GBConversationalService {
let res = await textToSpeech.synthesize(params); let res = await textToSpeech.synthesize(params);
const waveFilename = `work/tmp${name}.pcm`; const waveFilename = `work/tmp${name}.pcm`;
let audio = ''; let audio = await textToSpeech.repairWavHeaderStream(res.result as any);
audio = await textToSpeech.repairWavHeaderStream(res.result); Fs.writeFileSync(waveFilename, audio);
fs.writeFileSync(waveFilename, audio);
const oggFilenameOnly = `tmp${name}.ogg`; const oggFilenameOnly = `tmp${name}.ogg`;
const oggFilename = `work/${oggFilenameOnly}`; const oggFilename = `work/${oggFilenameOnly}`;
const output = fs.createWriteStream(oggFilename); const output = Fs.createWriteStream(oggFilename);
const transcoder = new prism.FFmpeg({ const transcoder = new prism.FFmpeg({
args: ['-analyzeduration', '0', '-loglevel', '0', '-f', 'opus', '-ar', '16000', '-ac', '1'] args: ['-analyzeduration', '0', '-loglevel', '0', '-f', 'opus', '-ar', '16000', '-ac', '1']
}); });
fs.createReadStream(waveFilename).pipe(transcoder).pipe(output); Fs.createReadStream(waveFilename)
.pipe(transcoder)
.pipe(output);
let url = urlJoin(GBServer.globals.publicAddress, 'audios', oggFilenameOnly); let url = urlJoin(GBServer.globals.publicAddress, 'audios', oggFilenameOnly);
resolve(url); resolve(url);
} catch (error) { } catch (error) {
reject(error); reject(error);
} }
@ -383,7 +464,7 @@ export class GBConversationalService {
const dest = `work/tmp${name}.wav`; const dest = `work/tmp${name}.wav`;
const src = `work/tmp${name}.ogg`; const src = `work/tmp${name}.ogg`;
fs.writeFileSync(src, oggFile.read()); Fs.writeFileSync(src, oggFile.read());
const makeMp3 = shell([ const makeMp3 = shell([
'node_modules/ffmpeg-static/ffmpeg.exe', 'node_modules/ffmpeg-static/ffmpeg.exe',
@ -406,7 +487,7 @@ export class GBConversationalService {
GBLog.error(error); GBLog.error(error);
return Promise.reject(error); return Promise.reject(error);
} else { } else {
let data = fs.readFileSync(dest); let data = Fs.readFileSync(dest);
const speechToText = new SpeechToTextV1({ const speechToText = new SpeechToTextV1({
authenticator: new IamAuthenticator({ apikey: process.env.WATSON_STT_KEY }), authenticator: new IamAuthenticator({ apikey: process.env.WATSON_STT_KEY }),
@ -439,13 +520,7 @@ export class GBConversationalService {
}); });
} }
public async playMarkdown( public async playMarkdown (min: GBMinInstance, answer: string, channel: string, step: GBDialogStep, mobile: string) {
min: GBMinInstance,
answer: string,
channel: string,
step: GBDialogStep,
mobile: string
) {
const user = step ? await min.userProfile.get(step.context, {}) : null; const user = step ? await min.userProfile.get(step.context, {}) : null;
let text = answer; let text = answer;
@ -461,15 +536,22 @@ export class GBConversationalService {
GBLog.verbose(`Translated text(playMarkdown): ${text}.`); GBLog.verbose(`Translated text(playMarkdown): ${text}.`);
} }
var renderer = new marked.Renderer(); var renderer = new marked.marked.Renderer();
renderer.oldImage = renderer.image; renderer.oldImage = renderer.image;
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
var videos = ['webm', 'mp4', 'mov']; var videos = ['webm', 'mp4', 'mov'];
var filetype = href.split('.').pop(); var filetype = href.split('.').pop();
if (videos.indexOf(filetype) > -1) { if (videos.indexOf(filetype) > -1) {
var out = '<video autoplay loop alt="' + text + '">' var out =
+ ' <source src="' + href + '" type="video/' + filetype + '">' '<video autoplay loop alt="' +
+ '</video>' text +
'">' +
' <source src="' +
href +
'" type="video/' +
filetype +
'">' +
'</video>';
return out; return out;
} else { } else {
return renderer.oldImage(href, title, text); return renderer.oldImage(href, title, text);
@ -494,8 +576,7 @@ export class GBConversationalService {
text = text.replace('! [', '![').replace('] (', ']('); text = text.replace('! [', '![').replace('] (', '](');
text = text.replace(`[[embed url=`, process.env.BOT_URL + '/').replace(']]', ''); // TODO: Improve it. text = text.replace(`[[embed url=`, process.env.BOT_URL + '/').replace(']]', ''); // TODO: Improve it.
text = text.replace(`](kb`, "](" + process.env.BOT_URL + '/kb'); // TODO: Improve it. text = text.replace(`](kb`, '](' + process.env.BOT_URL + '/kb'); // TODO: Improve it.
if (mobile) { if (mobile) {
await this.sendMarkdownToMobile(min, step, mobile, text); await this.sendMarkdownToMobile(min, step, mobile, text);
@ -508,12 +589,7 @@ export class GBConversationalService {
} }
} }
private async sendMarkdownToWeb( private async sendMarkdownToWeb (min, step: GBDialogStep, html: string, answer: string) {
min,
step: GBDialogStep,
html: string,
answer: string
) {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
html = html.replace(/src\=\"kb\//gi, `src=\"../kb/`); html = html.replace(/src\=\"kb\//gi, `src=\"../kb/`);
@ -523,12 +599,11 @@ export class GBConversationalService {
content: html, content: html,
answer: answer, answer: answer,
prevId: 0, // TODO: answer.prevId, prevId: 0, // TODO: answer.prevId,
nextId: 0, // TODO: answer.nextId nextId: 0 // TODO: answer.nextId
} }
}); });
} }
// tslint:enable:no-unsafe-any // tslint:enable:no-unsafe-any
public async sendMarkdownToMobile (min: GBMinInstance, step: GBDialogStep, mobile: string, text: string) { public async sendMarkdownToMobile (min: GBMinInstance, step: GBDialogStep, mobile: string, text: string) {
@ -701,7 +776,6 @@ export class GBConversationalService {
} }
} }
// TODO: Update botlib. // TODO: Update botlib.
public async routeNLP (step: GBDialogStep, min: GBMinInstance, text: string): Promise<boolean> { public async routeNLP (step: GBDialogStep, min: GBMinInstance, text: string): Promise<boolean> {
return false; return false;
@ -713,9 +787,9 @@ export class GBConversationalService {
text = text.toLowerCase(); text = text.toLowerCase();
text = text.replace('who´s', 'who is'); text = text.replace('who´s', 'who is');
text = text.replace('who\'s', 'who is'); text = text.replace("who's", 'who is');
text = text.replace('what´s', 'what is'); text = text.replace('what´s', 'what is');
text = text.replace('what\'s', 'what is'); text = text.replace("what's", 'what is');
text = text.replace('?', ' '); text = text.replace('?', ' ');
text = text.replace('¿', ' '); text = text.replace('¿', ' ');
text = text.replace('!', ' '); text = text.replace('!', ' ');
@ -734,10 +808,14 @@ export class GBConversationalService {
try { try {
const saved = step.context.activity.text; const saved = step.context.activity.text;
step.context.activity.text = text; step.context.activity.text = text;
nlp = await model.recognize(step.context, {}, {}, { IncludeAllIntents: false, IncludeInstanceData: false, includeAPIResults: true }); nlp = await model.recognize(
step.context,
{},
{},
{ IncludeAllIntents: false, IncludeInstanceData: false, includeAPIResults: true }
);
step.context.activity.text = saved; step.context.activity.text = saved;
} catch (error) { } catch (error) {
// tslint:disable:no-unsafe-any // tslint:disable:no-unsafe-any
if (error.statusCode === 404 || error.statusCode === 400) { if (error.statusCode === 404 || error.statusCode === 400) {
GBLog.warn('NLP application still not publish and there are no other options for answering.'); GBLog.warn('NLP application still not publish and there are no other options for answering.');
@ -755,8 +833,11 @@ export class GBConversationalService {
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
let nlpActive = false; let nlpActive = false;
let score = 0; let score = 0;
const instanceScore = min.core.getParam(min.instance, 'NLP Score', const instanceScore = min.core.getParam(
min.instance.nlpScore ? min.instance.nlpScore : minBoot.instance.nlpScore); min.instance,
'NLP Score',
min.instance.nlpScore ? min.instance.nlpScore : minBoot.instance.nlpScore
);
Object.keys(nlp.intents).forEach(name => { Object.keys(nlp.intents).forEach(name => {
score = nlp.intents[name].score; score = nlp.intents[name].score;
@ -775,7 +856,9 @@ export class GBConversationalService {
} }
GBLog.info( GBLog.info(
`NLP called: ${intent}, entities: ${nlp.entities.length}, score: ${score} > required (nlpScore): ${instanceScore}` `NLP called: ${intent}, entities: ${
nlp.entities.length
}, score: ${score} > required (nlpScore): ${instanceScore}`
); );
step.activeDialog.state.options.entities = nlp.entities; step.activeDialog.state.options.entities = nlp.entities;
@ -784,7 +867,7 @@ export class GBConversationalService {
if (nlp.entities) { if (nlp.entities) {
await CollectionUtil.asyncForEach(Object.keys(nlp.entities), async key => { await CollectionUtil.asyncForEach(Object.keys(nlp.entities), async key => {
if (key !== "$instance") { if (key !== '$instance') {
let entity = nlp.entities[key]; let entity = nlp.entities[key];
if (Array.isArray(entity[0])) { if (Array.isArray(entity[0])) {
nlp.entities[key] = entity.slice(1); nlp.entities[key] = entity.slice(1);
@ -796,9 +879,7 @@ export class GBConversationalService {
return await step.replaceDialog(`/${intent}`, step.activeDialog.state.options); return await step.replaceDialog(`/${intent}`, step.activeDialog.state.options);
} }
GBLog.info( GBLog.info(`NLP NOT called: score: ${score} > required (nlpScore): ${instanceScore}`);
`NLP NOT called: score: ${score} > required (nlpScore): ${instanceScore}`
);
return null; return null;
} }
@ -818,8 +899,7 @@ export class GBConversationalService {
} }
public async spellCheck (min: GBMinInstance, text: string): Promise<string> { public async spellCheck (min: GBMinInstance, text: string): Promise<string> {
const key = const key = min.core.getParam<string>(min.instance, 'spellcheckerKey', null);
min.core.getParam<string>(min.instance, 'spellcheckerKey', null);
if (key) { if (key) {
text = text.charAt(0).toUpperCase() + text.slice(1); text = text.charAt(0).toUpperCase() + text.slice(1);
@ -844,7 +924,11 @@ export class GBConversationalService {
const endPoint = min.core.getParam<string>(min.instance, 'translatorEndpoint', null); const endPoint = min.core.getParam<string>(min.instance, 'translatorEndpoint', null);
const key = min.core.getParam<string>(min.instance, 'translatorKey', null); const key = min.core.getParam<string>(min.instance, 'translatorKey', null);
if ((endPoint === null && !min.instance.googleProjectId) || !translatorEnabled() || process.env.TRANSLATOR_DISABLED === 'true') { if (
(endPoint === null && !min.instance.googleProjectId) ||
!translatorEnabled() ||
process.env.TRANSLATOR_DISABLED === 'true'
) {
return text; return text;
} }
@ -859,13 +943,15 @@ export class GBConversationalService {
if (min.instance.googleProjectId) { if (min.instance.googleProjectId) {
// Instantiates a client // Instantiates a client
const translate = new Translate({ const translate = new Translate.v2.Translate({
projectId: min.instance.googleProjectId, projectId: min.instance.googleProjectId,
credentials: { client_email: min.instance.googleClientEmail, private_key: min.instance.googlePrivateKey.replace(/\\n/gm, '\n') } credentials: {
client_email: min.instance.googleClientEmail,
private_key: min.instance.googlePrivateKey.replace(/\\n/gm, '\n')
}
}); });
try { try {
const [translation] = await translate.translate(text, language); const [translation] = await translate.translate(text, language);
return translation; return translation;
@ -874,10 +960,7 @@ export class GBConversationalService {
return Promise.reject(new Error(msg)); return Promise.reject(new Error(msg));
} }
} else {
}
else {
let options = { let options = {
method: 'POST', method: 'POST',
baseUrl: endPoint, baseUrl: endPoint,
@ -901,7 +984,6 @@ export class GBConversationalService {
}; };
try { try {
const results = await request(options); const results = await request(options);
return results[0].translations[0].text; return results[0].translations[0].text;
@ -917,7 +999,7 @@ export class GBConversationalService {
const user = await min.userProfile.get(step.context, {}); const user = await min.userProfile.get(step.context, {});
const systemUser = user.systemUser; const systemUser = user.systemUser;
if (text && text !== "") { if (text && text !== '') {
text = await min.conversationalService.translate( text = await min.conversationalService.translate(
min, min,
text, text,
@ -927,14 +1009,11 @@ export class GBConversationalService {
); );
GBLog.verbose(`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', {});
} } else {
else {
return await step.prompt('textPrompt', text ? text : {}); return await step.prompt('textPrompt', text ? text : {});
} }
} }
public async sendText (min: GBMinInstance, step, text) { public async sendText (min: GBMinInstance, step, text) {
@ -968,9 +1047,7 @@ export class GBConversationalService {
text = await min.conversationalService.translate( text = await min.conversationalService.translate(
min, min,
text, text,
locale locale ? locale : min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
? locale
: min.core.getParam<string>(min.instance, 'Locale', GBConfigService.get('LOCALE'))
); );
if (keepTextList) { if (keepTextList) {
@ -991,15 +1068,12 @@ export class GBConversationalService {
analytics.createMessage(min.instance.instanceId, user.conversation, null, text); analytics.createMessage(min.instance.instanceId, user.conversation, null, text);
if (!isNaN(member.id) && !member.id.startsWith('1000')) { if (!isNaN(member.id) && !member.id.startsWith('1000')) {
const to = step.context.activity.group ? step.context.activity.group : member.id; const to = step.context.activity.group ? step.context.activity.group : member.id;
await min.whatsAppDirectLine.sendToDevice(to, text, step.context.activity.conversation.id); await min.whatsAppDirectLine.sendToDevice(to, text, step.context.activity.conversation.id);
} else { } else {
await step.context.sendActivity(text); await step.context.sendActivity(text);
} }
} }
public async broadcast (min: GBMinInstance, message: string) { public async broadcast (min: GBMinInstance, message: string) {
@ -1027,21 +1101,18 @@ export class GBConversationalService {
* Sends a message in a user with an already started conversation (got ConversationReference set) * Sends a message in a user with an already started conversation (got ConversationReference set)
*/ */
public async sendOnConversation (min: GBMinInstance, user: GuaribasUser, message: string) { public async sendOnConversation (min: GBMinInstance, user: GuaribasUser, message: string) {
if (user.conversationReference.startsWith('spaces')) { if (user.conversationReference.startsWith('spaces')) {
await min['googleDirectLine'].sendToDevice(user.userSystemId, null, user.conversationReference, message); await min['googleDirectLine'].sendToDevice(user.userSystemId, null, user.conversationReference, message);
} } else {
else {
const ref = JSON.parse(user.conversationReference); const ref = JSON.parse(user.conversationReference);
MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl); MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl);
try { try {
await min.bot['continueConversation'](ref, async (t1) => { 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) { } catch (error) {
console.log(error); console.log(error);
} }
@ -1051,32 +1122,26 @@ export class GBConversationalService {
public static kmpSearch (pattern, text) { public static kmpSearch (pattern, text) {
pattern = pattern.toLowerCase(); pattern = pattern.toLowerCase();
text = text.toLowerCase(); text = text.toLowerCase();
if (pattern.length == 0) if (pattern.length == 0) return 0; // Immediate match
return 0; // Immediate match
// Compute longest suffix-prefix table // Compute longest suffix-prefix table
var lsp = [0]; // Base case var lsp = [0]; // Base case
for (var i = 1; i < pattern.length; i++) { for (var i = 1; i < pattern.length; i++) {
var j = lsp[i - 1]; // Start by assuming we're extending the previous LSP var j = lsp[i - 1]; // Start by assuming we're extending the previous LSP
while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) j = lsp[j - 1];
j = lsp[j - 1]; if (pattern.charAt(i) == pattern.charAt(j)) j++;
if (pattern.charAt(i) == pattern.charAt(j))
j++;
lsp.push(j); lsp.push(j);
} }
// Walk through text string // Walk through text string
var j = 0; // Number of chars matched in pattern var j = 0; // Number of chars matched in pattern
for (var i = 0; i < text.length; i++) { for (var i = 0; i < text.length; i++) {
while (j > 0 && text.charAt(i) != pattern.charAt(j)) while (j > 0 && text.charAt(i) != pattern.charAt(j)) j = lsp[j - 1]; // Fall back in the pattern
j = lsp[j - 1]; // Fall back in the pattern
if (text.charAt(i) == pattern.charAt(j)) { if (text.charAt(i) == pattern.charAt(j)) {
j++; // Next char matched, increment position j++; // Next char matched, increment position
if (j == pattern.length) if (j == pattern.length) return i - (j - 1);
return i - (j - 1);
} }
} }
return -1; // Not found return -1; // Not found
} }
} }

View file

@ -37,30 +37,29 @@
'use strict'; 'use strict';
import { GBLog, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib'; import { GBLog, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib';
import * as fs from 'fs'; import * as Fs from 'fs';
import { Sequelize, SequelizeOptions } from 'sequelize-typescript'; import { Sequelize, SequelizeOptions } from 'sequelize-typescript';
import { Op, Dialect } from 'sequelize'; import { Op, Dialect } from 'sequelize';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBAdminPackage } from '../../admin.gbapp/index'; import { GBAdminPackage } from '../../admin.gbapp/index.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBAnalyticsPackage } from '../../analytics.gblib'; import { GBAnalyticsPackage } from '../../analytics.gblib/index.js';
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog'; import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog.js';
import { GBCorePackage } from '../../core.gbapp'; import { GBCorePackage } from '../../core.gbapp/index.js';
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp'; import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp/index.js';
import { GBKBPackage } from '../../kb.gbapp'; import { GBKBPackage } from '../../kb.gbapp/index.js';
import { GBSecurityPackage } from '../../security.gbapp'; import { GBSecurityPackage } from '../../security.gbapp/index.js';
import { GBWhatsappPackage } from '../../whatsapp.gblib/index'; import { GBWhatsappPackage } from '../../whatsapp.gblib/index.js';
import { GuaribasInstance } from '../models/GBModel'; import { GuaribasInstance } from '../models/GBModel.js';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService.js';
import { GBAzureDeployerPackage } from '../../azuredeployer.gbapp'; import { GBAzureDeployerPackage } from '../../azuredeployer.gbapp/index.js';
import { GBSharePointPackage } from '../../sharepoint.gblib'; import { GBSharePointPackage } from '../../sharepoint.gblib/index.js';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBBasicPackage } from '../../basic.gblib'; import { GBBasicPackage } from '../../basic.gblib/index.js';
import { GBGoogleChatPackage } from '../../google-chat.gblib'; import { GBGoogleChatPackage } from '../../google-chat.gblib/index.js';
import { GBHubSpotPackage } from '../../hubspot.gblib'; import { GBHubSpotPackage } from '../../hubspot.gblib/index.js';
import open from 'open';
const opn = require('opn'); import ngrok from 'ngrok';
const cron = require('node-cron');
/** /**
* GBCoreService contains main logic for handling storage services related * GBCoreService contains main logic for handling storage services related
@ -193,7 +192,6 @@ export class GBCoreService implements IGBCoreService {
} }
} }
/** /**
* Syncronizes structure between model and tables in storage. * Syncronizes structure between model and tables in storage.
*/ */
@ -288,10 +286,9 @@ STORAGE_SYNC_ALTER=true
ENDPOINT_UPDATE=true ENDPOINT_UPDATE=true
`; `;
fs.writeFileSync('.env', env); Fs.writeFileSync('.env', env);
} }
/** /**
* Certifies that network servers will reach back the development machine * Certifies that network servers will reach back the development machine
* when calling back from web services. This ensures that reverse proxy is * when calling back from web services. This ensures that reverse proxy is
@ -299,9 +296,8 @@ ENDPOINT_UPDATE=true
*/ */
public async ensureProxy (port): Promise<string> { public async ensureProxy (port): Promise<string> {
try { try {
if (fs.existsSync('node_modules/ngrok/bin/ngrok.exe') || fs.existsSync('node_modules/ngrok/bin/ngrok')) { if (Fs.existsSync('node_modules/ngrok/bin/ngrok.exe') || Fs.existsSync('node_modules/ngrok/bin/ngrok')) {
const ngrok = require('ngrok'); return await ngrok.connect({ port: port });
return await ngrok.connect({ port: port }, 10);
} else { } else {
GBLog.warn('ngrok executable not found (only tested on Windows). Check installation or node_modules folder.'); GBLog.warn('ngrok executable not found (only tested on Windows). Check installation or node_modules folder.');
@ -525,7 +521,7 @@ ENDPOINT_UPDATE=true
*/ */
public openBrowserInDevelopment () { public openBrowserInDevelopment () {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
opn('http://localhost:4242'); open('http://localhost:4242');
} }
} }
@ -551,12 +547,17 @@ ENDPOINT_UPDATE=true
if (matches !== null) { if (matches !== null) {
const table = matches[1]; const table = matches[1];
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/; const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
sql = sql.replace(re2, (match: string, ...args: any[]): string => { sql = sql.replace(
re2,
(match: string, ...args: any[]): string => {
return `CONSTRAINT [${table}_pk] ${match}`; return `CONSTRAINT [${table}_pk] ${match}`;
}); }
);
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g; const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g; const re4 = /\[([^\]]*)\]/g;
sql = sql.replace(re3, (match: string, ...args: any[]): string => { sql = sql.replace(
re3,
(match: string, ...args: any[]): string => {
const fkcols = args[0]; const fkcols = args[0];
let fkname = table; let fkname = table;
let matches2 = re4.exec(fkcols); let matches2 = re4.exec(fkcols);
@ -566,7 +567,8 @@ ENDPOINT_UPDATE=true
} }
return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`; return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
}); }
);
} }
return sql; return sql;
@ -588,7 +590,9 @@ ENDPOINT_UPDATE=true
const table = matches[1]; const table = matches[1];
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g; const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re3 = /\[([^\]]*)\]/g; const re3 = /\[([^\]]*)\]/g;
sql = sql.replace(re2, (match: string, ...args: any[]): string => { sql = sql.replace(
re2,
(match: string, ...args: any[]): string => {
const fkcols = args[2]; const fkcols = args[2];
let fkname = table; let fkname = table;
let matches2 = re3.exec(fkcols); let matches2 = re3.exec(fkcols);
@ -598,7 +602,8 @@ ENDPOINT_UPDATE=true
} }
return `${args[0] ? args[0] : ''}CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`; return `${args[0] ? args[0] : ''}CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
}); }
);
} }
return sql; return sql;
@ -648,5 +653,4 @@ ENDPOINT_UPDATE=true
return value; return value;
} }
} }

View file

@ -36,34 +36,32 @@
'use strict'; 'use strict';
const Path = require('path'); import Path from 'path';
const urlJoin = require('url-join'); import express from 'express';
const Fs = require('fs'); import child_process from 'child_process';
const express = require('express'); import rimraf from 'rimraf';
const child_process = require('child_process'); import request from 'request-promise-native';
const rimraf = require('rimraf'); import vhost from 'vhost';
const request = require('request-promise-native'); import urlJoin from 'url-join';
const vhost = require('vhost') import Fs from 'fs';
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBDeployer, IGBInstance, IGBPackage } from 'botlib'; import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBDeployer, IGBInstance, IGBPackage } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework'; import { AzureSearch } from 'pragmatismo-io-framework';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { GuaribasPackage } from '../models/GBModel'; import { GuaribasPackage } from '../models/GBModel.js';
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from './../../admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService'; import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { KBService } from './../../kb.gbapp/services/KBService'; import { KBService } from './../../kb.gbapp/services/KBService.js';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService.js';
import { GBImporter } from './GBImporterService'; import { GBImporter } from './GBImporterService.js';
import { TeamsService } from '../../teams.gblib/services/TeamsService'; import { TeamsService } from '../../teams.gblib/services/TeamsService.js';
const MicrosoftGraph = require('@microsoft/microsoft-graph-client'); import MicrosoftGraph from '@microsoft/microsoft-graph-client';
/** /**
* Deployer service for bots, themes, ai and more. * Deployer service for bots, themes, ai and more.
*/ */
export class GBDeployer implements IGBDeployer { export class GBDeployer implements IGBDeployer {
/** /**
* Where should deployer look into for general packages. * Where should deployer look into for general packages.
*/ */
@ -97,7 +95,9 @@ export class GBDeployer implements IGBDeployer {
* use to database like the Indexer (Azure Search). * use to database like the Indexer (Azure Search).
*/ */
public static getConnectionStringFromInstance (instance: IGBInstance) { public static getConnectionStringFromInstance (instance: IGBInstance) {
return `Server=tcp:${instance.storageServer},1433;Database=${instance.storageName};User ID=${instance.storageUsername};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`; return `Server=tcp:${instance.storageServer},1433;Database=${instance.storageName};User ID=${
instance.storageUsername
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
} }
/** /**
@ -114,14 +114,13 @@ export class GBDeployer implements IGBDeployer {
} }
}); });
const baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}`; const baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}`;
return [baseUrl, client]; return { baseUrl, client };
} }
/** /**
* Performs package deployment in all .gbai or default. * Performs package deployment in all .gbai or default.
*/ */
public async deployPackages (core: IGBCoreService, server: any, appPackages: IGBPackage[]) { public async deployPackages (core: IGBCoreService, server: any, appPackages: IGBPackage[]) {
// Builds lists of paths to search for packages. // Builds lists of paths to search for packages.
let paths = [urlJoin(process.env.PWD, GBDeployer.deployFolder), urlJoin(process.env.PWD, GBDeployer.workFolder)]; let paths = [urlJoin(process.env.PWD, GBDeployer.deployFolder), urlJoin(process.env.PWD, GBDeployer.workFolder)];
@ -134,7 +133,6 @@ export class GBDeployer implements IGBDeployer {
const generalPackages: string[] = []; const generalPackages: string[] = [];
async function scanPackageDirectory (path) { async function scanPackageDirectory (path) {
// Gets all directories. // Gets all directories.
const isDirectory = source => Fs.lstatSync(source).isDirectory(); const isDirectory = source => Fs.lstatSync(source).isDirectory();
@ -144,7 +142,6 @@ export class GBDeployer implements IGBDeployer {
.filter(isDirectory); .filter(isDirectory);
const dirs = getDirectories(path); const dirs = getDirectories(path);
await CollectionUtil.asyncForEach(dirs, async element => { await CollectionUtil.asyncForEach(dirs, async element => {
// For each folder, checks its extensions looking for valid packages. // For each folder, checks its extensions looking for valid packages.
if (element === '.') { if (element === '.') {
@ -154,8 +151,9 @@ export class GBDeployer implements IGBDeployer {
// Skips what does not need to be loaded. // Skips what does not need to be loaded.
if (process.env.GBAPP_SKIP && (process.env.GBAPP_SKIP.toLowerCase().indexOf(name) !== -1 if (
|| process.env.GBAPP_SKIP === 'true') process.env.GBAPP_SKIP &&
(process.env.GBAPP_SKIP.toLowerCase().indexOf(name) !== -1 || process.env.GBAPP_SKIP === 'true')
) { ) {
return; return;
} }
@ -204,7 +202,6 @@ export class GBDeployer implements IGBDeployer {
* Deploys a new blank bot to the database, cognitive services and other services. * Deploys a new blank bot to the database, cognitive services and other services.
*/ */
public async deployBlankBot (botId: string, mobile: string, email: string) { public async deployBlankBot (botId: string, mobile: string, email: string) {
// Creates a new row on the GuaribasInstance table. // Creates a new row on the GuaribasInstance table.
const instance = await this.importer.createBotInstance(botId); const instance = await this.importer.createBotInstance(botId);
@ -229,9 +226,9 @@ export class GBDeployer implements IGBDeployer {
instance.state = 'active'; instance.state = 'active';
instance.nlpScore = 0.8; instance.nlpScore = 0.8;
instance.searchScore = 0.45; instance.searchScore = 0.45;
instance.whatsappServiceKey = bootInstance.whatsappServiceKey; instance.whatsappServiceKey = null;
instance.whatsappServiceNumber = bootInstance.whatsappServiceNumber; instance.whatsappServiceNumber = null;
instance.whatsappServiceUrl = bootInstance.whatsappServiceUrl; instance.whatsappServiceUrl = null;
instance.params = JSON.stringify({ 'Can Publish': mobile, 'Admin Notify E-mail': email }); instance.params = JSON.stringify({ 'Can Publish': mobile, 'Admin Notify E-mail': email });
// Saves bot information to the store. // Saves bot information to the store.
@ -256,7 +253,6 @@ export class GBDeployer implements IGBDeployer {
* Performs all tasks of deploying a new bot on the cloud. * Performs all tasks of deploying a new bot on the cloud.
*/ */
public async deployBotFull (instance: IGBInstance, publicAddress: string): Promise<IGBInstance> { public async deployBotFull (instance: IGBInstance, publicAddress: string): Promise<IGBInstance> {
// Reads base configuration from environent file. // Reads base configuration from environent file.
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
@ -328,9 +324,10 @@ export class GBDeployer implements IGBDeployer {
*/ */
public async publishNLP (instance: IGBInstance): Promise<void> { public async publishNLP (instance: IGBInstance): Promise<void> {
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
const res = await service.publishNLP(instance.cloudLocation, instance.nlpAppId, const res = await service.publishNLP(instance.cloudLocation, instance.nlpAppId, instance.nlpAuthoringKey);
instance.nlpAuthoringKey); if (res.status !== 200 && res.status !== 201) {
if (res.status !== 200 && res.status !== 201) { throw res.bodyAsText; } throw res.bodyAsText;
}
} }
/** /**
@ -339,7 +336,9 @@ export class GBDeployer implements IGBDeployer {
public async trainNLP (instance: IGBInstance): Promise<void> { public async trainNLP (instance: IGBInstance): Promise<void> {
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
const res = await service.trainNLP(instance.cloudLocation, instance.nlpAppId, instance.nlpAuthoringKey); const res = await service.trainNLP(instance.cloudLocation, instance.nlpAppId, instance.nlpAuthoringKey);
if (res.status !== 200 && res.status !== 202) { throw res.bodyAsText; } if (res.status !== 200 && res.status !== 202) {
throw res.bodyAsText;
}
const sleep = ms => { const sleep = ms => {
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
@ -353,8 +352,14 @@ export class GBDeployer implements IGBDeployer {
*/ */
public async getBotManifest (instance: IGBInstance): Promise<Buffer> { public async getBotManifest (instance: IGBInstance): Promise<Buffer> {
const s = new TeamsService(); const s = new TeamsService();
const manifest = await s.getManifest(instance.marketplaceId, instance.title, instance.description, const manifest = await s.getManifest(
GBAdminService.generateUuid(), instance.botId, "General Bots"); instance.marketplaceId,
instance.title,
instance.description,
GBAdminService.generateUuid(),
instance.botId,
'General Bots'
);
return await s.getAppFile(manifest); return await s.getAppFile(manifest);
} }
@ -371,7 +376,9 @@ export class GBDeployer implements IGBDeployer {
instance.nlpAuthoringKey, instance.nlpAuthoringKey,
listData listData
); );
if (res.status !== 200) { throw res.bodyAsText; } if (res.status !== 200) {
throw res.bodyAsText;
}
} }
/** /**
@ -408,9 +415,7 @@ export class GBDeployer implements IGBDeployer {
let url = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:${path}:/children`; let url = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:${path}:/children`;
GBLog.info(`Loading .gbot from Excel: ${url}`); GBLog.info(`Loading .gbot from Excel: ${url}`);
const res = await client const res = await client.api(url).get();
.api(url)
.get();
// Finds Config.xlsx. // Finds Config.xlsx.
@ -428,10 +433,13 @@ export class GBDeployer implements IGBDeployer {
const results = await client const results = await client
.api( .api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/items/${document[0].id}/workbook/worksheets('General')/range(address='A7:B100')` `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/items/${
document[0].id
}/workbook/worksheets('General')/range(address='A7:B100')`
) )
.get(); .get();
let index = 0, obj = {}; let index = 0,
obj = {};
for (; index < results.text.length; index++) { for (; index < results.text.length; index++) {
if (results.text[index][0] === '') { if (results.text[index][0] === '') {
return obj; return obj;
@ -445,14 +453,17 @@ export class GBDeployer implements IGBDeployer {
/** /**
* Loads all para from tabular file Config.xlsx. * Loads all para from tabular file Config.xlsx.
*/ */
public async downloadFolder(min: GBMinInstance, localPath: string, remotePath: string, public async downloadFolder (
baseUrl: string = null, client = null): Promise<any> { min: GBMinInstance,
localPath: string,
remotePath: string,
baseUrl: string = null,
client = null
): Promise<any> {
GBLog.info(`downloadFolder: localPath=${localPath}, remotePath=${remotePath}, baseUrl=${baseUrl}`); GBLog.info(`downloadFolder: localPath=${localPath}, remotePath=${remotePath}, baseUrl=${baseUrl}`);
if (!baseUrl) { if (!baseUrl) {
[baseUrl, client] = await GBDeployer.internalGetDriveClient(min); client = await GBDeployer.internalGetDriveClient(min);
remotePath = remotePath.replace(/\\/gi, '/'); remotePath = remotePath.replace(/\\/gi, '/');
const parts = remotePath.split('/'); const parts = remotePath.split('/');
@ -479,9 +490,7 @@ export class GBDeployer implements IGBDeployer {
GBLog.info(`Download URL: ${url}`); GBLog.info(`Download URL: ${url}`);
const res = await client const res = await client.client.api(url).get();
.api(url)
.get();
const documents = res.value; const documents = res.value;
if (documents === undefined || documents.length === 0) { if (documents === undefined || documents.length === 0) {
GBLog.info(`${remotePath} is an empty folder.`); GBLog.info(`${remotePath} is an empty folder.`);
@ -491,7 +500,6 @@ export class GBDeployer implements IGBDeployer {
// Download files or navigate to directory to recurse. // Download files or navigate to directory to recurse.
await CollectionUtil.asyncForEach(documents, async item => { await CollectionUtil.asyncForEach(documents, async item => {
const itemPath = Path.join(localPath, remotePath, item.name); const itemPath = Path.join(localPath, remotePath, item.name);
if (item.folder) { if (item.folder) {
@ -516,10 +524,8 @@ export class GBDeployer implements IGBDeployer {
const response = await request({ uri: url, encoding: null }); const response = await request({ uri: url, encoding: null });
Fs.writeFileSync(itemPath, response, { encoding: null }); Fs.writeFileSync(itemPath, response, { encoding: null });
Fs.utimesSync(itemPath, Fs.utimesSync(itemPath, new Date(), new Date(item.lastModifiedDateTime));
new Date(), new Date(item.lastModifiedDateTime)); } else {
}
else {
GBLog.info(`Local is up to date: ${itemPath}...`); GBLog.info(`Local is up to date: ${itemPath}...`);
} }
} }
@ -530,7 +536,6 @@ export class GBDeployer implements IGBDeployer {
* UndDeploys a bot to the storage. * UndDeploys a bot to the storage.
*/ */
public async undeployBot (botId: string, packageName: string): Promise<void> { public async undeployBot (botId: string, packageName: string): Promise<void> {
// Deletes Bot registration on cloud. // Deletes Bot registration on cloud.
const service = new AzureDeployerService(this); const service = new AzureDeployerService(this);
@ -562,7 +567,6 @@ export class GBDeployer implements IGBDeployer {
* Deploys a folder into the bot storage. * Deploys a folder into the bot storage.
*/ */
public async deployPackage (min: GBMinInstance, localPath: string) { public async deployPackage (min: GBMinInstance, localPath: string) {
const packageType = Path.extname(localPath); const packageType = Path.extname(localPath);
let handled = false; let handled = false;
let pck = null; let pck = null;
@ -572,7 +576,6 @@ export class GBDeployer implements IGBDeployer {
const _this = this; const _this = this;
await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => { await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => {
try { try {
// If it will be handled, create a temporary service layer to be // If it will be handled, create a temporary service layer to be
// called by .gbapp and manage the associated package row. // called by .gbapp and manage the associated package row.
@ -605,7 +608,6 @@ export class GBDeployer implements IGBDeployer {
switch (packageType) { switch (packageType) {
case '.gbot': case '.gbot':
// Extracts configuration information from .gbot files. // Extracts configuration information from .gbot files.
if (process.env.ENABLE_PARAMS_ONLINE === 'false') { if (process.env.ENABLE_PARAMS_ONLINE === 'false') {
@ -624,7 +626,6 @@ export class GBDeployer implements IGBDeployer {
break; break;
case '.gbkb': case '.gbkb':
// Deploys .gbkb into the storage. // Deploys .gbkb into the storage.
const service = new KBService(this.core.sequelize); const service = new KBService(this.core.sequelize);
@ -632,7 +633,6 @@ export class GBDeployer implements IGBDeployer {
break; break;
case '.gbdialog': case '.gbdialog':
// Compiles files from .gbdialog into work folder and deploys // Compiles files from .gbdialog into work folder and deploys
// it to the VM. // it to the VM.
@ -642,7 +642,6 @@ export class GBDeployer implements IGBDeployer {
break; break;
case '.gbtheme': case '.gbtheme':
// Updates server listeners to serve theme files in .gbtheme. // Updates server listeners to serve theme files in .gbtheme.
const packageName = Path.basename(localPath); const packageName = Path.basename(localPath);
@ -652,21 +651,18 @@ export class GBDeployer implements IGBDeployer {
break; break;
case '.gbapp': case '.gbapp':
// Dynamically compiles and loads .gbapp packages (Node.js packages). // Dynamically compiles and loads .gbapp packages (Node.js packages).
await this.callGBAppCompiler(localPath, this.core); await this.callGBAppCompiler(localPath, this.core);
break; break;
case '.gblib': case '.gblib':
// Dynamically compiles and loads .gblib packages (Node.js packages). // Dynamically compiles and loads .gblib packages (Node.js packages).
await this.callGBAppCompiler(localPath, this.core); await this.callGBAppCompiler(localPath, this.core);
break; break;
default: default:
const err = GBError.create(`Unhandled package type: ${packageType}.`); const err = GBError.create(`Unhandled package type: ${packageType}.`);
Promise.reject(err); Promise.reject(err);
break; break;
@ -677,7 +673,6 @@ export class GBDeployer implements IGBDeployer {
* Removes the package from the storage and local work folders. * Removes the package from the storage and local work folders.
*/ */
public async undeployPackageFromLocalPath (instance: IGBInstance, localPath: string) { public async undeployPackageFromLocalPath (instance: IGBInstance, localPath: string) {
// Gets information about the package. // Gets information about the package.
const packageType = Path.extname(localPath); const packageType = Path.extname(localPath);
@ -687,7 +682,6 @@ export class GBDeployer implements IGBDeployer {
// Removes objects from storage, cloud resources and local files if any. // Removes objects from storage, cloud resources and local files if any.
switch (packageType) { switch (packageType) {
case '.gbot': case '.gbot':
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8')); const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
await this.undeployBot(packageObject.botId, packageName); await this.undeployBot(packageObject.botId, packageName);
@ -727,7 +721,6 @@ export class GBDeployer implements IGBDeployer {
* its index based on .gbkb structure. * its index based on .gbkb structure.
*/ */
public async rebuildIndex (instance: IGBInstance, searchSchema: any) { public async rebuildIndex (instance: IGBInstance, searchSchema: any) {
// Prepares search. // Prepares search.
const search = new AzureSearch( const search = new AzureSearch(
@ -744,11 +737,9 @@ export class GBDeployer implements IGBDeployer {
try { try {
await search.deleteDataSource(dsName); await search.deleteDataSource(dsName);
} catch (err) { } catch (err) {
// If it is a 404 there is nothing to delete as it is the first creation. // If it is a 404 there is nothing to delete as it is the first creation.
if (err.code !== 404) { if (err.code !== 404) {
throw err; throw err;
} }
} }
@ -758,10 +749,9 @@ export class GBDeployer implements IGBDeployer {
try { try {
await search.deleteIndex(); await search.deleteIndex();
} catch (err) { } catch (err) {
// If it is a 404 there is nothing to delete as it is the first creation. // If it is a 404 there is nothing to delete as it is the first creation.
if (err.code !== 404 && err.code !== "OperationNotAllowed") { if (err.code !== 404 && err.code !== 'OperationNotAllowed') {
throw err; throw err;
} }
} }
@ -793,7 +783,6 @@ export class GBDeployer implements IGBDeployer {
* makes this web application available as default web front-end. * makes this web application available as default web front-end.
*/ */
public setupDefaultGBUI () { public setupDefaultGBUI () {
// Setups paths. // Setups paths.
const root = 'packages/default.gbui'; const root = 'packages/default.gbui';
@ -802,7 +791,6 @@ export class GBDeployer implements IGBDeployer {
// Checks if .gbapp compiliation is enabled. // Checks if .gbapp compiliation is enabled.
if (!Fs.existsSync(`${root}/build`) && process.env.DISABLE_WEB !== 'true') { if (!Fs.existsSync(`${root}/build`) && process.env.DISABLE_WEB !== 'true') {
// Write a .env required to fix some bungs in create-react-app tool. // Write a .env required to fix some bungs in create-react-app tool.
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true'); Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
@ -822,7 +810,6 @@ export class GBDeployer implements IGBDeployer {
* Servers bot storage assets to be used by web, WhatsApp and other channels. * Servers bot storage assets to be used by web, WhatsApp and other channels.
*/ */
public static mountGBKBAssets (packageName: any, botId: string, filename: string) { public static mountGBKBAssets (packageName: any, botId: string, filename: string) {
// Servers menu assets. // Servers menu assets.
GBServer.globals.server.use( GBServer.globals.server.use(
@ -833,20 +820,27 @@ export class GBDeployer implements IGBDeployer {
// Servers all other assets in .gbkb folders. // Servers all other assets in .gbkb folders.
const gbaiName = `${botId}.gbai`; const gbaiName = `${botId}.gbai`;
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/assets`, GBServer.globals.server.use(
express.static(urlJoin('work', gbaiName, filename, 'assets'))); `/kb/${gbaiName}/${packageName}/assets`,
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/images`, express.static(urlJoin('work', gbaiName, filename, 'assets'))
express.static(urlJoin('work', gbaiName, filename, 'images'))); );
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/audios`, GBServer.globals.server.use(
express.static(urlJoin('work', gbaiName, filename, 'audios'))); `/kb/${gbaiName}/${packageName}/images`,
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/videos`, express.static(urlJoin('work', gbaiName, filename, 'images'))
express.static(urlJoin('work', gbaiName, filename, 'videos'))); );
GBServer.globals.server.use(`/${botId}/cache`, GBServer.globals.server.use(
express.static(urlJoin('work', gbaiName, 'cache'))); `/kb/${gbaiName}/${packageName}/audios`,
GBServer.globals.server.use(`/${gbaiName}/${botId}.gbdata/public`, express.static(urlJoin('work', gbaiName, filename, 'audios'))
express.static(urlJoin('work', gbaiName, `${botId}.gbdata`, 'public'))); );
GBServer.globals.server.use(
`/kb/${gbaiName}/${packageName}/videos`,
express.static(urlJoin('work', gbaiName, filename, 'videos'))
);
GBServer.globals.server.use(`/${botId}/cache`, express.static(urlJoin('work', gbaiName, 'cache')));
GBServer.globals.server.use(
`/${gbaiName}/${botId}.gbdata/public`,
express.static(urlJoin('work', gbaiName, `${botId}.gbdata`, 'public'))
);
GBLog.verbose(`KB (.gbkb) assets accessible at: /kb/${botId}.gbai/${packageName}.`); GBLog.verbose(`KB (.gbkb) assets accessible at: /kb/${botId}.gbai/${packageName}.`);
} }
@ -860,7 +854,6 @@ export class GBDeployer implements IGBDeployer {
appPackages: any[] = undefined, appPackages: any[] = undefined,
appPackagesProcessed: number = 0 appPackagesProcessed: number = 0
) { ) {
// Runs `npm install` for the package. // Runs `npm install` for the package.
GBLog.info(`Deploying General Bots Application (.gbapp) or Library (.gblib): ${Path.basename(gbappPath)}...`); GBLog.info(`Deploying General Bots Application (.gbapp) or Library (.gblib): ${Path.basename(gbappPath)}...`);
@ -874,7 +867,6 @@ export class GBDeployer implements IGBDeployer {
folder = Path.join(gbappPath, 'dist'); folder = Path.join(gbappPath, 'dist');
try { try {
// Runs TSC in .gbapp folder. // Runs TSC in .gbapp folder.
if (process.env.GBAPP_DISABLE_COMPILE !== 'true') { if (process.env.GBAPP_DISABLE_COMPILE !== 'true') {
@ -896,7 +888,6 @@ export class GBDeployer implements IGBDeployer {
} }
GBLog.info(`.gbapp or .gblib deployed: ${gbappPath}.`); GBLog.info(`.gbapp or .gblib deployed: ${gbappPath}.`);
appPackagesProcessed++; appPackagesProcessed++;
} catch (error) { } catch (error) {
GBLog.error(`Error compiling package, message: ${error.message}\n${error.stack}`); GBLog.error(`Error compiling package, message: ${error.message}\n${error.stack}`);
if (error.stdout) { if (error.stdout) {
@ -936,7 +927,6 @@ export class GBDeployer implements IGBDeployer {
* Performs the process of compiling all .gbapp folders. * Performs the process of compiling all .gbapp folders.
*/ */
private async deployAppPackages (gbappPackages: string[], core: any, appPackages: any[]) { private async deployAppPackages (gbappPackages: string[], core: any, appPackages: any[]) {
// Loops through all ready to load .gbapp packages. // Loops through all ready to load .gbapp packages.
let appPackagesProcessed = 0; let appPackagesProcessed = 0;

View file

@ -38,11 +38,11 @@
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib'; import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
import { CreateOptions } from 'sequelize/types'; import { CreateOptions } from 'sequelize/types';
const fs = require('fs'); import Fs from 'fs';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GuaribasInstance } from '../models/GBModel'; import { GuaribasInstance } from '../models/GBModel.js';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService.js';
/** /**
* Handles the importing of packages. * Handles the importing of packages.
@ -54,9 +54,13 @@ export class GBImporter {
this.core = core; this.core = core;
} }
public async importIfNotExistsBotPackage(botId: string, public async importIfNotExistsBotPackage (
packageName: string, localPath: string, additionalInstance: IGBInstance = null) { botId: string,
const settingsJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8')); packageName: string,
localPath: string,
additionalInstance: IGBInstance = null
) {
const settingsJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
if (botId === undefined) { if (botId === undefined) {
botId = settingsJson.botId; botId = settingsJson.botId;
} }
@ -107,8 +111,8 @@ export class GBImporter {
localPath: string, localPath: string,
settingsJson: any settingsJson: any
) { ) {
const packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8')); const packageJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8')); const servicesJson = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
const fullSettingsJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settingsJson, ...servicesJson }; const fullSettingsJson = { ...GBServer.globals.bootInstance, ...packageJson, ...settingsJson, ...servicesJson };

View file

@ -35,19 +35,18 @@
*/ */
'use strict'; 'use strict';
const cliProgress = require('cli-progress'); import cliProgress from 'cli-progress';
const { DialogSet, TextPrompt } = require('botbuilder-dialogs'); import { DialogSet, TextPrompt } from 'botbuilder-dialogs';
const express = require('express'); import express from 'express';
const Swagger = require('swagger-client'); import Swagger from 'swagger-client';
const Fs = require('fs'); import request from 'request-promise-native';
const request = require('request-promise-native'); import removeRoute from 'express-remove-route';
const removeRoute = require('express-remove-route'); import AuthenticationContext from '@azure/msal-node';
const AuthenticationContext = require('adal-node').AuthenticationContext; import wash from 'washyourmouthoutwithsoap';
const wash = require('washyourmouthoutwithsoap'); import { FacebookAdapter } from 'botbuilder-adapter-facebook';
const { FacebookAdapter } = require('botbuilder-adapter-facebook'); import path from 'path';
const path = require('path'); import mkdirp from 'mkdirp';
const { NerManager } = require('node-nlp'); import Fs from 'fs';
const mkdirp = require('mkdirp');
import { import {
AutoSaveStateMiddleware, AutoSaveStateMiddleware,
BotFrameworkAdapter, BotFrameworkAdapter,
@ -69,31 +68,31 @@ import {
} from 'botlib'; } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { MicrosoftAppCredentials } from 'botframework-connector'; import { MicrosoftAppCredentials } from 'botframework-connector';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GuaribasConversationMessage } from '../../analytics.gblib/models'; import { GuaribasConversationMessage } from '../../analytics.gblib/models/index.js';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog'; import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog.js';
import { KBService } from '../../kb.gbapp/services/KBService'; import { KBService } from '../../kb.gbapp/services/KBService.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine'; import { WhatsappDirectLine } from '../../whatsapp.gblib/services/WhatsappDirectLine.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
import { GBConfigService } from './GBConfigService'; import { GBConfigService } from './GBConfigService.js';
import { GBConversationalService } from './GBConversationalService'; import { GBConversationalService } from './GBConversationalService.js';
import { GBDeployer } from './GBDeployer'; import { GBDeployer } from './GBDeployer.js';
import urlJoin = require('url-join'); import urlJoin from 'url-join';
import fs = require('fs'); import fs from 'fs';
import { GoogleChatDirectLine } from '../../google-chat.gblib/services/GoogleChatDirectLine'; import { GoogleChatDirectLine } from '../../google-chat.gblib/services/GoogleChatDirectLine.js';
import { ScheduleServices } from '../../basic.gblib/services/ScheduleServices'; import { ScheduleServices } from '../../basic.gblib/services/ScheduleServices.js';
import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords'; import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js';
import { ssrForBots } from './GBSSR'; import { ssrForBots } from './GBSSR';
import * as nlp from 'node-nlp';
/** /**
* Minimal service layer for a bot and encapsulation of BOT Framework calls. * Minimal service layer for a bot and encapsulation of BOT Framework calls.
*/ */
export class GBMinService { export class GBMinService {
/** /**
* Default General Bots User Interface package. * Default General Bots User Interface package.
*/ */
@ -119,7 +118,6 @@ export class GBMinService {
*/ */
public deployer: GBDeployer; public deployer: GBDeployer;
bar1; bar1;
/** /**
@ -141,16 +139,14 @@ export class GBMinService {
* Constructs a new minimal instance for each bot. * Constructs a new minimal instance for each bot.
*/ */
public async buildMin (instances: IGBInstance[]) { public async buildMin (instances: IGBInstance[]) {
// Servers default UI on root address '/' if web enabled. // Servers default UI on root address '/' if web enabled.
if (process.env.DISABLE_WEB !== 'true') { if (process.env.DISABLE_WEB !== 'true') {
// SSR processing. // SSR processing.
const defaultOptions = { const defaultOptions = {
prerender: [], prerender: [],
exclude: ["/api/", "/instances/", "/webhooks/"], exclude: ['/api/', '/instances/', '/webhooks/'],
useCache: true, useCache: true,
cacheRefreshRate: 86400 cacheRefreshRate: 86400
}; };
@ -164,25 +160,27 @@ export class GBMinService {
GBServer.globals.server.use('/', express.static(url)); GBServer.globals.server.use('/', express.static(url));
// Servers the bot information object via HTTP so clients can get // Servers the bot information object via HTTP so clients can get
// instance information stored on server. // instance information stored on server.
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; let i = 1;
if (instances.length > 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); },
this.bar1.start(instances.length, i, { botId: "Boot" }); cliProgress.Presets.rect
);
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;
const results = new Array(promises.length); const results = new Array(promises.length);
@ -203,34 +201,34 @@ export class GBMinService {
return results; return results;
}; };
await throttledPromiseAll(instances.map((async instance => { await throttledPromiseAll(
instances.map(
(async instance => {
try { try {
await this['mountBot'](instance); await this['mountBot'](instance);
if (this.bar1) { 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) { 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...`);
const service = new ScheduleServices(); // const service = new ScheduleServices();
await service.scheduleAll(); // await service.scheduleAll();
GBLog.info(`All Bot instances loaded.`); GBLog.info(`All Bot instances loaded.`);
} }
/** /**
* Removes bot endpoint from web listeners and remove bot instance * Removes bot endpoint from web listeners and remove bot instance
* from list of global server bot instances. * from list of global server bot instances.
@ -243,7 +241,6 @@ export class GBMinService {
removeRoute(GBServer.globals.server, uiUrl); removeRoute(GBServer.globals.server, uiUrl);
GBServer.globals.minInstances = GBServer.globals.minInstances.filter(p => p.instance.botId !== botId); GBServer.globals.minInstances = GBServer.globals.minInstances.filter(p => p.instance.botId !== botId);
} }
/** /**
@ -252,7 +249,6 @@ export class GBMinService {
* installing all BASIC artifacts from .gbdialog and OAuth2. * installing all BASIC artifacts from .gbdialog and OAuth2.
*/ */
public async mountBot (instance: IGBInstance) { public async mountBot (instance: IGBInstance) {
// Build bot adapter. // Build bot adapter.
const { min, adapter, conversationState } = await this.buildBotAdapter( const { min, adapter, conversationState } = await this.buildBotAdapter(
@ -268,33 +264,33 @@ export class GBMinService {
// Install per bot deployed packages. // Install per bot deployed packages.
let packagePath = `work/${min.botId}.gbai/${min.botId}.gbdialog`; let packagePath = `work/${min.botId}.gbai/${min.botId}.gbdialog`;
if (fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer.deployPackage(min, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gbapp`; packagePath = `work/${min.botId}.gbai/${min.botId}.gbapp`;
if (fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer.deployPackage(min, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gbtheme`; packagePath = `work/${min.botId}.gbai/${min.botId}.gbtheme`;
if (fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer.deployPackage(min, packagePath);
} }
packagePath = `work/${min.botId}.gbai/${min.botId}.gblib`; packagePath = `work/${min.botId}.gbai/${min.botId}.gblib`;
if (fs.existsSync(packagePath)) { if (Fs.existsSync(packagePath)) {
await this.deployer.deployPackage(min, packagePath); await this.deployer.deployPackage(min, packagePath);
} }
let dir = `work/${min.botId}.gbai/cache`; let dir = `work/${min.botId}.gbai/cache`;
if (!fs.existsSync(dir)) { if (!Fs.existsSync(dir)) {
mkdirp.sync(dir); mkdirp.sync(dir);
} }
dir = `work/${min.botId}.gbai/profile`; dir = `work/${min.botId}.gbai/profile`;
if (!fs.existsSync(dir)) { if (!Fs.existsSync(dir)) {
mkdirp.sync(dir); mkdirp.sync(dir);
} }
dir = `work/${min.botId}.gbai/uploads`; dir = `work/${min.botId}.gbai/uploads`;
if (!fs.existsSync(dir)) { if (!Fs.existsSync(dir)) {
mkdirp.sync(dir); mkdirp.sync(dir);
} }
@ -329,8 +325,11 @@ export class GBMinService {
// Test code. // Test code.
if (process.env.TEST_MESSAGE) { if (process.env.TEST_MESSAGE) {
GBLog.info(`Starting auto test with '${process.env.TEST_MESSAGE}'.`);
const client = await new Swagger({ const client = await new Swagger({
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), usePromise: true spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
usePromise: true
}); });
client.clientAuthorizations.add( client.clientAuthorizations.add(
'AuthorizationBotConnector', 'AuthorizationBotConnector',
@ -348,7 +347,6 @@ export class GBMinService {
}; };
await CollectionUtil.asyncForEach(steps, async step => { await CollectionUtil.asyncForEach(steps, async step => {
client.Conversations.Conversations_PostActivity({ client.Conversations.Conversations_PostActivity({
conversationId: conversationId, conversationId: conversationId,
activity: { activity: {
@ -362,9 +360,7 @@ export class GBMinService {
} }
}); });
await sleep(15000); await sleep(5000);
}); });
} }
@ -408,15 +404,14 @@ export class GBMinService {
this.createCheckHealthAddress(GBServer.globals.server, min, min.instance); this.createCheckHealthAddress(GBServer.globals.server, min, min.instance);
GBDeployer.mountGBKBAssets(`${instance.botId}.gbkb`, GBDeployer.mountGBKBAssets(`${instance.botId}.gbkb`, instance.botId, `${instance.botId}.gbkb`);
instance.botId, `${instance.botId}.gbkb`);
} }
public static isChatAPI (req, res) { public static isChatAPI (req, res) {
if (!res) { if (!res) {
return "GeneralBots"; return 'GeneralBots';
} }
return req.body.phone_id ? "maytapi" : "chatapi"; return req.body.phone_id ? 'maytapi' : 'chatapi';
} }
/** /**
@ -426,7 +421,6 @@ export class GBMinService {
private createCheckHealthAddress (server: any, min: GBMinInstance, instance: IGBInstance) { private createCheckHealthAddress (server: any, min: GBMinInstance, instance: IGBInstance) {
server.get(`/${min.instance.botId}/check`, async (req, res) => { server.get(`/${min.instance.botId}/check`, async (req, res) => {
try { try {
// Performs the checking of WhatsApp API if enabled for this instance. // Performs the checking of WhatsApp API if enabled for this instance.
if (min.whatsAppDirectLine != undefined && instance.whatsappServiceKey !== null) { if (min.whatsAppDirectLine != undefined && instance.whatsappServiceKey !== null) {
@ -442,9 +436,7 @@ export class GBMinService {
// GB is OK, so 200. // GB is OK, so 200.
res.status(200).send(`General Bot ${min.botId} is healthly.`); res.status(200).send(`General Bot ${min.botId} is healthly.`);
} catch (error) { } catch (error) {
// GB is not OK, 500 and detail the information on response content. // GB is not OK, 500 and detail the information on response content.
GBLog.error(error); GBLog.error(error);
@ -458,9 +450,7 @@ export class GBMinService {
* on https://<gbhost>/<BotId>/token URL. * on https://<gbhost>/<BotId>/token URL.
*/ */
private handleOAuthTokenRequests (server: any, min: GBMinInstance, instance: IGBInstance) { private handleOAuthTokenRequests (server: any, min: GBMinInstance, instance: IGBInstance) {
server.get(`/${min.instance.botId}/token`, async (req, res) => { server.get(`/${min.instance.botId}/token`, async (req, res) => {
// Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure. // Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure.
const state = await min.adminService.getValue(instance.instanceId, 'AntiCSRFAttackState'); const state = await min.adminService.getValue(instance.instanceId, 'AntiCSRFAttackState');
@ -469,7 +459,7 @@ export class GBMinService {
GBLog.error(msg); GBLog.error(msg);
throw new Error(msg); throw new Error(msg);
} }
const authenticationContext = new AuthenticationContext( const authenticationContext = new AuthenticationContext.AuthenticationContext(
urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant) urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
); );
const resource = 'https://graph.microsoft.com'; const resource = 'https://graph.microsoft.com';
@ -488,12 +478,11 @@ export class GBMinService {
GBLog.error(msg); GBLog.error(msg);
res.send(msg); res.send(msg);
} else { } else {
// Saves token to the database. // Saves token to the database.
await this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken); await this.adminService.setValue(instance.instanceId, 'accessToken', token['accessToken']);
await this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken); await this.adminService.setValue(instance.instanceId, 'refreshToken', token['refreshToken']);
await this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString()); await this.adminService.setValue(instance.instanceId, 'expiresOn', token['expiresOn'].toString());
await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', undefined); await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', undefined);
// Inform the home for default .gbui after finishing token retrival. // Inform the home for default .gbui after finishing token retrival.
@ -516,7 +505,8 @@ export class GBMinService {
min.instance.authenticatorTenant, min.instance.authenticatorTenant,
'/oauth2/authorize' '/oauth2/authorize'
); );
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.marketplaceId
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`; }&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`); GBLog.info(`HandleOAuthRequests: ${authorizationUrl}.`);
res.redirect(authorizationUrl); res.redirect(authorizationUrl);
@ -527,7 +517,6 @@ export class GBMinService {
* Returns the instance object to clients requesting bot info. * Returns the instance object to clients requesting bot info.
*/ */
private async handleGetInstanceForClient (req: any, res: any) { private async handleGetInstanceForClient (req: any, res: any) {
// Translates the requested botId. // Translates the requested botId.
let botId = req.params.botId; let botId = req.params.botId;
@ -545,7 +534,6 @@ export class GBMinService {
} }
if (instance !== null) { if (instance !== null) {
// Gets the webchat token, speech token and theme. // Gets the webchat token, speech token and theme.
const webchatTokenContainer = await this.getWebchatToken(instance); const webchatTokenContainer = await this.getWebchatToken(instance);
@ -558,7 +546,6 @@ export class GBMinService {
theme = `default.gbtheme`; theme = `default.gbtheme`;
} }
res.send( res.send(
JSON.stringify({ JSON.stringify({
instanceId: instance.instanceId, instanceId: instance.instanceId,
@ -631,16 +618,18 @@ export class GBMinService {
* Builds the BOT Framework & GB infrastructures. * Builds the BOT Framework & GB infrastructures.
*/ */
private async buildBotAdapter (instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) { private async buildBotAdapter (instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) {
// MSFT stuff. // MSFT stuff.
const adapter = new BotFrameworkAdapter( const adapter = new BotFrameworkAdapter({
{ appId: instance.marketplaceId, appPassword: instance.marketplacePassword }); appId: instance.marketplaceId,
appPassword: instance.marketplacePassword
});
const storage = new MemoryStorage(); const storage = new MemoryStorage();
const conversationState = new ConversationState(storage); const conversationState = new ConversationState(storage);
const userState = new UserState(storage); const userState = new UserState(storage);
adapter.use(new AutoSaveStateMiddleware(conversationState, userState)); adapter.use(new AutoSaveStateMiddleware(conversationState, userState));
MicrosoftAppCredentials.trustServiceUrl('https://directline.botframework.com', MicrosoftAppCredentials.trustServiceUrl(
'https://directline.botframework.com',
new Date(new Date().setFullYear(new Date().getFullYear() + 10)) new Date(new Date().setFullYear(new Date().getFullYear() + 10))
); );
@ -659,9 +648,9 @@ export class GBMinService {
min.cbMap = {}; min.cbMap = {};
min.scriptMap = {}; min.scriptMap = {};
min.sandBoxMap = {}; min.sandBoxMap = {};
min["scheduleMap"] = {}; min['scheduleMap'] = {};
min["conversationWelcomed"] = {}; min['conversationWelcomed'] = {};
min["nerEngine"] = new NerManager();; min['nerEngine'] = new nlp.default.NerManager();
min.packages = sysPackages; min.packages = sysPackages;
min.appPackages = appPackages; min.appPackages = appPackages;
@ -687,8 +676,6 @@ export class GBMinService {
} }
}); });
if (min.instance.googlePrivateKey) { if (min.instance.googlePrivateKey) {
min['googleDirectLine'] = new GoogleChatDirectLine( min['googleDirectLine'] = new GoogleChatDirectLine(
min, min,
@ -703,11 +690,7 @@ export class GBMinService {
await min['googleDirectLine'].setup(true); await min['googleDirectLine'].setup(true);
} }
const group = min.core.getParam<string>( const group = min.core.getParam<string>(min.instance, 'WhatsApp Group ID', null);
min.instance,
'WhatsApp Group ID',
null,
);
WhatsappDirectLine.botGroups[min.botId] = group; WhatsappDirectLine.botGroups[min.botId] = group;
@ -767,7 +750,6 @@ export class GBMinService {
* Performs calling of loadBot event in all .gbapps. * Performs calling of loadBot event in all .gbapps.
*/ */
private async invokeLoadBot (appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) { private async invokeLoadBot (appPackages: IGBPackage[], sysPackages: IGBPackage[], min: GBMinInstance) {
// Calls loadBot event in all .gbapp packages. // Calls loadBot event in all .gbapp packages.
await CollectionUtil.asyncForEach(sysPackages, async p => { await CollectionUtil.asyncForEach(sysPackages, async p => {
@ -802,11 +784,14 @@ export class GBMinService {
// TODO: Unify in util. // TODO: Unify in util.
public static userMobile (step) { public static userMobile (step) {
let mobile = WhatsappDirectLine.mobiles[step.context.activity.conversation.id] let mobile = WhatsappDirectLine.mobiles[step.context.activity.conversation.id];
return mobile;
if (!mobile && step) {
return step.context.activity.from.id;
} }
return mobile;
}
/** /**
* BOT Framework web service hook method. * BOT Framework web service hook method.
@ -819,7 +804,6 @@ export class GBMinService {
instance: any, instance: any,
appPackages: any[] appPackages: any[]
) { ) {
let adapter = min.bot; let adapter = min.bot;
if (req.body.object) { if (req.body.object) {
@ -841,13 +825,10 @@ export class GBMinService {
step.context.activity.locale = 'pt-BR'; step.context.activity.locale = 'pt-BR';
let firstTime = false; let firstTime = false;
try { try {
const sec = new SecService(); const sec = new SecService();
const user = await min.userProfile.get(context, {}); const user = await min.userProfile.get(context, {});
const conversationReference = JSON.stringify( const conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
TurnContext.getConversationReference(context.activity)
);
// First time processing. // First time processing.
@ -860,7 +841,7 @@ export class GBMinService {
user.subjects = []; user.subjects = [];
user.cb = undefined; user.cb = undefined;
user.welcomed = false; user.welcomed = false;
user.basicOptions = { maxLines: 100, translatorOn: true, wholeWord: true, theme: "white", maxColumns: 40 }; user.basicOptions = { maxLines: 100, translatorOn: true, wholeWord: true, theme: 'white', maxColumns: 40 };
firstTime = true; firstTime = true;
@ -868,7 +849,6 @@ export class GBMinService {
// including the bot, that is filtered bellow. // including the bot, that is filtered bellow.
if (context.activity.from.id !== min.botId) { if (context.activity.from.id !== min.botId) {
// Creates a new row in user table if it does not exists. // Creates a new row in user table if it does not exists.
const member = context.activity.from; const member = context.activity.from;
@ -886,7 +866,6 @@ export class GBMinService {
const analytics = new AnalyticsService(); const analytics = new AnalyticsService();
user.systemUser = persistedUser; user.systemUser = persistedUser;
user.conversation = await analytics.createConversation(persistedUser); user.conversation = await analytics.createConversation(persistedUser);
} }
await sec.updateConversationReferenceById(user.systemUser.userId, conversationReference); await sec.updateConversationReferenceById(user.systemUser.userId, conversationReference);
@ -911,37 +890,52 @@ export class GBMinService {
// Required for MSTEAMS handling of persisted conversations. // Required for MSTEAMS handling of persisted conversations.
if (step.context.activity.channelId === 'msteams') { if (step.context.activity.channelId === 'msteams') {
if (step.context.activity.attachments && step.context.activity.attachments.length > 1) { if (step.context.activity.attachments && step.context.activity.attachments.length > 1) {
const file = context.activity.attachments[0]; const file = context.activity.attachments[0];
const credentials = new MicrosoftAppCredentials(min.instance.marketplaceId, min.instance.marketplacePassword); const credentials = new MicrosoftAppCredentials(
min.instance.marketplaceId,
min.instance.marketplacePassword
);
const botToken = await credentials.getToken(); const botToken = await credentials.getToken();
const headers = { Authorization: `Bearer ${botToken}` }; const headers = { Authorization: `Bearer ${botToken}` };
const t = new SystemKeywords(null, null, null); const t = new SystemKeywords(null, null, null, null);
const data = await t.getByHttp(file.contentUrl, headers, null, null, null, true); const data = await t.getByHttp({
url: file.contentUrl,
headers,
username: null,
ps: null,
qs: null,
streaming: true
});
const folder = `work/${min.instance.botId}.gbai/cache`; const folder = `work/${min.instance.botId}.gbai/cache`;
const filename = `${GBAdminService.generateUuid()}.png`; const filename = `${GBAdminService.generateUuid()}.png`;
Fs.writeFileSync(path.join(folder, filename), data); Fs.writeFileSync(path.join(folder, filename), data);
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
);
} }
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);
if (startDialog && !user.welcomed) { if (startDialog && !user.welcomed) {
user.welcomed = true; user.welcomed = true;
GBLog.info(`Auto start (teams) dialog is now being called: ${startDialog} for ${min.instance.botId}...`); GBLog.info(`Auto start (teams) dialog is now being called: ${startDialog} for ${min.instance.botId}...`);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer); await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer, false);
} }
} }
} }
// Required for F0 handling of persisted conversations. // Required for F0 handling of persisted conversations.
GBLog.info(`Input> ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`); 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.
@ -949,9 +943,7 @@ export class GBMinService {
if (context.activity.type === 'installationUpdate') { if (context.activity.type === 'installationUpdate') {
GBLog.info(`Bot installed on Teams.`); GBLog.info(`Bot installed on Teams.`);
} else if (context.activity.type === 'conversationUpdate' && } else if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) {
context.activity.membersAdded.length > 0) {
// Check if a bot or a human participant is being added to the conversation. // Check if a bot or a human participant is being added to the conversation.
const member = context.activity.membersAdded[0]; const member = context.activity.membersAdded[0];
@ -967,45 +959,46 @@ export class GBMinService {
// Auto starts dialogs if any is specified. // Auto starts dialogs if any is specified.
if (!startDialog && !user.welcomed) { if (!startDialog && !user.welcomed) {
// Otherwise, calls / (root) to default welcome users. // Otherwise, calls / (root) to default welcome users.
await step.beginDialog('/'); await step.beginDialog('/');
} } else {
else { if (
if (!GBMinService.userMobile(step) && !GBMinService.userMobile(step) &&
!min["conversationWelcomed"][step.context.activity.conversation.id]) { !min['conversationWelcomed'][step.context.activity.conversation.id]
) {
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
min["conversationWelcomed"][step.context.activity.conversation.id] = true; GBLog.info(
`Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
GBLog.info(`Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`); );
await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer); await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer, false);
} }
} }
} else { } else {
GBLog.info(`Person added to conversation: ${member.name}`); GBLog.info(`Person added to conversation: ${member.name}`);
if (GBMinService.userMobile(step)) { if (GBMinService.userMobile(step)) {
if (startDialog && !min["conversationWelcomed"][step.context.activity.conversation.id] && if (
!step.context.activity['group']) { startDialog &&
!min['conversationWelcomed'][step.context.activity.conversation.id] &&
!step.context.activity['group']
) {
user.welcomed = true; user.welcomed = true;
min["conversationWelcomed"][step.context.activity.conversation.id] = true; min['conversationWelcomed'][step.context.activity.conversation.id] = true;
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
GBLog.info(`Auto start (whatsapp) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`); GBLog.info(
await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer); `Auto start (whatsapp) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer, false);
} }
} }
} }
} else if (context.activity.type === 'message') { } else if (context.activity.type === 'message') {
// Processes messages activities. // Processes messages activities.
await this.processMessageActivity(context, min, step); await this.processMessageActivity(context, min, step);
} else if (context.activity.type === 'event') { } else if (context.activity.type === 'event') {
// Processes events activities. // Processes events activities.
await this.processEventActivity(min, user, context, step); await this.processEventActivity(min, user, context, step);
@ -1014,9 +1007,7 @@ export class GBMinService {
// Saves conversation state for later use. // Saves conversation state for later use.
await conversationState.saveChanges(context, true); await conversationState.saveChanges(context, true);
} catch (error) { } catch (error) {
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`; const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
GBLog.error(msg); GBLog.error(msg);
@ -1035,7 +1026,6 @@ export class GBMinService {
* Called to handle all event sent by .gbui clients. * Called to handle all event sent by .gbui clients.
*/ */
private async processEventActivity (min, user, context, step: GBDialogStep) { private async processEventActivity (min, user, context, step: GBDialogStep) {
if (context.activity.name === 'whoAmI') { if (context.activity.name === 'whoAmI') {
await step.beginDialog('/whoAmI'); await step.beginDialog('/whoAmI');
} else if (context.activity.name === 'showSubjects') { } else if (context.activity.name === 'showSubjects') {
@ -1057,10 +1047,10 @@ export class GBMinService {
}); });
} else if (context.activity.name === 'startGB') { } else if (context.activity.name === 'startGB') {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null); const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
if (startDialog && !min["conversationWelcomed"][step.context.activity.conversation.id]) { if (startDialog && !min['conversationWelcomed'][step.context.activity.conversation.id]) {
user.welcomed = true; user.welcomed = true;
GBLog.info(`Auto start (web 2) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`); GBLog.info(`Auto start (web 2) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer); await GBVMService.callVM(startDialog.toLowerCase(), min, step, this.deployer, false);
} }
} else if (context.activity.name === 'updateToken') { } else if (context.activity.name === 'updateToken') {
const token = context.activity.data; const token = context.activity.data;
@ -1074,7 +1064,6 @@ export class GBMinService {
* Called to handle all text messages sent and received by the bot. * Called to handle all text messages sent and received by the bot.
*/ */
private async processMessageActivity (context, min: GBMinInstance, step: GBDialogStep) { private async processMessageActivity (context, min: GBMinInstance, step: GBDialogStep) {
const sec = new SecService(); const sec = new SecService();
if (!context.activity.text) { if (!context.activity.text) {
@ -1099,12 +1088,10 @@ export class GBMinService {
const user = await min.userProfile.get(context, {}); const user = await min.userProfile.get(context, {});
let message: GuaribasConversationMessage; let message: GuaribasConversationMessage;
if (process.env.PRIVACY_STORE_MESSAGES === 'true') { if (process.env.PRIVACY_STORE_MESSAGES === 'true') {
// Adds message to the analytics layer. // Adds message to the analytics layer.
const analytics = new AnalyticsService(); const analytics = new AnalyticsService();
if (user) { if (user) {
if (!user.conversation) { if (!user.conversation) {
user.conversation = await analytics.createConversation(user.systemUser); user.conversation = await analytics.createConversation(user.systemUser);
} }
@ -1128,51 +1115,47 @@ export class GBMinService {
const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined; const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
if (isVMCall) { if (isVMCall) {
await GBVMService.callVM(context.activity.text, min, step, this.deployer); await GBVMService.callVM(context.activity.text, min, step, this.deployer, false);
} else if (context.activity.text.charAt(0) === '/') { } else if (context.activity.text.charAt(0) === '/') {
const text = context.activity.text; const text = context.activity.text;
const parts = text.split(' '); const parts = text.split(' ');
const cmdOrDialogName = parts[0]; const cmdOrDialogName = parts[0];
parts.splice(0, 1); parts.splice(0, 1);
const args = parts.join(' '); const args = parts.join(' ');
if (cmdOrDialogName === '/start') { if (cmdOrDialogName === '/start') {
// Reset user. // Reset user.
const user = await min.userProfile.get(context, {}); const user = await min.userProfile.get(context, {});
await min.conversationalService.sendEvent(min, step, 'loadInstance', {}); await min.conversationalService.sendEvent(min, step, 'loadInstance', {});
user.loaded = false; user.loaded = false;
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
} else if (cmdOrDialogName === '/call') { } else if (cmdOrDialogName === '/call') {
await GBVMService.callVM(args, min, step, this.deployer); await GBVMService.callVM(args, min, step, this.deployer, false);
} else if (cmdOrDialogName === '/callsch') { } else if (cmdOrDialogName === '/callsch') {
await GBVMService.callVM(args, min, null, null); await GBVMService.callVM(args, min, null, null, false);
} else if (cmdOrDialogName === '/calldbg') {
await GBVMService.callVM(args, min, step, this.deployer, true);
} else { } else {
await step.beginDialog(cmdOrDialogName, { args: args }); await step.beginDialog(cmdOrDialogName, { args: args });
} }
} else if (globalQuit(step.context.activity.locale, context.activity.text)) { } else if (globalQuit(step.context.activity.locale, context.activity.text)) {
await step.cancelAllDialogs(); await step.cancelAllDialogs();
await min.conversationalService.sendText(min, step, Messages[step.context.activity.locale].canceled); await min.conversationalService.sendText(min, step, Messages[step.context.activity.locale].canceled);
} else if (context.activity.text === 'admin') { } else if (context.activity.text === 'admin') {
await step.beginDialog('/admin'); await step.beginDialog('/admin');
} else if (context.activity.text.startsWith('{"title"')) { } else if (context.activity.text.startsWith('{"title"')) {
await step.beginDialog('/menu', JSON.parse(context.activity.text)); await step.beginDialog('/menu', JSON.parse(context.activity.text));
} else if ( } else if (
!(await this.deployer.getStoragePackageByName(min.instance.instanceId, `${min.instance.botId}.gbkb`)) && !(await this.deployer.getStoragePackageByName(min.instance.instanceId, `${min.instance.botId}.gbkb`)) &&
process.env.GBKB_ENABLE_AUTO_PUBLISH === 'true' process.env.GBKB_ENABLE_AUTO_PUBLISH === 'true'
) { ) {
await min.conversationalService.sendText(min, step, await min.conversationalService.sendText(
min,
step,
`Oi, ainda não possuo pacotes de conhecimento publicados. Por favor, aguarde alguns segundos enquanto eu auto-publico alguns pacotes.` `Oi, ainda não possuo pacotes de conhecimento publicados. Por favor, aguarde alguns segundos enquanto eu auto-publico alguns pacotes.`
); );
await step.beginDialog('/publish', { confirm: true, firstTime: true }); await step.beginDialog('/publish', { confirm: true, firstTime: true });
} else { } else {
// Removes unwanted chars in input text. // Removes unwanted chars in input text.
let text = context.activity.text; let text = context.activity.text;
@ -1195,7 +1178,7 @@ export class GBMinService {
} }
}); });
const getNormalizedRegExp = (value) => { const getNormalizedRegExp = value => {
var chars = [ var chars = [
{ letter: 'a', reg: '[aáàãäâ]' }, { letter: 'a', reg: '[aáàãäâ]' },
{ letter: 'e', reg: '[eéèëê]' }, { letter: 'e', reg: '[eéèëê]' },
@ -1207,7 +1190,7 @@ export class GBMinService {
for (var i in chars) { for (var i in chars) {
value = value.replace(new RegExp(chars[i].letter, 'gi'), chars[i].reg); value = value.replace(new RegExp(chars[i].letter, 'gi'), chars[i].reg);
}; }
return value; return value;
}; };
@ -1223,7 +1206,10 @@ export class GBMinService {
const replacementToken = 'X' + GBAdminService.getNumberIdentifier().substr(0, 4); const replacementToken = 'X' + GBAdminService.getNumberIdentifier().substr(0, 4);
replacements[i] = { text: item, replacementToken: replacementToken }; replacements[i] = { text: item, replacementToken: replacementToken };
i++; i++;
textProcessed = textProcessed.replace(new RegExp(`\\b${getNormalizedRegExp(it.trim())}\\b`, 'gi'), `${replacementToken}`); textProcessed = textProcessed.replace(
new RegExp(`\\b${getNormalizedRegExp(it.trim())}\\b`, 'gi'),
`${replacementToken}`
);
} }
}); });
} }
@ -1235,10 +1221,15 @@ export class GBMinService {
// Detects user typed language and updates their locale profile if applies. // Detects user typed language and updates their locale profile if applies.
let locale = min.core.getParam<string>(min.instance, 'Default User Language', let locale = min.core.getParam<string>(
min.instance,
'Default User Language',
GBConfigService.get('DEFAULT_USER_LANGUAGE') GBConfigService.get('DEFAULT_USER_LANGUAGE')
); );
const detectLanguage = min.core.getParam<boolean>(min.instance, 'Language Detector', const detectLanguage =
min.core.getParam<boolean>(
min.instance,
'Language Detector',
GBConfigService.getBoolean('LANGUAGE_DETECTOR') GBConfigService.getBoolean('LANGUAGE_DETECTOR')
) === 'true'; ) === 'true';
const systemUser = user.systemUser; const systemUser = user.systemUser;
@ -1246,7 +1237,6 @@ export class GBMinService {
if (text != '' && detectLanguage && !locale) { if (text != '' && detectLanguage && !locale) {
locale = await min.conversationalService.getLanguage(min, text); locale = await min.conversationalService.getLanguage(min, text);
if (systemUser.locale != locale) { if (systemUser.locale != locale) {
user.systemUser = await sec.updateUserLocale(systemUser.userId, locale); user.systemUser = await sec.updateUserLocale(systemUser.userId, locale);
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
} }
@ -1295,32 +1285,32 @@ export class GBMinService {
const message = await min.kbService.getAnswerTextByMediaName(min.instance.instanceId, filename); const message = await min.kbService.getAnswerTextByMediaName(min.instance.instanceId, filename);
if (message === null) { if (message === null) {
GBLog.error(`File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`); GBLog.error(
`File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`
);
} else { } else {
await min.conversationalService.sendMarkdownToMobile(min, null, manualUser.userSystemId, message); await min.conversationalService.sendMarkdownToMobile(min, null, manualUser.userSystemId, message);
} }
} else {
await min.whatsAppDirectLine.sendToDeviceEx(
manualUser.userSystemId,
`${manualUser.agentSystemId}: ${text}`,
locale,
step.context.activity.conversation.id
);
} }
else { } else {
await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId, `${manualUser.agentSystemId}: ${text}`, locale, if (min.cbMap[user.systemUser.userId] && min.cbMap[user.systemUser.userId].promise == '!GBHEAR') {
step.context.activity.conversation.id);
}
}
else {
if (min.cbMap[user.systemUser.userId] &&
min.cbMap[user.systemUser.userId].promise == '!GBHEAR') {
min.cbMap[user.systemUser.userId].promise = text; 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.
else if (step.activeDialog !== undefined) { else if (step.activeDialog !== undefined) {
await step.continueDialog(); await step.continueDialog();
} else { } else {
const startDialog = user.hearOnDialog
const startDialog = user.hearOnDialog ? ? user.hearOnDialog
user.hearOnDialog : : min.core.getParam(min.instance, 'Start Dialog', null);
min.core.getParam(min.instance, 'Start Dialog', null);
if (text !== startDialog) { if (text !== startDialog) {
let nextDialog = null; let nextDialog = null;
@ -1344,7 +1334,6 @@ export class GBMinService {
user: user ? user.dataValues : null, user: user ? user.dataValues : null,
message: message message: message
}); });
} }
} }
} }

View file

@ -36,58 +36,45 @@
'use strict'; 'use strict';
const puppeteer = require('puppeteer-extra') import puppeteer from 'puppeteer-extra';
const Fs = require('fs'); import Fs from 'fs';
// const StealthPlugin = require('puppeteer-extra-plugin-stealth') // const StealthPlugin from 'puppeteer-extra-plugin-stealth')
// puppeteer.use(StealthPlugin()); // puppeteer.use(StealthPlugin());
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from 'express';
import urljoin = require("url-join"); import urljoin from 'url-join';
const Path = require('path');
// https://hackernoon.com/tips-and-tricks-for-web-scraping-with-puppeteer-ed391a63d952 // https://hackernoon.com/tips-and-tricks-for-web-scraping-with-puppeteer-ed391a63d952
// Dont download all resources, we just need the HTML // Dont download all resources, we just need the HTML
// Also, this is huge performance/response time boost // Also, this is huge performance/response time boost
const blockedResourceTypes = [ const blockedResourceTypes = ['image', 'media', 'font', 'texttrack', 'object', 'beacon', 'csp_report', 'imageset'];
"image",
"media",
"font",
"texttrack",
"object",
"beacon",
"csp_report",
"imageset",
];
// const whitelist = ["document", "script", "xhr", "fetch"]; // const whitelist = ["document", "script", "xhr", "fetch"];
const skippedResources = [ const skippedResources = [
"quantserve", 'quantserve',
"adzerk", 'adzerk',
"doubleclick", 'doubleclick',
"adition", 'adition',
"exelator", 'exelator',
"sharethrough", 'sharethrough',
"cdn.api.twitter", 'cdn.api.twitter',
"google-analytics", 'google-analytics',
"googletagmanager", 'googletagmanager',
"google", 'google',
"fontawesome", 'fontawesome',
"facebook", 'facebook',
"analytics", 'analytics',
"optimizely", 'optimizely',
"clicktale", 'clicktale',
"mixpanel", 'mixpanel',
"zedo", 'zedo',
"clicksor", 'clicksor',
"tiqcdn", 'tiqcdn'
]; ];
const RENDER_CACHE = new Map(); const RENDER_CACHE = new Map();
async function createBrowser (profilePath): Promise<any> { async function createBrowser (profilePath): Promise<any> {
let args = [ let args = [
'--check-for-update-interval=2592000', '--check-for-update-interval=2592000',
'--disable-accelerated-2d-canvas', '--disable-accelerated-2d-canvas',
@ -101,12 +88,12 @@ async function createBrowser(profilePath): Promise<any> {
if (profilePath) { if (profilePath) {
args.push(`--user-data-dir=${profilePath}`); args.push(`--user-data-dir=${profilePath}`);
const preferences = urljoin(profilePath, "Default", "Preferences"); const preferences = urljoin(profilePath, 'Default', 'Preferences');
if (Fs.existsSync(preferences)) { if (Fs.existsSync(preferences)) {
const file = Fs.readFileSync(preferences, "utf8") const file = Fs.readFileSync(preferences, 'utf8');
const data = JSON.parse(file) const data = JSON.parse(file);
data["profile"]['exit_type'] = "none"; data['profile']['exit_type'] = 'none';
Fs.writeFileSync(preferences, JSON.stringify(data)) Fs.writeFileSync(preferences, JSON.stringify(data));
} }
} }
@ -114,9 +101,8 @@ async function createBrowser(profilePath): Promise<any> {
args: args, args: args,
ignoreHTTPSErrors: true, ignoreHTTPSErrors: true,
headless: false, headless: false,
devTools: false,
defaultViewport: null, defaultViewport: null,
ignoreDefaultArgs: ["--enable-automation", "--enable-blink-features=IdleDetection"] ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection']
}); });
return browser; return browser;
} }
@ -136,9 +122,6 @@ async function recursiveFindInFrames(inputFrame, selector) {
return results.find(Boolean); return results.find(Boolean);
} }
/** /**
* https://developers.google.com/web/tools/puppeteer/articles/ssr#reuseinstance * https://developers.google.com/web/tools/puppeteer/articles/ssr#reuseinstance
* @param {string} url URL to prerender. * @param {string} url URL to prerender.
@ -146,15 +129,12 @@ async function recursiveFindInFrames(inputFrame, selector) {
async function ssr (url: string, useCache: boolean, cacheRefreshRate: number) { async function ssr (url: string, useCache: boolean, cacheRefreshRate: number) {
if (RENDER_CACHE.has(url) && useCache) { if (RENDER_CACHE.has(url) && useCache) {
const cached = RENDER_CACHE.get(url); const cached = RENDER_CACHE.get(url);
if ( if (Date.now() - cached.renderedAt > cacheRefreshRate && !(cacheRefreshRate <= 0)) {
Date.now() - cached.renderedAt > cacheRefreshRate &&
!(cacheRefreshRate <= 0)
) {
RENDER_CACHE.delete(url); RENDER_CACHE.delete(url);
} else { } else {
return { return {
html: cached.html, html: cached.html,
status: 200, status: 200
}; };
} }
} }
@ -164,14 +144,17 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
try { try {
const page = await browser.newPage(); const page = await browser.newPage();
await page.setUserAgent( await page.setUserAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
); );
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on("request", (request) => { page.on('request', request => {
const requestUrl = request.url().split("?")[0].split("#")[0]; const requestUrl = request
.url()
.split('?')[0]
.split('#')[0];
if ( if (
blockedResourceTypes.indexOf(request.resourceType()) !== -1 || blockedResourceTypes.indexOf(request.resourceType()) !== -1 ||
skippedResources.some((resource) => requestUrl.indexOf(resource) !== -1) skippedResources.some(resource => requestUrl.indexOf(resource) !== -1)
) { ) {
request.abort(); request.abort();
} else { } else {
@ -179,10 +162,10 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
} }
}); });
page.on("response", async (resp) => { page.on('response', async resp => {
const responseUrl = resp.url(); const responseUrl = resp.url();
const sameOrigin = new URL(responseUrl).origin === new URL(url).origin; const sameOrigin = new URL(responseUrl).origin === new URL(url).origin;
const isStylesheet = resp.request().resourceType() === "stylesheet"; const isStylesheet = resp.request().resourceType() === 'stylesheet';
if (sameOrigin && isStylesheet) { if (sameOrigin && isStylesheet) {
stylesheetContents[responseUrl] = await resp.text(); stylesheetContents[responseUrl] = await resp.text();
} }
@ -190,7 +173,7 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
const response = await page.goto(url, { const response = await page.goto(url, {
timeout: 120000, timeout: 120000,
waitUntil: "networkidle0", waitUntil: 'networkidle0'
}); });
const sleep = ms => { const sleep = ms => {
@ -201,8 +184,8 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
await sleep(45000); await sleep(45000);
// Inject <base> on page to relative resources load properly. // Inject <base> on page to relative resources load properly.
await page.evaluate((url) => { await page.evaluate(url => {
const base = document.createElement("base"); const base = document.createElement('base');
base.href = url; base.href = url;
// Add to top of head, before all other resources. // Add to top of head, before all other resources.
document.head.prepend(base); document.head.prepend(base);
@ -211,7 +194,7 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
// Remove scripts and html imports. They've already executed. // Remove scripts and html imports. They've already executed.
await page.evaluate(() => { await page.evaluate(() => {
const elements = document.querySelectorAll('script, link[rel="import"]'); const elements = document.querySelectorAll('script, link[rel="import"]');
elements.forEach((e) => { elements.forEach(e => {
e.remove(); e.remove();
}); });
}); });
@ -223,7 +206,7 @@ async function ssr(url: string, useCache: boolean, cacheRefreshRate: number) {
links.forEach((link: any) => { links.forEach((link: any) => {
const cssText = content[link.href]; const cssText = content[link.href];
if (cssText) { if (cssText) {
const style = document.createElement("style"); const style = document.createElement('style');
style.textContent = cssText; style.textContent = cssText;
link.replaceWith(style); link.replaceWith(style);
} }
@ -280,52 +263,51 @@ function ssrForBots(
// Default user agents // Default user agents
const prerenderArray = [ const prerenderArray = [
"bot", 'bot',
"googlebot", 'googlebot',
"Chrome-Lighthouse", 'Chrome-Lighthouse',
"DuckDuckBot", 'DuckDuckBot',
"ia_archiver", 'ia_archiver',
"bingbot", 'bingbot',
"yandex", 'yandex',
"baiduspider", 'baiduspider',
"Facebot", 'Facebot',
"facebookexternalhit", 'facebookexternalhit',
"facebookexternalhit/1.1", 'facebookexternalhit/1.1',
"twitterbot", 'twitterbot',
"rogerbot", 'rogerbot',
"linkedinbot", 'linkedinbot',
"embedly", 'embedly',
"quora link preview", 'quora link preview',
"showyoubot", 'showyoubot',
"outbrain", 'outbrain',
"pinterest", 'pinterest',
"slackbot", 'slackbot',
"vkShare", 'vkShare',
"W3C_Validator", 'W3C_Validator'
]; ];
// default exclude array // default exclude array
const excludeArray = [".xml", ".ico", ".txt", ".json"]; const excludeArray = ['.xml', '.ico', '.txt', '.json'];
function ssrOnDemand (req: Request, res: Response, next: NextFunction) { function ssrOnDemand (req: Request, res: Response, next: NextFunction) {
Promise.resolve(() => { Promise.resolve(() => {
return true; return true;
}) })
.then(async () => { .then(async () => {
const userAgent: string = req.headers["user-agent"] || ""; const userAgent: string = req.headers['user-agent'] || '';
const prerender = new RegExp( const prerender = new RegExp([...prerenderArray, ...applyOptions.prerender].join('|').slice(0, -1), 'i').test(
[...prerenderArray, ...applyOptions.prerender].join("|").slice(0, -1), userAgent
"i" );
).test(userAgent);
const exclude = !new RegExp( const exclude = !new RegExp([...excludeArray, ...applyOptions.exclude].join('|').slice(0, -1)).test(
[...excludeArray, ...applyOptions.exclude].join("|").slice(0, -1) req.originalUrl
).test(req.originalUrl); );
if (req.originalUrl && prerender && exclude) { if (req.originalUrl && prerender && exclude) {
const { html, status } = await ssr( const { html, status } = await ssr(
req.protocol + "://" + req.get("host") + req.originalUrl, req.protocol + '://' + req.get('host') + req.originalUrl,
applyOptions.useCache, applyOptions.useCache,
applyOptions.cacheRefreshRate applyOptions.cacheRefreshRate
); );
@ -340,5 +322,4 @@ function ssrForBots(
return ssrOnDemand; return ssrOnDemand;
} }
export { createBrowser, ssr, clearCache, ssrForBots }; export { createBrowser, ssr, clearCache, ssrForBots };

View file

@ -1,4 +1,3 @@
export const Messages = { export const Messages = {
global_quit: /^(\bsair\b|\bsai\b|\bchega\b|\bexit\b|\bquit\b|\bfinish\b|\bend\b|\bausfahrt\b|\bverlassen\b)/i, global_quit: /^(\bsair\b|\bsai\b|\bchega\b|\bexit\b|\bquit\b|\bfinish\b|\bend\b|\bausfahrt\b|\bverlassen\b)/i,
'en-US': { 'en-US': {
@ -6,23 +5,22 @@ export const Messages = {
good_morning: 'good morning', good_morning: 'good morning',
good_evening: 'good evening', good_evening: 'good evening',
good_night: 'good night', good_night: 'good night',
hi: (msg) => `Hello, ${msg}.`, hi: msg => `Hello, ${msg}.`,
very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`, very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`,
canceled: 'Canceled. If I can be useful, let me know how', canceled: 'Canceled. If I can be useful, let me know how',
whats_email: 'What\'s your E-mail address?', whats_email: "What's your E-mail address?",
which_language: 'Please, type the language name you would like to talk through.', which_language: 'Please, type the language name you would like to talk through.',
validation_enter_valid_email: 'Please enter a valid e-mail.', validation_enter_valid_email: 'Please enter a valid e-mail.',
language_chosen: 'Very good, so let\'s go...', language_chosen: "Very good, so let's go...",
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i, affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
will_answer_projector: will_answer_projector: "I'll answer on the projector to a better experience..."
'I\'ll answer on the projector to a better experience...',
}, },
'pt-BR': { 'pt-BR': {
show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...', show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...',
good_morning: 'bom dia', good_morning: 'bom dia',
good_evening: 'boa tarde', good_evening: 'boa tarde',
good_night: 'boa noite', good_night: 'boa noite',
hi: (msg) => `Oi, ${msg}.`, hi: msg => `Oi, ${msg}.`,
very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`, very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`,
canceled: 'Cancelado, avise como posso ser útil novamente.', canceled: 'Cancelado, avise como posso ser útil novamente.',
whats_email: 'Qual seu e-mail?', whats_email: 'Qual seu e-mail?',
@ -30,8 +28,6 @@ export const Messages = {
validation_enter_valid_email: 'Por favor digite um email válido.', validation_enter_valid_email: 'Por favor digite um email válido.',
language_chosen: 'Muito bem, então vamos lá...', language_chosen: 'Muito bem, então vamos lá...',
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i, affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
will_answer_projector: will_answer_projector: 'Vou te responder na tela para melhor visualização...'
'Vou te responder na tela para melhor visualização...',
} }
}; };

View file

@ -39,11 +39,11 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBMinService } from '../../core.gbapp/services/GBMinService'; import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { CSService } from '../services/CSService'; import { CSService } from '../services/CSService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog for feedback collecting. * Dialog for feedback collecting.
@ -74,13 +74,11 @@ export class FeedbackDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const sec = new SecService(); const sec = new SecService();
let from = GBMinService.userMobile(step); let from = GBMinService.userMobile(step);
@ -90,40 +88,39 @@ export class FeedbackDialog extends IGBDialog {
// Transfer to... // Transfer to...
if (args && args.to) { if (args && args.to) {
// An user from Teams willing to transfer to a WhatsApp user. // An user from Teams willing to transfer to a WhatsApp user.
await sec.ensureUser(min.instance.instanceId, args.to, await sec.ensureUser(min.instance.instanceId, args.to, 'Name', '', 'whatsapp', 'Name', null);
'Name', '', 'whatsapp', 'Name', null);
await sec.assignHumanAgent(min, args.to, profile.systemUser.userSystemId); await sec.assignHumanAgent(min, args.to, profile.systemUser.userSystemId);
await min.conversationalService.sendText(min, step, await min.conversationalService.sendText(
Messages[locale].notify_agent_transfer_done(min.instance.botId)); min,
step,
} Messages[locale].notify_agent_transfer_done(min.instance.botId)
else { );
} else {
await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering); await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering);
const agentSystemId = await sec.assignHumanAgent(min, from); const agentSystemId = await sec.assignHumanAgent(min, from);
profile.systemUser = await sec.getUserFromAgentSystemId(agentSystemId); profile.systemUser = await sec.getUserFromAgentSystemId(agentSystemId);
await min.userProfile.set(step.context, profile); await min.userProfile.set(step.context, profile);
if (agentSystemId.charAt(2) === ":" || agentSystemId.indexOf("@") > -1) { // Agent is from Teams or Google Chat. if (agentSystemId.charAt(2) === ':' || agentSystemId.indexOf('@') > -1) {
// Agent is from Teams or Google Chat.
const agent = await sec.getUserFromSystemId(agentSystemId); const agent = await sec.getUserFromSystemId(agentSystemId);
await min.conversationalService['sendOnConversation'](min, agent, await min.conversationalService['sendOnConversation'](
Messages[locale].notify_agent(step.context.activity.from.name)); min,
agent,
} Messages[locale].notify_agent(step.context.activity.from.name)
else { );
} else {
await min.whatsAppDirectLine.sendToDevice(agentSystemId, Messages[locale].notify_agent(step.context.activity.from.name)); await min.whatsAppDirectLine.sendToDevice(
agentSystemId,
Messages[locale].notify_agent(step.context.activity.from.name)
);
} }
} }
return await step.next(); return await step.next();
} }
]) ])
); );
@ -133,13 +130,11 @@ export class FeedbackDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const sec = new SecService(); const sec = new SecService();
@ -149,16 +144,27 @@ export class FeedbackDialog extends IGBDialog {
if (user.systemUser.agentMode === 'self') { if (user.systemUser.agentMode === 'self') {
const manualUser = await sec.getUserFromAgentSystemId(userSystemId); const manualUser = await sec.getUserFromAgentSystemId(userSystemId);
await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId, await min.whatsAppDirectLine.sendToDeviceEx(
Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); 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. if (userSystemId.charAt(2) === ':' || userSystemId.indexOf('@') > -1) {
await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); // Agent is from Teams or Google Chat.
} await min.conversationalService.sendText(
else { min,
await min.whatsAppDirectLine.sendToDeviceEx(userSystemId, step,
Messages[locale].notify_end_transfer(min.instance.botId), locale Messages[locale].notify_end_transfer(min.instance.botId)
, step.context.activity.conversation.id); );
} 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(userSystemId, min.instance.instanceId, null);
@ -166,22 +172,30 @@ export class FeedbackDialog extends IGBDialog {
user.systemUser = await sec.getUserFromSystemId(userSystemId); user.systemUser = await sec.getUserFromSystemId(userSystemId);
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
} else if (user.systemUser.agentMode === 'human') {
}
else if (user.systemUser.agentMode === 'human') {
const agent = await sec.getUserFromSystemId(user.systemUser.agentSystemId); const agent = await sec.getUserFromSystemId(user.systemUser.agentSystemId);
await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, await min.whatsAppDirectLine.sendToDeviceEx(
Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); 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) {
if (user.systemUser.agentSystemId.charAt(2) === ":" || userSystemId.indexOf('@') > -1) { // Agent is from Teams or Google Chat. // Agent is from Teams or Google Chat.
await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId)); await min.conversationalService.sendText(
} min,
else { step,
await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.agentSystemId, Messages[locale].notify_end_transfer(min.instance.botId)
Messages[locale].notify_end_transfer(min.instance.botId), locale, step.context.activity.conversation.id); );
} 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(user.systemUser.userSystemId, min.instance.instanceId, null);
@ -189,15 +203,17 @@ export class FeedbackDialog extends IGBDialog {
user.systemUser = await sec.getUserFromSystemId(userSystemId); user.systemUser = await sec.getUserFromSystemId(userSystemId);
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
} else {
} if (user.systemUser.userSystemId.charAt(2) === ':' || userSystemId.indexOf('@') > -1) {
else { // Agent is from Teams or Google Chat.
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.'); await min.conversationalService.sendText(min, step, 'Nenhum atendimento em andamento.');
} } else {
else { await min.whatsAppDirectLine.sendToDeviceEx(
await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId, user.systemUser.userSystemId,
'Nenhum atendimento em andamento.', locale, step.context.activity.conversation.id); 'Nenhum atendimento em andamento.',
locale,
step.context.activity.conversation.id
);
} }
} }
@ -211,8 +227,7 @@ export class FeedbackDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -238,8 +253,7 @@ export class FeedbackDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -259,14 +273,20 @@ export class FeedbackDialog extends IGBDialog {
const analytics = new AnalyticsService(); const analytics = new AnalyticsService();
const rate = await analytics.updateConversationSuggestion( const rate = await analytics.updateConversationSuggestion(
min.instance.instanceId, user.conversation.conversationId, step.result, user.systemUser.locale); min.instance.instanceId,
user.conversation.conversationId,
step.result,
user.systemUser.locale
);
if (rate > 0.5) { if (rate > 0.5) {
await min.conversationalService.sendText(min, step, Messages[fixedLocale].glad_you_liked); await min.conversationalService.sendText(min, step, Messages[fixedLocale].glad_you_liked);
} else { } else {
const message = min.core.getParam<string>(
const message = min.core.getParam<string>(min.instance, 'Feedback Improve Message', min.instance,
Messages[fixedLocale].we_will_improve); // TODO: Improve to be multi-language. 'Feedback Improve Message',
Messages[fixedLocale].we_will_improve
); // TODO: Improve to be multi-language.
await min.conversationalService.sendText(min, step, message); await min.conversationalService.sendText(min, step, message);
} }

View file

@ -40,10 +40,10 @@ import { GBMinInstance, IGBDialog } from 'botlib';
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService'; import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { CSService } from '../services/CSService'; import { CSService } from '../services/CSService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog for collecting quality of answer. * Dialog for collecting quality of answer.
@ -58,16 +58,18 @@ export class QualityDialog extends IGBDialog {
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
const service = new CSService(); const service = new CSService();
min.dialogs.add(new WaterfallDialog('/check', [ min.dialogs.add(
new WaterfallDialog('/check', [
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, Messages[locale].check_whatsapp_ok); await min.conversationalService.sendText(min, step, Messages[locale].check_whatsapp_ok);
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });
} }
] ])
)); );
min.dialogs.add(new WaterfallDialog('/quality', [ min.dialogs.add(
new WaterfallDialog('/quality', [
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const user = await min.userProfile.get(step.context, {}); const user = await min.userProfile.get(step.context, {});
@ -83,7 +85,7 @@ export class QualityDialog extends IGBDialog {
await min.conversationalService.sendEvent(min, step, 'play', { await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'markdown', playerType: 'markdown',
data: { data: {
content: Messages[locale].great_thanks, content: Messages[locale].great_thanks
} }
}); });
let sleep = ms => { let sleep = ms => {
@ -92,23 +94,24 @@ export class QualityDialog extends IGBDialog {
}); });
}; };
await service.insertQuestionAlternate( await service.insertQuestionAlternate(min.instance.instanceId, user.lastQuestion, user.lastQuestionId);
min.instance.instanceId,
user.lastQuestion,
user.lastQuestionId
);
// Updates values to perform Bot Analytics. // Updates values to perform Bot Analytics.
const analytics = new AnalyticsService(); const analytics = new AnalyticsService();
analytics.updateConversationSuggestion( analytics.updateConversationSuggestion(
min.instance.instanceId, user.conversation, step.result, user.systemUser.locale); min.instance.instanceId,
user.conversation,
step.result,
user.systemUser.locale
);
// Goes to the ask loop. // Goes to the ask loop.
return await step.replaceDialog('/ask', { emptyPrompt: true }); return await step.replaceDialog('/ask', { emptyPrompt: true });
} }
} }
])); ])
);
} }
} }

View file

@ -37,10 +37,10 @@
'use strict'; 'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import { FeedbackDialog } from './dialogs/FeedbackDialog'; import { FeedbackDialog } from './dialogs/FeedbackDialog.js';
import { QualityDialog } from './dialogs/QualityDialog'; import { QualityDialog } from './dialogs/QualityDialog.js';
import { GuaribasQuestionAlternate } from './models/index'; import { GuaribasQuestionAlternate } from './models/index.js';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';

View file

@ -36,40 +36,30 @@
'use strict'; 'use strict';
import { import { AutoIncrement, BelongsTo, Column, DataType, ForeignKey, Model, PrimaryKey, Table } from 'sequelize-typescript';
AutoIncrement,
BelongsTo,
Column,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table
} from 'sequelize-typescript';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
/** /**
* List of saved alternate questions. * List of saved alternate questions.
*/ */
@Table @Table
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> { export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public quickAnswerId: number; quickAnswerId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public questionTyped: string; questionTyped: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public questionText: string; questionText: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
} }

View file

@ -31,19 +31,15 @@
\*****************************************************************************/ \*****************************************************************************/
import { FindOptions, NonNullFindOptions } from 'sequelize/types'; import { FindOptions, NonNullFindOptions } from 'sequelize/types';
import { GuaribasQuestion } from '../../../packages/kb.gbapp/models'; import { GuaribasQuestion } from '../../../packages/kb.gbapp/models/index.js';
import { GuaribasConversation } from '../../analytics.gblib/models'; import { GuaribasConversation } from '../../analytics.gblib/models/index.js';
import { GuaribasQuestionAlternate } from '../models'; import { GuaribasQuestionAlternate } from '../models/index.js';
/** /**
* Customer Satisfaction Service Layer. * Customer Satisfaction Service Layer.
*/ */
export class CSService { export class CSService {
public async getQuestionFromAlternateText (instanceId: number, text: string): Promise<GuaribasQuestion> {
public async getQuestionFromAlternateText(
instanceId: number,
text: string): Promise<GuaribasQuestion> {
const questionAlternate = await GuaribasQuestionAlternate.findOne({ const questionAlternate = await GuaribasQuestionAlternate.findOne({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
@ -54,7 +50,6 @@ export class CSService {
let question: GuaribasQuestion = null; let question: GuaribasQuestion = null;
if (questionAlternate !== null) { if (questionAlternate !== null) {
question = await GuaribasQuestion.findOne({ question = await GuaribasQuestion.findOne({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
@ -69,7 +64,8 @@ export class CSService {
public async insertQuestionAlternate ( public async insertQuestionAlternate (
instanceId: number, instanceId: number,
questionTyped: string, questionTyped: string,
questionText: string): Promise<GuaribasQuestionAlternate> { questionText: string
): Promise<GuaribasQuestionAlternate> {
return await GuaribasQuestionAlternate.create(<GuaribasQuestionAlternate>{ return await GuaribasQuestionAlternate.create(<GuaribasQuestionAlternate>{
questionTyped: questionTyped, questionTyped: questionTyped,
questionText: questionText questionText: questionText

View file

@ -1,20 +1,20 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
about_suggestions: 'Suggestions are welcomed and improve my quality...', about_suggestions: 'Suggestions are welcomed and improve my quality...',
what_about_service: 'What about my service?', what_about_service: 'What about my service?',
glad_you_liked: 'I\'m glad you liked. I\'m here for you.', glad_you_liked: "I'm glad you liked. I'm here for you.",
we_will_improve: 'Let\'s take note of that, thanks for sharing.', we_will_improve: "Let's take note of that, thanks for sharing.",
what_about_me: 'What about the service, please rate between 1 and 5.', what_about_me: 'What about the service, please rate between 1 and 5.',
thanks: 'Thanks!', thanks: 'Thanks!',
im_sorry_lets_try: 'I\'m sorry. Let\'s try again...', im_sorry_lets_try: "I'm sorry. Let's try again...",
great_thanks: 'Great, thanks for sharing your thoughts.', great_thanks: 'Great, thanks for sharing your thoughts.',
please_no_bad_words: 'Please, no bad words.', please_no_bad_words: 'Please, no bad words.',
please_wait_transfering: 'Please, wait while I find an agent to answer you.', 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_agent: name =>
notify_end_transfer: (botName) => `All messages will be now routed to user ${botName}.`, `New call available for *${name}*, you can answer right here when you are finished, type /qt.`,
notify_agent_transfer_done: (person) => `Now talking directly to ${person}.`, notify_end_transfer: botName => `All messages will be now routed to user ${botName}.`,
check_whatsapp_ok: 'If you are seeing this message, WhatsApp API is OK.', 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': { 'pt-BR': {
about_suggestions: 'Sugestões melhoram muito minha qualidade...', about_suggestions: 'Sugestões melhoram muito minha qualidade...',
@ -27,9 +27,10 @@ export const Messages = {
great_thanks: 'Ótimo, obrigado por contribuir com sua resposta.', great_thanks: 'Ótimo, obrigado por contribuir com sua resposta.',
please_no_bad_words: 'Por favor, sem palavrões!', please_no_bad_words: 'Por favor, sem palavrões!',
please_wait_transfering: 'Por favor, aguarde enquanto eu localizo alguém para te atender.', 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_agent: name =>
notify_end_transfer: (botName) => `Falando novamente com o bot ${botName}.`, `Existe um novo atendimento para *${name}*, por favor, responda aqui mesmo para a pessoa. Para finalizar, digite /qt.`,
notify_agent_transfer_done: (person) => `Todas as mensagens agora sendo transmitidas para ${person}.`, notify_end_transfer: botName => `Falando novamente com o bot ${botName}.`,
check_whatsapp_ok: 'Se você está recebendo esta mensagem, significa que a API do WhatsApp está OK.', 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.'
} }
}; };

File diff suppressed because it is too large Load diff

View file

@ -7,27 +7,26 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"homepage": ".", "homepage": ".",
"dependencies": { "dependencies": {
"@midudev/react-static-content": "^1.0.4", "@midudev/react-static-content": "1.0.4",
"ajv": "^8.6.0", "ajv": "8.11.2",
"botframework-directlinejs": "0.14.1", "botframework-directlinejs": "0.15.1",
"botframework-webchat": "^4.13.0", "botframework-webchat": "4.15.5",
"deep-extend": "0.6.0", "deep-extend": "0.6.0",
"eslint": "7.11.0", "eslint": "8.28.0",
"fetch": "1.1.0", "fetch": "1.1.0",
"msal": "^1.4.11", "msal": "1.4.17",
"powerbi-client": "2.18.0", "powerbi-client": "2.22.0",
"react": "^17.0.2", "react": "18.2.0",
"react-dom": "^17.0.2", "react-dom": "18.2.0",
"react-helmet": "6.1.0", "react-helmet": "6.1.0",
"react-modern-audio-player": "^1.2.2", "react-player": "2.11.0",
"react-player": "^2.9.0",
"react-powerbi": "0.9.1", "react-powerbi": "0.9.1",
"react-scripts": "^4.0.3", "react-scripts": "5.0.1",
"react-super-seo": "^1.0.6", "react-super-seo": "1.0.7",
"react-transition-group": "^4.4.2", "react-transition-group": "4.4.5",
"rxjs": "^7.1.0", "rxjs": "7.5.7",
"url-join": "4.0.1", "url-join": "5.0.0",
"webpack": "4.44.2" "webpack": "5.75.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

File diff suppressed because it is too large Load diff

View file

@ -38,7 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { GoogleChatDirectLine } from './services/GoogleChatDirectLine'; import { GoogleChatDirectLine } from './services/GoogleChatDirectLine.js';
/** /**
* Package for GoogleChat.gblib * Package for GoogleChat.gblib
@ -46,9 +46,7 @@ import { GoogleChatDirectLine } from './services/GoogleChatDirectLine';
export class GBGoogleChatPackage implements IGBPackage { export class GBGoogleChatPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public async loadBot(min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {}
}
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`); GBLog.verbose(`getDialogs called.`);
@ -68,5 +66,4 @@ export class GBGoogleChatPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -30,20 +30,19 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
const Swagger = require('swagger-client'); import Swagger from 'swagger-client';
const fs = require('fs'); import { google } from 'googleapis';
const { google } = require('googleapis') import { promisify } from 'util';
const { promisify } = require('util'); import { PubSub } from '@google-cloud/pubsub';
const { PubSub } = require('@google-cloud/pubsub'); import Fs from 'fs';
import { GBLog, GBMinInstance, GBService } from 'botlib'; import { GBLog, GBMinInstance, GBService } from 'botlib';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
/** /**
* Support for Google Chat. * Support for Google Chat.
*/ */
export class GoogleChatDirectLine extends GBService { export class GoogleChatDirectLine extends GBService {
public static conversationIds = {}; public static conversationIds = {};
public pollInterval = 5000; public pollInterval = 5000;
public directLineClientName = 'DirectLineClient'; public directLineClientName = 'DirectLineClient';
@ -84,7 +83,6 @@ export class GoogleChatDirectLine extends GBService {
projectId: this.GoogleProjectId, projectId: this.GoogleProjectId,
credentials: { client_email: GoogleClientEmail, private_key: GoogleClientPrivateKey } credentials: { client_email: GoogleClientEmail, private_key: GoogleClientPrivateKey }
}); });
} }
public static async asyncForEach (array, callback) { public static async asyncForEach (array, callback) {
@ -94,10 +92,8 @@ export class GoogleChatDirectLine extends GBService {
} }
public async setup (setUrl) { public async setup (setUrl) {
this.directLineClient = new Swagger({
this.directLineClient = spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
new Swagger({
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')),
usePromise: true usePromise: true
}); });
const client = await this.directLineClient; const client = await this.directLineClient;
@ -109,15 +105,12 @@ export class GoogleChatDirectLine extends GBService {
if (setUrl) { if (setUrl) {
try { try {
const subscription = this.pubSubClient.subscription(this.GoogleChatSubscriptionName); const subscription = this.pubSubClient.subscription(this.GoogleChatSubscriptionName);
subscription.on('message', this.receiver.bind(this)); subscription.on('message', this.receiver.bind(this));
} catch (error) { } catch (error) {
GBLog.error(`Error initializing 3rd party GoogleChat provider(1) ${error.message}`); GBLog.error(`Error initializing 3rd party GoogleChat provider(1) ${error.message}`);
} }
} }
} }
public async resetConversationId (key) { public async resetConversationId (key) {
@ -125,13 +118,11 @@ export class GoogleChatDirectLine extends GBService {
} }
public async check () { public async check () {
GBLog.info(`GBGoogleChat: Checking server...`); GBLog.info(`GBGoogleChat: Checking server...`);
} }
// TODO: Check service.Users.Messages.List("me"). // TODO: Check service.Users.Messages.List("me").
public async receiver (message) { public async receiver (message) {
const event = JSON.parse(Buffer.from(message.data, 'binary').toString()); const event = JSON.parse(Buffer.from(message.data, 'binary').toString());
let from = ''; let from = '';
@ -140,7 +131,6 @@ export class GoogleChatDirectLine extends GBService {
const threadName = event.message.thread.name; const threadName = event.message.thread.name;
if (event['type'] === 'ADDED_TO_SPACE' && event['space']['singleUserBotDm']) { if (event['type'] === 'ADDED_TO_SPACE' && event['space']['singleUserBotDm']) {
} else if (event['type'] === 'MESSAGE') { } else if (event['type'] === 'MESSAGE') {
text = event.message.text; text = event.message.text;
fromName = event.message.sender.displayName; fromName = event.message.sender.displayName;
@ -150,8 +140,7 @@ export class GoogleChatDirectLine extends GBService {
message.ack(); message.ack();
const sec = new SecService(); const sec = new SecService();
const user = await sec.ensureUser(this.min.instance.instanceId, from, const user = await sec.ensureUser(this.min.instance.instanceId, from, from, '', 'googlechat', fromName, from);
from, '', 'googlechat', fromName, from);
await sec.updateConversationReferenceById(user.userId, threadName); await sec.updateConversationReferenceById(user.userId, threadName);
@ -170,7 +159,6 @@ export class GoogleChatDirectLine extends GBService {
this.pollMessages(client, generatedConversationId, threadName, from, fromName); this.pollMessages(client, generatedConversationId, threadName, from, fromName);
this.inputMessage(client, generatedConversationId, threadName, text, from, fromName); this.inputMessage(client, generatedConversationId, threadName, text, from, fromName);
} else { } else {
this.inputMessage(client, conversationId, threadName, text, from, fromName); this.inputMessage(client, conversationId, threadName, text, from, fromName);
} }
} }
@ -253,26 +241,17 @@ export class GoogleChatDirectLine extends GBService {
} }
public async sendToDevice (from: string, conversationId: string, threadName, msg: string) { public async sendToDevice (from: string, conversationId: string, threadName, msg: string) {
try { try {
let threadParts = threadName.split('/'); let threadParts = threadName.split('/');
let spaces = threadParts[1]; let spaces = threadParts[1];
let threadKey = threadParts[3]; let threadKey = threadParts[3];
const scopes = ['https://www.googleapis.com/auth/chat.bot']; const scopes = ['https://www.googleapis.com/auth/chat.bot'];
const jwtClient = new google.auth.JWT( const jwtClient = new google.auth.JWT(this.GoogleClientEmail, null, this.GoogleClientPrivateKey, scopes, null);
this.GoogleClientEmail,
null,
this.GoogleClientPrivateKey,
scopes,
null
);
await jwtClient.authorize(); await jwtClient.authorize();
const chat = google.chat({ version: 'v1', auth: jwtClient }); const chat = google.chat({ version: 'v1', auth: jwtClient });
chat.spaces.messages.createAsync = promisify(chat.spaces.messages.create);
const res = await chat.spaces.messages.createAsync({ const res = await chat.spaces.messages.create({
parent: `spaces/${spaces}`, parent: `spaces/${spaces}`,
threadKey: threadKey, threadKey: threadKey,
requestBody: { requestBody: {
@ -286,15 +265,10 @@ export class GoogleChatDirectLine extends GBService {
} }
} }
public async sendToDeviceEx (to, conversationId, threadName, text, locale) { public async sendToDeviceEx (to, conversationId, threadName, text, locale) {
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
text = await minBoot.conversationalService.translate( text = await minBoot.conversationalService.translate(minBoot, text, locale);
minBoot,
text,
locale
);
await this.sendToDevice(to, conversationId, threadName, text); await this.sendToDevice(to, conversationId, threadName, text);
} }
} }

View file

@ -1,8 +1,8 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
notify_end_transfer: (botName) => `Now talking to ${botName} again.` notify_end_transfer: botName => `Now talking to ${botName} again.`
}, },
'pt-BR': { 'pt-BR': {
notify_end_transfer: (botName) => `Falando com o bot ${botName} novamente.` notify_end_transfer: botName => `Falando com o bot ${botName} novamente.`
} }
}; };

View file

@ -45,9 +45,7 @@ import { Sequelize } from 'sequelize-typescript';
export class GBHubSpotPackage implements IGBPackage { export class GBHubSpotPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public async loadBot(min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {}
}
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`); GBLog.verbose(`getDialogs called.`);
@ -67,5 +65,4 @@ export class GBHubSpotPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -30,171 +30,12 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
const Swagger = require('swagger-client');
const fs = require('fs');
const { promisify } = require('util');
import { GBLog, GBMinInstance, GBService } from 'botlib'; import { GBLog, GBMinInstance, GBService } from 'botlib';
import { GBServer } from '../../../src/app'; import { promisify } from 'util';
import { SecService } from '../../security.gbapp/services/SecService'; import Swagger from 'swagger-client';
const hubspot = require('@hubspot/api-client'); import * as hubspot from '@hubspot/api-client';
/** /**
* Support for Hub Spot XRM. * Support for Hub Spot XRM.
*/ */
export class HubSpotServices extends GBService { export class HubSpotServices extends GBService {}
public static conversationIds = {};
public pollInterval = 5000;
public botId: string;
public min: GBMinInstance;
private key: any;
constructor(
min: GBMinInstance,
botId,
key
) {
super();
this.min = min;
this.botId = botId;
this.key = key;
}
public static async asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
public async addDealNote(name, note)
{
}
public async createDeal(dealName, contact, company, amount) {
const dealObj = {
properties: {
dealname: dealName,
dealstage: 'appointmentscheduled',
pipeline: 'default',
amount: amount
},
}
const contactObj = {
properties: {
firstname: contact
},
}
const companyObj = {
properties: {
name: company,
},
}
const hubspotClient = new hubspot.Client({ apiKey: this.key });
const createDealResponse = await hubspotClient.crm.deals.basicApi.create(dealObj);
const createContactResponse = await hubspotClient.crm.contacts.basicApi.create(contactObj);
const createCompanyResponse = await hubspotClient.crm.companies.basicApi.create(companyObj);
await hubspotClient.crm.deals.associationsApi.create(
createDealResponse.body.id,
'contacts',
createContactResponse.body.id,
'deal_to_contact'
)
await hubspotClient.crm.deals.associationsApi.create(
createDealResponse.body.id,
'companies',
createCompanyResponse.body.id,
'deal_to_company'
)
return createDealResponse.body;
}
public async createContact(firstName, lastName, domain, companyName) {
const contactObj = {
properties: {
firstname: firstName,
lastname: lastName,
},
}
const companyObj = {
properties: {
domain: domain,
name: companyName,
},
}
const hubspotClient = new hubspot.Client({ apiKey: this.key })
const createContactResponse = await hubspotClient.crm.contacts.basicApi.create(contactObj)
const createCompanyResponse = await hubspotClient.crm.companies.basicApi.create(companyObj)
return await hubspotClient.crm.companies.associationsApi.create(
createCompanyResponse.body.id,
'contacts',
createContactResponse.body.id,
'company_to_contact'
)
}
public async searchContact(query) {
const client = new hubspot.Client({ apiKey: this.key });
const sort = JSON.stringify({ propertyName: 'createdate', direction: 'DESCENDING' })
const properties = ['createdate', 'firstname', 'lastname', 'phone', 'email']
const limit = 100
const after = 0
const publicObjectSearchRequest = {
sorts: [sort],
query,
properties,
limit,
after,
}
const result = await client.crm.contacts.searchApi.doSearch(publicObjectSearchRequest)
return result.body.results;
}
public async getActiveTasks(): Promise<[]> {
const client = new hubspot.Client({ apiKey: this.key });
let properties = ['hs_task_subject', 'hubspot_owner_id', 'hs_task_status', 'hs_task_priority'];
const pageSize = 100;
let list;
list = [];
let r = await client.crm.objects.basicApi.getPage("TASK", pageSize, 0, properties);
list = list.concat(r.body.results);
while (r.body.results && r.body.results.length === pageSize) {
r = await client.crm.objects.basicApi.getPage("TASK", pageSize, r.body.paging.next.after, properties);
list = list.concat(r.body.results);
}
let final;
final = [];
list.forEach(e => {
if (e.properties.hs_task_status === "NOT_STARTED") {
e['status'] = e.properties.hs_task_status;
e['title'] = e.properties.hs_task_subject;
e['ownerId'] = e.properties.hubspot_owner_id;
e['priority'] = e.properties.hs_task_priority;
final.push(e);
}
});
return final;
}
}

View file

@ -1,8 +1,4 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {},
'pt-BR': {}
},
'pt-BR': {
}
}; };

View file

@ -36,19 +36,19 @@
'use strict'; 'use strict';
import { GBServer } from '../../../src/app'; import { GBServer } from '../../../src/app.js';
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, IGBDialog, IGBPackage } from 'botlib'; import { GBLog, GBMinInstance, IGBDialog, IGBPackage } from 'botlib';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
import { KBService } from './../services/KBService'; import { KBService } from './../services/KBService.js';
import { GuaribasAnswer } from '../models'; import { GuaribasAnswer } from '../models/index.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework'; import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { GBVMService } from '../../basic.gblib/services/GBVMService'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService'; import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
/** /**
* Dialog arguments. * Dialog arguments.
@ -84,8 +84,7 @@ export class AskDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -101,9 +100,9 @@ export class AskDialog extends IGBDialog {
if (step.options && step.options.firstTime) { if (step.options && step.options.firstTime) {
text = Messages[locale].ask_first_time; text = Messages[locale].ask_first_time;
} else if (step.options && step.options.isReturning) { } else if (step.options && step.options.isReturning) {
text = ""; // REMOVED: Messages[locale].anything_else; text = ''; // REMOVED: Messages[locale].anything_else;
} else if (step.options && step.options.emptyPrompt) { } else if (step.options && step.options.emptyPrompt) {
text = ""; text = '';
} else if (user.subjects.length > 0) { } else if (user.subjects.length > 0) {
text = Messages[locale].which_question; text = Messages[locale].which_question;
} else { } else {
@ -111,7 +110,6 @@ export class AskDialog extends IGBDialog {
} }
return await min.conversationalService.prompt(min, step, text); return await min.conversationalService.prompt(min, step, text);
}, },
async step => { async step => {
if (step.result) { if (step.result) {
@ -120,7 +118,15 @@ export class AskDialog extends IGBDialog {
let sec = new SecService(); let sec = new SecService();
const member = step.context.activity.from; const member = step.context.activity.from;
const user = await sec.ensureUser(min.instance.instanceId, member.id, member.name, '', 'web', member.name, null); const user = await sec.ensureUser(
min.instance.instanceId,
member.id,
member.name,
'',
'web',
member.name,
null
);
let handled = false; let handled = false;
let nextDialog = null; let nextDialog = null;
@ -132,9 +138,7 @@ export class AskDialog extends IGBDialog {
user: user ? user['dataValues'] : null user: user ? user['dataValues'] : null
}; };
await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => { await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => {
if ( if ((nextDialog = await e.onExchangeData(min, 'handleAnswer', data))) {
nextDialog = await e.onExchangeData(min, 'handleAnswer', data)
) {
handled = true; handled = true;
} }
}); });
@ -158,8 +162,7 @@ export class AskDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -176,10 +179,9 @@ export class AskDialog extends IGBDialog {
// when people type just the @botName in MSTEAMS for example. // when people type just the @botName in MSTEAMS for example.
if (!text) { if (!text) {
const startDialog = const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
min.core.getParam(min.instance, 'Start Dialog', null);
if (startDialog) { if (startDialog) {
await GBVMService.callVM(startDialog.toLowerCase().trim(), min, step, this.deployer); await GBVMService.callVM(startDialog.toLowerCase().trim(), min, step, this.deployer, false);
} }
return step.endDialog(); return step.endDialog();
@ -201,8 +203,11 @@ export class AskDialog extends IGBDialog {
// Searches KB for the first time. // Searches KB for the first time.
const searchScore = min.core.getParam(min.instance, 'Search Score', const searchScore = min.core.getParam(
min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore); min.instance,
'Search Score',
min.instance.searchScore ? min.instance.searchScore : minBoot.instance.searchScore
);
user.lastQuestion = text; user.lastQuestion = text;
await min.userProfile.set(step.context, user); await min.userProfile.set(step.context, user);
@ -256,7 +261,7 @@ export class AskDialog extends IGBDialog {
// Tries to answer by NLP. // Tries to answer by NLP.
let nextDialog = await min.conversationalService["routeNLP2"](step, min, text); let nextDialog = await min.conversationalService['routeNLP2'](step, min, text);
if (nextDialog) { if (nextDialog) {
return nextDialog; return nextDialog;
} }
@ -270,30 +275,29 @@ export class AskDialog extends IGBDialog {
const docs = await min.kbService['getDocs'](min.instance.instanceId); const docs = await min.kbService['getDocs'](min.instance.instanceId);
await CollectionUtil.asyncForEach(docs, async (doc: GuaribasAnswer) => { await CollectionUtil.asyncForEach(docs, async (doc: GuaribasAnswer) => {
if (!answered) { if (!answered) {
const answerText = await min.kbService['readComprehension'](min.instance.instanceId, doc.content, text); const answerText = await min.kbService['readComprehension'](min.instance.instanceId, doc.content, text);
answered = true; answered = true;
text = await min.conversationalService.translate(min, text, user.systemUser.locale text = await min.conversationalService.translate(
min,
text,
user.systemUser.locale
? 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'))
);
await min.conversationalService.sendText(min, step, answerText); await min.conversationalService.sendText(min, step, answerText);
await min.conversationalService.sendEvent(min, step, 'stop', undefined); await min.conversationalService.sendEvent(min, step, 'stop', undefined);
} }
}); });
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });
} }
// Not found. // Not found.
const message = min.core.getParam<string>(min.instance, 'Not Found Message', const message = min.core.getParam<string>(min.instance, 'Not Found Message', Messages[locale].did_not_find);
Messages[locale].did_not_find);
await min.conversationalService.sendText(min, step, message); await min.conversationalService.sendText(min, step, message);
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });
} }
]; ];
} }
@ -301,10 +305,8 @@ export class AskDialog extends IGBDialog {
private static async handleAnswer (service: KBService, min: GBMinInstance, step: any, answer: GuaribasAnswer) { private static async handleAnswer (service: KBService, min: GBMinInstance, step: any, answer: GuaribasAnswer) {
const text = answer.content; const text = answer.content;
if (text.endsWith('.docx')) { if (text.endsWith('.docx')) {
const mainName = GBVMService.getMethodNameFromVBSFilename(text); const mainName = GBVMService.getMethodNameFromVBSFilename(text);
return await GBVMService.callVM(mainName, min, step, this.deployer); return await GBVMService.callVM(mainName, min, step, this.deployer, false);
} else { } else {
await service.sendAnswer(min, AskDialog.getChannel(step), step, answer); await service.sendAnswer(min, AskDialog.getChannel(step), step, answer);
return await step.replaceDialog('/ask', { isReturning: true }); return await step.replaceDialog('/ask', { isReturning: true });
@ -320,8 +322,7 @@ export class AskDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else {
return await step.next(step.options); return await step.next(step.options);
} }
}, },

View file

@ -39,9 +39,9 @@
import { BotAdapter } from 'botbuilder'; import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
import { KBService } from './../services/KBService'; import { KBService } from './../services/KBService.js';
/** /**
* Handle display of FAQ allowing direct access to KB. * Handle display of FAQ allowing direct access to KB.
@ -54,15 +54,14 @@ export class FaqDialog extends IGBDialog {
* @param min The minimal bot instance data. * @param min The minimal bot instance data.
*/ */
public static setup (bot: BotAdapter, min: GBMinInstance) { public static setup (bot: BotAdapter, min: GBMinInstance) {
const service = new KBService(min.core.sequelize); const service = new KBService(min.core.sequelize);
min.dialogs.add(new WaterfallDialog('/faq', [ min.dialogs.add(
new WaterfallDialog('/faq', [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -81,6 +80,7 @@ export class FaqDialog extends IGBDialog {
return await step.next(); return await step.next();
} }
} }
])); ])
);
} }
} }

View file

@ -36,15 +36,15 @@
'use strict'; 'use strict';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder'; import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs'; import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib'; import { GBMinInstance, IGBDialog } from 'botlib';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { GuaribasSubject } from '../models'; import { GuaribasSubject } from '../models/index.js';
import { KBService } from '../services/KBService'; import { KBService } from '../services/KBService.js';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialog arguments. * Dialog arguments.
@ -75,8 +75,7 @@ export class MenuDialog extends IGBDialog {
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -103,8 +102,7 @@ export class MenuDialog extends IGBDialog {
// Whenever a subject is selected, shows a faq about it. // Whenever a subject is selected, shows a faq about it.
if (user.subjects.length > 0) { if (user.subjects.length > 0) {
const list = await service.getFaqBySubjectArray(min.instance.instanceId, const list = await service.getFaqBySubjectArray(min.instance.instanceId, 'menu', user.subjects);
'menu', user.subjects);
await min.conversationalService.sendEvent(min, step, 'play', { await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'bullet', playerType: 'bullet',
data: list.slice(0, 10) data: list.slice(0, 10)
@ -142,9 +140,10 @@ export class MenuDialog extends IGBDialog {
attachments.push(card); attachments.push(card);
}); });
if (attachments.length === 0) { if (attachments.length === 0) {
if (user.subjects && user.subjects.length > 0) { if (user.subjects && user.subjects.length > 0) {
await min.conversationalService.sendText(min, step, await min.conversationalService.sendText(
min,
step,
Messages[locale].lets_search(KBService.getFormattedSubjectItems(user.subjects)) Messages[locale].lets_search(KBService.getFormattedSubjectItems(user.subjects))
); );
} }

View file

@ -38,10 +38,10 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { AskDialog } from './dialogs/AskDialog'; import { AskDialog } from './dialogs/AskDialog.js';
import { FaqDialog } from './dialogs/FaqDialog'; import { FaqDialog } from './dialogs/FaqDialog.js';
import { MenuDialog } from './dialogs/MenuDialog'; import { MenuDialog } from './dialogs/MenuDialog.js';
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index'; import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index.js';
/** /**
* Package for kb.gbapp. * Package for kb.gbapp.

View file

@ -54,11 +54,8 @@ import {
UpdatedAt UpdatedAt
} from 'sequelize-typescript'; } from 'sequelize-typescript';
import { import { GuaribasInstance, GuaribasPackage } from '../../core.gbapp/models/GBModel.js';
GuaribasInstance, import { GuaribasUser } from '../../security.gbapp/models/index.js';
GuaribasPackage
} from '../../core.gbapp/models/GBModel';
import { GuaribasUser } from '../../security.gbapp/models';
/** /**
* Subjects to group the pair of questions and answers. * Subjects to group the pair of questions and answers.
@ -68,53 +65,53 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public subjectId: number; subjectId: number;
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public internalId: string; internalId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public title: string; title: string;
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public description: string; description: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public from: string; from: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public to: string; to: string;
@ForeignKey(() => GuaribasSubject) @ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public parentSubjectId: number; parentSubjectId: number;
@BelongsTo(() => GuaribasSubject, 'parentSubjectId') @BelongsTo(() => GuaribasSubject, 'parentSubjectId')
public parentSubject: GuaribasSubject; parentSubject: GuaribasSubject;
@HasMany(() => GuaribasSubject, { foreignKey: 'parentSubjectId' }) @HasMany(() => GuaribasSubject, { foreignKey: 'parentSubjectId' })
public childrenSubjects: GuaribasSubject[]; childrenSubjects: GuaribasSubject[];
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
@ForeignKey(() => GuaribasUser) @ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public responsibleUserId: number; responsibleUserId: number;
@BelongsTo(() => GuaribasUser) @BelongsTo(() => GuaribasUser)
public responsibleUser: GuaribasUser; responsibleUser: GuaribasUser;
@ForeignKey(() => GuaribasPackage) @ForeignKey(() => GuaribasPackage)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public packageId: number; packageId: number;
@BelongsTo(() => GuaribasPackage) @BelongsTo(() => GuaribasPackage)
public package: GuaribasPackage; package: GuaribasPackage;
} }
/** /**
@ -125,62 +122,61 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public questionId: number; questionId: number;
@Column(DataType.STRING(64)) @Column(DataType.STRING(64))
public subject1: string; subject1: string;
@Column(DataType.STRING(64)) @Column(DataType.STRING(64))
public subject2: string; subject2: string;
@Column(DataType.STRING(64)) @Column(DataType.STRING(64))
public subject3: string; subject3: string;
@Column(DataType.STRING(64)) @Column(DataType.STRING(64))
public subject4: string; subject4: string;
@Column(DataType.STRING(1024)) @Column(DataType.STRING(1024))
public keywords: string; keywords: string;
@Column(DataType.BOOLEAN) @Column(DataType.BOOLEAN)
public skipIndex: boolean; skipIndex: boolean;
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public from: string; from: string;
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public to: string; to: string;
@Column(DataType.TEXT) @Column(DataType.TEXT)
public content: string; content: string;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; updatedAt: Date;
//tslint:disable-next-line:no-use-before-declare //tslint:disable-next-line:no-use-before-declare
@ForeignKey(() => GuaribasAnswer) @ForeignKey(() => GuaribasAnswer)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public answerId: number; answerId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@ForeignKey(() => GuaribasPackage) @ForeignKey(() => GuaribasPackage)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public packageId: number; packageId: number;
@BelongsTo(() => GuaribasPackage) @BelongsTo(() => GuaribasPackage)
public package: GuaribasPackage; package: GuaribasPackage;
} }
/** /**
@ -191,53 +187,52 @@ export class GuaribasAnswer extends Model<GuaribasAnswer> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public answerId: number; declare answerId: number;
@Length({ min: 0, max: 512 }) @Length({ min: 0, max: 512 })
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public media: string; declare media: string;
@Length({ min: 0, max: 12 }) @Length({ min: 0, max: 12 })
@Column(DataType.STRING(12)) @Column(DataType.STRING(12))
public format: string; declare format: string;
@Column(DataType.TEXT) @Column(DataType.TEXT)
public content: string; declare content: string;
@Column(DataType.DATE) @Column(DataType.DATE)
@CreatedAt @CreatedAt
public createdAt: Date; declare createdAt: Date;
@Column(DataType.DATE) @Column(DataType.DATE)
@UpdatedAt @UpdatedAt
public updatedAt: Date; declare updatedAt: Date;
@HasMany(() => GuaribasQuestion) @HasMany(() => GuaribasQuestion)
public questions: GuaribasQuestion[]; declare questions: GuaribasQuestion[];
@HasOne(() => GuaribasQuestion) @HasOne(() => GuaribasQuestion)
public prev: GuaribasQuestion; declare prev: GuaribasQuestion;
@HasOne(() => GuaribasQuestion) @HasOne(() => GuaribasQuestion)
public next: GuaribasQuestion; declare next: GuaribasQuestion;
@ForeignKey(() => GuaribasQuestion) @ForeignKey(() => GuaribasQuestion)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public nextId: number; declare nextId: number;
@ForeignKey(() => GuaribasQuestion) @ForeignKey(() => GuaribasQuestion)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public prevId: number; declare prevId: number;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; declare instanceId: number;
@ForeignKey(() => GuaribasPackage) @ForeignKey(() => GuaribasPackage)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public packageId: number; declare packageId: number;
@BelongsTo(() => GuaribasPackage) @BelongsTo(() => GuaribasPackage)
public package: GuaribasPackage; declare package: GuaribasPackage;
} }

View file

@ -34,17 +34,16 @@
* @fileoverview Knowledge base services and logic. * @fileoverview Knowledge base services and logic.
*/ */
const Path = require('path'); import Path from 'path';
const Fs = require('fs'); import Fs from 'fs';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
const path = require('path'); import path from 'path';
const asyncPromise = require('async-promises'); import asyncPromise from 'async-promises';
const walkPromise = require('walk-promise'); import walkPromise from 'walk-promise';
// tslint:disable-next-line:newline-per-chained-call import { SearchClient } from '@azure/search-documents';
const { SearchService } = require('azure-search-client'); import Excel from 'exceljs';
const Excel = require('exceljs'); import getSlug from 'speakingurl';
const getSlug = require('speakingurl'); import { GBServer } from '../../../src/app.js';
import { GBServer } from '../../../src/app';
import { import {
GBDialogStep, GBDialogStep,
GBLog, GBLog,
@ -57,15 +56,15 @@ import {
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService'; import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { GuaribasPackage } from '../../core.gbapp/models/GBModel'; import { GuaribasPackage } from '../../core.gbapp/models/GBModel.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { CSService } from '../../customer-satisfaction.gbapp/services/CSService'; import { CSService } from '../../customer-satisfaction.gbapp/services/CSService.js';
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models'; import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models/index.js';
import { GBConfigService } from './../../core.gbapp/services/GBConfigService'; import { GBConfigService } from './../../core.gbapp/services/GBConfigService.js';
const request = require('request-promise-native'); import request from 'request-promise-native';
const textract = require('textract'); import textract from 'textract';
const pdf = require("pdf-extraction"); import pdf from 'pdf-extraction';
/** /**
* Result for quey on KB data. * Result for quey on KB data.
@ -142,7 +141,6 @@ export class KBService implements IGBKBService {
* Returns a question object given a SEO friendly URL. * Returns a question object given a SEO friendly URL.
*/ */
public async getQuestionIdFromURL (core: IGBCoreService, url: string) { public async getQuestionIdFromURL (core: IGBCoreService, url: string) {
// Extracts questionId from URL. // Extracts questionId from URL.
const id = url.substr(url.lastIndexOf('-') + 1); const id = url.substr(url.lastIndexOf('-') + 1);
@ -177,11 +175,9 @@ export class KBService implements IGBKBService {
}); });
return questions; return questions;
} }
public async getQuestionsSEO (instanceId: number) { public async getQuestionsSEO (instanceId: number) {
const questions = await GuaribasQuestion.findAll({ const questions = await GuaribasQuestion.findAll({
where: { where: {
instanceId: instanceId instanceId: instanceId
@ -200,14 +196,12 @@ export class KBService implements IGBKBService {
} }
public async getDocs (instanceId: number) { public async getDocs (instanceId: number) {
return await GuaribasAnswer.findAll({ return await GuaribasAnswer.findAll({
where: { where: {
instanceId: instanceId, instanceId: instanceId,
format: '.docx' format: '.docx'
} }
}); });
} }
public async getAnswerByText (instanceId: number, text: string, from: string = null): Promise<any> { public async getAnswerByText (instanceId: number, text: string, from: string = null): Promise<any> {
@ -233,7 +227,7 @@ export class KBService implements IGBKBService {
let where = { let where = {
instanceId: instanceId, instanceId: instanceId,
content: { [Op.eq]: `${text}` } content: { [Op.eq]: `${text}` }
} };
question = await GuaribasQuestion.findOne({ question = await GuaribasQuestion.findOne({
where: where where: where
}); });
@ -253,9 +247,6 @@ export class KBService implements IGBKBService {
return undefined; return undefined;
} }
public async addAnswer (obj: GuaribasAnswer): Promise<GuaribasAnswer> { public async addAnswer (obj: GuaribasAnswer): Promise<GuaribasAnswer> {
return await GuaribasAnswer.create(obj); return await GuaribasAnswer.create(obj);
} }
@ -266,8 +257,6 @@ export class KBService implements IGBKBService {
searchScore: number, searchScore: number,
subjects: GuaribasSubject[] subjects: GuaribasSubject[]
): Promise<KBServiceSearchResults> { ): Promise<KBServiceSearchResults> {
// Builds search query. // Builds search query.
query = query.toLowerCase(); query = query.toLowerCase();
@ -293,43 +282,51 @@ export class KBService implements IGBKBService {
} }
} }
// No direct match found, so Search is used. // No direct match found, so Search is used.
if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') { if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
const client = new SearchService(instance.searchHost.split('.')[0], instance.searchKey); interface SearchResults {
instanceId: number;
questionId: number;
answerId: number;
content: string;
subject1: string;
subject2: string;
subject: string;
subject4: string;
}
const results = await client.indexes.use('azuresql-index').search({ const client = new SearchClient<SearchResults>(instance.searchHost.split('.')[0], 'azuresql-index', {
count: true, key: instance.searchKey
} as any);
const results = await client.search(query, {
filter: `instanceId eq ${instance.instanceId} and skipIndex eq false`, filter: `instanceId eq ${instance.instanceId} and skipIndex eq false`,
search: query, searchFields: ['content', 'subject1', 'subject2', 'subject', 'subject4'],
searchFields: 'content, subject1, subject2, subject3, subject4', select: ['instanceId', 'questionId', 'answerId'],
select: 'instanceId, questionId, answerId',
skip: 0, skip: 0,
top: 1, top: 1
}); });
const values = results.results; // TODO: See.
const values = results.result.value;
let returnedScore = 0; let returnedScore = 0;
// Searches via Search (Azure Search). // Searches via Search (Azure Search).
if (values && values.length > 0) { let found = false;
returnedScore = values[0]['@search.score']; for await (const result of values) {
found = true;
returnedScore = result['@search.score'];
if (returnedScore >= searchScore) { if (returnedScore >= searchScore) {
const value = await this.getAnswerById(instance.instanceId, values[0].answerId); const value = await this.getAnswerById(instance.instanceId, result.document.answerId);
if (value !== null) { if (value !== null) {
GBLog.info( GBLog.info(`SEARCH WILL BE USED with score: ${returnedScore} > required (searchScore): ${searchScore}`);
`SEARCH WILL BE USED with score: ${returnedScore} > required (searchScore): ${searchScore}`
);
return { answer: value, questionId: result.document.questionId };
return { answer: value, questionId: values[0].questionId };
} else { } else {
GBLog.info( GBLog.info(
`SEARCH WILL NOT be used as answerId ${values[0].answerId} was not found in database, `SEARCH WILL NOT be used as answerId ${result.document.answerId} was not found in database,
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}` returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
); );
@ -343,15 +340,15 @@ export class KBService implements IGBKBService {
return { answer: undefined, questionId: 0 }; return { answer: undefined, questionId: 0 };
} }
} else { }
GBLog.info(
`SEARCH called but NO answer could be found (zero results).` if (!found) {
); GBLog.info(`SEARCH called but NO answer could be found (zero results).`);
}
return { answer: undefined, questionId: 0 }; return { answer: undefined, questionId: 0 };
} }
} }
}
public async getSubjectItems (instanceId: number, parentId: number): Promise<GuaribasSubject[]> { public async getSubjectItems (instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
const where = { parentSubjectId: parentId, instanceId: instanceId }; const where = { parentSubjectId: parentId, instanceId: instanceId };
@ -425,8 +422,8 @@ export class KBService implements IGBKBService {
// when loading worksheets collection. // when loading worksheets collection.
let worksheet: any; let worksheet: any;
for (let t = 0; t < data._worksheets.length; t++) { for (let t = 0; t < data.worksheets.length; t++) {
worksheet = data._worksheets[t]; worksheet = data.worksheets[t];
if (worksheet) { if (worksheet) {
break; break;
} }
@ -456,8 +453,13 @@ export class KBService implements IGBKBService {
const question = line._cells[3].text.trim(); const question = line._cells[3].text.trim();
let answer = line._cells[4].text.trim(); let answer = line._cells[4].text.trim();
if (!(subjectsText === 'subjects' && from === 'from') && answer !== null && question !== null && if (
answer !== '' && question !== '') { !(subjectsText === 'subjects' && from === 'from') &&
answer !== null &&
question !== null &&
answer !== '' &&
question !== ''
) {
let format = '.txt'; let format = '.txt';
// Extracts answer from external media if any. // Extracts answer from external media if any.
@ -469,16 +471,13 @@ export class KBService implements IGBKBService {
answer = answer =
'Existe um problema na base de conhecimento. Fui treinado para entender sua pergunta, avise a quem me criou que a resposta não foi informada para esta pergunta.'; 'Existe um problema na base de conhecimento. Fui treinado para entender sua pergunta, avise a quem me criou que a resposta não foi informada para esta pergunta.';
} else if (answer.indexOf('.md') > -1 || answer.indexOf('.docx') > -1) { } else if (answer.indexOf('.md') > -1 || answer.indexOf('.docx') > -1) {
const mediaFilename = urlJoin(path.dirname(filePath), '..', 'articles', answer); const mediaFilename = urlJoin(path.dirname(filePath), '..', 'articles', answer);
if (Fs.existsSync(mediaFilename)) { if (Fs.existsSync(mediaFilename)) {
// Tries to load .docx file from Articles folder. // Tries to load .docx file from Articles folder.
if (answer.indexOf('.docx') > -1) { if (answer.indexOf('.docx') > -1) {
answer = await this.getTextFromFile(filePath); answer = await this.getTextFromFile(filePath);
} } else {
else {
// Loads normally markdown file. // Loads normally markdown file.
answer = Fs.readFileSync(mediaFilename, 'utf8'); answer = Fs.readFileSync(mediaFilename, 'utf8');
@ -543,7 +542,7 @@ export class KBService implements IGBKBService {
subject4: subject4, subject4: subject4,
content: question.replace(/["]+/g, ''), content: question.replace(/["]+/g, ''),
instanceId: instanceId, instanceId: instanceId,
skipIndex: (question.charAt(0) === "\""), skipIndex: question.charAt(0) === '"',
packageId: packageId packageId: packageId
}; };
questions.push(question1); questions.push(question1);
@ -583,16 +582,19 @@ export class KBService implements IGBKBService {
answer.content.endsWith('.docx') || answer.content.endsWith('.docx') ||
answer.content.endsWith('.xls') || answer.content.endsWith('.xls') ||
answer.content.endsWith('.xlsx') answer.content.endsWith('.xlsx')
) { ) {
const doc = urlJoin(GBServer.globals.publicAddress, 'kb', `${min.instance.botId}.gbai`, const doc = urlJoin(
`${min.instance.botId}.gbkb`, 'assets', answer.content) GBServer.globals.publicAddress,
'kb',
`${min.instance.botId}.gbai`,
`${min.instance.botId}.gbkb`,
'assets',
answer.content
);
const url = `http://view.officeapps.live.com/op/view.aspx?src=${doc}`; const url = `http://view.officeapps.live.com/op/view.aspx?src=${doc}`;
await this.playUrl(min, min.conversationalService, step, url, channel); await this.playUrl(min, min.conversationalService, step, url, channel);
} else if (answer.content.endsWith('.pdf')) { } else if (answer.content.endsWith('.pdf')) {
const url = urlJoin('kb', `${min.instance.botId}.gbai`, `${min.instance.botId}.gbkb`, 'assets', answer.content);
const url = urlJoin('kb', `${min.instance.botId}.gbai`,
`${min.instance.botId}.gbkb`, 'assets', answer.content);
await this.playUrl(min, min.conversationalService, step, url, channel); await this.playUrl(min, min.conversationalService, step, url, channel);
} else if (answer.format === '.md') { } else if (answer.format === '.md') {
await min.conversationalService['playMarkdown'](min, answer.content, channel, step, min.conversationalService); await min.conversationalService['playMarkdown'](min, answer.content, channel, step, min.conversationalService);
@ -661,12 +663,18 @@ export class KBService implements IGBKBService {
/** /**
* Import all .docx files in reading comprehension folder. * Import all .docx files in reading comprehension folder.
*/ */
public async importDocs(min: GBMinInstance, localPath: string, instance: IGBInstance, packageId: number): Promise<any> { public async importDocs (
min: GBMinInstance,
localPath: string,
instance: IGBInstance,
packageId: number
): Promise<any> {
const files = await walkPromise(urlJoin(localPath, 'docs')); const files = await walkPromise(urlJoin(localPath, 'docs'));
if (!files[0]) { if (!files[0]) {
GBLog.info(`[GBDeployer] docs folder not created yet in .gbkb. To use Reading Comprehension, create this folder at root and put a document to get read by the.`); GBLog.info(
} `[GBDeployer] docs folder not created yet in .gbkb. To use Reading Comprehension, create this folder at root and put a document to get read by the.`
else { );
} else {
await CollectionUtil.asyncForEach(files, async file => { await CollectionUtil.asyncForEach(files, async file => {
let content = null; let content = null;
let filePath = Path.join(file.root, file.name); let filePath = Path.join(file.root, file.name);
@ -689,7 +697,6 @@ export class KBService implements IGBKBService {
packageId: packageId packageId: packageId
}); });
} }
}); });
} }
} }
@ -761,12 +768,9 @@ export class KBService implements IGBKBService {
if (categoryReg && nameReg) { if (categoryReg && nameReg) {
let category = categoryReg[1]; let category = categoryReg[1];
let name = nameReg[1]; let name = nameReg[1];
min["nerEngine"].addNamedEntityText(category, name, min['nerEngine'].addNamedEntityText(category, name, [contentLocale], [name]);
[contentLocale], [name]);
} }
}); });
} }
/** /**
@ -866,5 +870,3 @@ export class KBService implements IGBKBService {
}); });
} }
} }

View file

@ -1,32 +1,27 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
did_not_find: 'I\'m sorry I didn\'t find anything.', did_not_find: "I'm sorry I didn't find anything.",
going_answer: 'Great choice, now looking for your answer...', going_answer: 'Great choice, now looking for your answer...',
wider_answer: subjectText => wider_answer: subjectText => `Answering to you in a broader way... Not just about ${subjectText}.`,
`Answering to you in a broader way... Not just about ${subjectText}.`, which_question: "What's your question?",
which_question: 'What\'s your question?',
anything_else: 'Can I help you with anything else?', anything_else: 'Can I help you with anything else?',
here_is_subjects: 'Here are some subjects to choose from...', here_is_subjects: 'Here are some subjects to choose from...',
menu_select: 'Select', menu_select: 'Select',
lets_search: query => lets_search: query => `Lets search for ${query}... What do you want to know?`,
`Lets search for ${query}... What do you want to know?`, see_faq: "Please take a look at the FAQ I've prepared for you. You can click on them to get the answer.",
see_faq: 'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
ask_first_time: 'What are you looking for?' ask_first_time: 'What are you looking for?'
}, },
'pt-BR': { 'pt-BR': {
did_not_find: 'Desculpe-me, não encontrei nada a respeito.', did_not_find: 'Desculpe-me, não encontrei nada a respeito.',
going_answer: 'Ótima escolha, procurando resposta para sua questão...', going_answer: 'Ótima escolha, procurando resposta para sua questão...',
wider_answer: subjectText => wider_answer: subjectText => `Vou te responder de modo mais abrangente... Não apenas sobre ${subjectText}`,
`Vou te responder de modo mais abrangente... Não apenas sobre ${subjectText}`,
which_question: 'Qual a pergunta?', which_question: 'Qual a pergunta?',
anything_else: 'Posso ajudar em algo mais?', anything_else: 'Posso ajudar em algo mais?',
here_is_subjects: 'Aqui estão algumas categorias de assuntos...', here_is_subjects: 'Aqui estão algumas categorias de assuntos...',
menu_select: 'Selecionar', menu_select: 'Selecionar',
lets_search: query => lets_search: query => `Let's search about ${query}... What do you want to know?`,
`Let's search about ${query}... What do you want to know?`, see_faq: 'Veja algumas perguntas mais frequentes logo na tela. Clique numa delas para eu responder.',
see_faq:
'Veja algumas perguntas mais frequentes logo na tela. Clique numa delas para eu responder.',
ask_first_time: 'Como eu posso ajudar?' ask_first_time: 'Como eu posso ajudar?'
} }

View file

@ -38,7 +38,7 @@
import { TokenResponse } from 'botbuilder'; import { TokenResponse } from 'botbuilder';
import { GBLog, GBMinInstance, IGBDialog } from 'botlib'; import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { Messages } from '../strings'; import { Messages } from '../strings.js';
/** /**
* Dialogs for handling Menu control. * Dialogs for handling Menu control.

View file

@ -36,31 +36,23 @@
'use strict'; 'use strict';
const urlJoin = require('url-join');
import { BotAdapter, CardFactory, MessageFactory } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBLog, GBMinInstance, IGBDialog } from 'botlib'; import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { Messages } from '../strings.js';
import { Messages } from '../strings'; import * as phone from 'google-libphonenumber';
const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
const phone = require('phone');
/** /**
* Dialogs for handling Menu control. * Dialogs for handling Menu control.
*/ */
export class ProfileDialog extends IGBDialog { export class ProfileDialog extends IGBDialog {
public static getNameDialog (min: GBMinInstance) { public static getNameDialog (min: GBMinInstance) {
return { return {
id: '/profile_name', waterfall: [ id: '/profile_name',
waterfall: [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -72,7 +64,7 @@ export class ProfileDialog extends IGBDialog {
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const fullName = (text) => { const fullName = text => {
return text.match(/^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$/gi); return text.match(/^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$/gi);
}; };
@ -85,21 +77,20 @@ export class ProfileDialog extends IGBDialog {
step.activeDialog.state.options.name = value[0]; step.activeDialog.state.options.name = value[0];
return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options); return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options);
} }
}] }
]
}; };
} }
public static getMobileDialog (min: GBMinInstance) { public static getMobileDialog (min: GBMinInstance) {
return { return {
id: '/profile_mobile', waterfall: [ id: '/profile_mobile',
waterfall: [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -112,14 +103,15 @@ export class ProfileDialog extends IGBDialog {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
let phoneNumber; let phoneNumber;
try { try {
phoneNumber = phone(step.result, 'BRA')[0]; // TODO: Use accordingly to the person. let p = phone.PhoneNumberUtil.getInstance();
phoneNumber = phoneUtil.parse(phoneNumber); phoneNumber = p(step.result, 'BRA')[0]; // TODO: Use accordingly to the person.
phoneNumber = phone.phoneUtil.parse(phoneNumber);
} catch (error) { } catch (error) {
await step.context.sendActivity(Messages[locale].validation_enter_valid_mobile); await step.context.sendActivity(Messages[locale].validation_enter_valid_mobile);
return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options); return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options);
} }
if (!phoneUtil.isPossibleNumber(phoneNumber)) { if (!phone.phoneUtil.isPossibleNumber(phoneNumber)) {
await step.context.sendActivity(Messages[locale].validation_enter_valid_mobile); await step.context.sendActivity(Messages[locale].validation_enter_valid_mobile);
return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options); return await step.replaceDialog('/profile_mobile', step.activeDialog.state.options);
@ -129,19 +121,19 @@ export class ProfileDialog extends IGBDialog {
return await step.replaceDialog('/profile_mobile_confirm', step.activeDialog.state.options); return await step.replaceDialog('/profile_mobile_confirm', step.activeDialog.state.options);
} }
}] }
]
}; };
} }
public static getMobileConfirmDialog (min: GBMinInstance) { public static getMobileConfirmDialog (min: GBMinInstance) {
return { return {
id: '/profile_mobile_confirm', waterfall: [ id: '/profile_mobile_confirm',
waterfall: [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -150,8 +142,10 @@ export class ProfileDialog extends IGBDialog {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const from = step.activeDialog.state.options.mobile; const from = step.activeDialog.state.options.mobile;
if (min.whatsAppDirectLine) { if (min.whatsAppDirectLine) {
await min.whatsAppDirectLine.sendToDevice(
await min.whatsAppDirectLine.sendToDevice(from, `${step.activeDialog.state.options.mobileCode} is your General Bots creation code.`); from,
`${step.activeDialog.state.options.mobileCode} is your General Bots creation code.`
);
} else { } else {
GBLog.info(`WhatsApp not configured. Here is the code: ${step.activeDialog.state.options.mobileCode}.`); GBLog.info(`WhatsApp not configured. Here is the code: ${step.activeDialog.state.options.mobileCode}.`);
} }
@ -168,18 +162,19 @@ export class ProfileDialog extends IGBDialog {
} else { } else {
await step.replaceDialog('/profile_email', step.activeDialog.state.options); await step.replaceDialog('/profile_email', step.activeDialog.state.options);
} }
}] }
]
}; };
} }
public static getEmailDialog (min: GBMinInstance) { public static getEmailDialog (min: GBMinInstance) {
return { return {
id: '/profile_email', waterfall: [ id: '/profile_email',
waterfall: [
async step => { async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth'); return await step.beginDialog('/auth');
} } else {
else{
return await step.next(step.options); return await step.next(step.options);
} }
}, },
@ -190,7 +185,7 @@ export class ProfileDialog extends IGBDialog {
async step => { async step => {
const locale = step.context.activity.locale; const locale = step.context.activity.locale;
const extractEntity = (text) => { const extractEntity = text => {
return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi); return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi);
}; };
@ -203,7 +198,8 @@ export class ProfileDialog extends IGBDialog {
step.activeDialog.state.options.email = value[0]; step.activeDialog.state.options.email = value[0];
await step.replaceDialog(`/${step.activeDialog.state.options.nextDialog}`, step.activeDialog.state.options); await step.replaceDialog(`/${step.activeDialog.state.options.nextDialog}`, step.activeDialog.state.options);
} }
}] }
]
}; };
} }
} }

View file

@ -36,13 +36,11 @@
'use strict'; 'use strict';
const urlJoin = require('url-join');
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { OAuthDialog } from './dialogs/OAuthDialog'; import { OAuthDialog } from './dialogs/OAuthDialog.js';
import { ProfileDialog } from './dialogs/ProfileDialog'; import { ProfileDialog } from './dialogs/ProfileDialog.js';
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models'; import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models/index.js';
/** /**
* Package for the security module. * Package for the security module.
@ -50,7 +48,6 @@ import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
export class GBSecurityPackage implements IGBPackage { export class GBSecurityPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
const out = [ const out = [
ProfileDialog.getNameDialog(min), ProfileDialog.getNameDialog(min),
ProfileDialog.getEmailDialog(min), ProfileDialog.getEmailDialog(min),
@ -62,7 +59,6 @@ export class GBSecurityPackage implements IGBPackage {
out.push(OAuthDialog.getOAuthDialog(min)); out.push(OAuthDialog.getOAuthDialog(min));
} }
return out; return out;
} }
public async unloadPackage (core: IGBCoreService): Promise<void> { public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`); GBLog.verbose(`unloadPackage called.`);

View file

@ -48,7 +48,7 @@ import {
Table Table
} from 'sequelize-typescript'; } from 'sequelize-typescript';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel'; import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
/** /**
* A user and its metadata. * A user and its metadata.
@ -58,46 +58,46 @@ export class GuaribasUser extends Model<GuaribasUser> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public userId: number; userId: number;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public displayName: string; displayName: string;
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public userSystemId: string; userSystemId: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public userName: string; userName: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public defaultChannel: string; defaultChannel: string;
@Column(DataType.STRING(255)) @Column(DataType.STRING(255))
public email: string; email: string;
@Column(DataType.STRING(5)) @Column(DataType.STRING(5))
public locale: string; locale: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public agentSystemId: string; agentSystemId: string;
@Column(DataType.DATE) @Column(DataType.DATE)
public agentContacted: Date; agentContacted: Date;
@Column(DataType.STRING(16)) @Column(DataType.STRING(16))
public agentMode: string; agentMode: string;
@Column(DataType.TEXT) @Column(DataType.TEXT)
public conversationReference: string; conversationReference: string;
@Column(DataType.STRING(64)) @Column(DataType.STRING(64))
public hearOnDialog: string; hearOnDialog: string;
} }
/** /**
@ -108,18 +108,18 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
@PrimaryKey @PrimaryKey
@AutoIncrement @AutoIncrement
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public groupId: number; groupId: number;
@Length({ min: 0, max: 512 }) @Length({ min: 0, max: 512 })
@Column(DataType.STRING(512)) @Column(DataType.STRING(512))
public displayName: string; displayName: string;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
} }
/** /**
@ -129,22 +129,22 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
export class GuaribasUserGroup extends Model<GuaribasUserGroup> { export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
@ForeignKey(() => GuaribasUser) @ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public userId: number; userId: number;
@ForeignKey(() => GuaribasGroup) @ForeignKey(() => GuaribasGroup)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public groupId: number; groupId: number;
@ForeignKey(() => GuaribasInstance) @ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER) @Column(DataType.INTEGER)
public instanceId: number; instanceId: number;
@BelongsTo(() => GuaribasInstance) @BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance; instance: GuaribasInstance;
@BelongsTo(() => GuaribasGroup) @BelongsTo(() => GuaribasGroup)
public group: GuaribasGroup; group: GuaribasGroup;
@BelongsTo(() => GuaribasUser) @BelongsTo(() => GuaribasUser)
public user: GuaribasUser; user: GuaribasUser;
} }

View file

@ -1,17 +1,16 @@
const Fs = require('fs'); import Fs from 'fs';
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import { ConversationReference } from 'botbuilder'; import { ConversationReference } from 'botbuilder';
import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib'; import { GBLog, GBMinInstance, GBService, IGBInstance } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models'; import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models/index.js';
import { FindOptions } from 'sequelize'; import { FindOptions } from 'sequelize';
/** /**
* Security service layer. * Security service layer.
*/ */
export class SecService extends GBService { export class SecService extends GBService {
public async ensureUser ( public async ensureUser (
instanceId: number, instanceId: number,
userSystemId: string, userSystemId: string,
@ -167,24 +166,24 @@ export class SecService extends GBService {
return user.agentMode === 'self'; return user.agentMode === 'self';
} }
public async assignHumanAgent(min: GBMinInstance, userSystemId: string, agentSystemId: string = null): Promise<string> { public async assignHumanAgent (
min: GBMinInstance,
userSystemId: string,
agentSystemId: string = null
): Promise<string> {
if (!agentSystemId) { if (!agentSystemId) {
let list = min.core.getParam<string>( let list = min.core.getParam<string>(min.instance, 'Transfer To', process.env.TRANSFER_TO);
min.instance,
'Transfer To',
process.env.TRANSFER_TO
);
if (list) { if (list) {
list = list.split(';') list = list.split(';');
} }
await CollectionUtil.asyncForEach(list, async item => { await CollectionUtil.asyncForEach(list, async item => {
if ( if (
item !== undefined && item !== undefined &&
agentSystemId === undefined && agentSystemId === undefined &&
item !== userSystemId && !await this.isAgentSystemId(item) item !== userSystemId &&
!(await this.isAgentSystemId(item))
) { ) {
// TODO: Optimize loop. // TODO: Optimize loop.
agentSystemId = item; agentSystemId = item;

View file

@ -1,9 +1,9 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
whats_name: 'What\'s your name?', whats_name: "What's your name?",
whats_mobile: 'What\'s your mobile number including country code (e.g. +1 222 9998888)?', whats_mobile: "What's your mobile number including country code (e.g. +1 222 9998888)?",
confirm_mobile: 'Please type the code just sent to your mobile.', confirm_mobile: 'Please type the code just sent to your mobile.',
whats_email: 'What\'s your E-mail address?', whats_email: "What's your E-mail address?",
validation_enter_name: 'Please enter your full name.', validation_enter_name: 'Please enter your full name.',
validation_enter_valid_mobile: 'Please enter a valid mobile number.', validation_enter_valid_mobile: 'Please enter a valid mobile number.',
validation_enter_valid_email: 'Please enter a valid e-mail.' validation_enter_valid_email: 'Please enter a valid e-mail.'
@ -13,7 +13,8 @@ export const Messages = {
whats_email: 'Qual o seu e-mail?', whats_email: 'Qual o seu e-mail?',
whats_mobile: 'Qual o seu celular?', whats_mobile: 'Qual o seu celular?',
confirm_mobile: 'Por favor, digite o código enviado para seu celular.', confirm_mobile: 'Por favor, digite o código enviado para seu celular.',
confirm_mobile_again: 'Esse não me parece ser um código numérico válido. Por favor, digite novamente o código enviado para seu celular.', confirm_mobile_again:
'Esse não me parece ser um código numérico válido. Por favor, digite novamente o código enviado para seu celular.',
validation_enter_valid_email: 'Por favor, digite um e-mail válido no formato nome@domínio.com.br.', validation_enter_valid_email: 'Por favor, digite um e-mail válido no formato nome@domínio.com.br.',
validation_enter_name: 'Por favor, digite seu nome completo', validation_enter_name: 'Por favor, digite seu nome completo',
validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).' validation_enter_valid_mobile: 'Por favor, insira um número de celular válido (ex.: +55 21 98888-7766).'

View file

@ -65,5 +65,4 @@ export class GBSharePointPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -36,29 +36,7 @@
'use strict'; 'use strict';
const { sppull } = require('sppull');
/** /**
* Service facade for SharePoint Online. * Service facade for SharePoint Online.
*/ */
export class GBSharePointService { export class GBSharePointService {}
public async downloadFolder(localPath: string, siteUrl: string, folderUrl: string, username: string, password: string) {
const context = {
siteUrl: siteUrl,
creds: {
username: username,
password: password
}
};
const options = {
spRootFolder: folderUrl,
dlRootFolder: localPath
};
return await sppull(context, options);
}
}

View file

@ -1,8 +1,4 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {},
'pt-BR': {}
},
'pt-BR': {
}
}; };

View file

@ -45,9 +45,7 @@ import { Sequelize } from 'sequelize-typescript';
export class GBTeamsPackage implements IGBPackage { export class GBTeamsPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public async loadBot(min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {}
}
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`); GBLog.verbose(`getDialogs called.`);
@ -67,5 +65,4 @@ export class GBTeamsPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -30,28 +30,24 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
const urlJoin = require('url-join');
import { GBService } from 'botlib'; import { GBService } from 'botlib';
const fs = require('fs'); import Fs from 'fs';
var AdmZip = require("adm-zip"); import AdmZip from 'adm-zip';
/** /**
* Support for Whatsapp. * Support for Whatsapp.
*/ */
export class TeamsService extends GBService { export class TeamsService extends GBService {
public async getAppFile (manifest) {
public async getAppFile(manifest)
{
var zip = new AdmZip(); var zip = new AdmZip();
zip.addFile("manifest.json", Buffer.from(manifest, "utf8"), "Built with General Bots™."); zip.addFile('manifest.json', Buffer.from(manifest, 'utf8'), 'Built with General Bots™.');
zip.addLocalFile("teams-color.png", null, "color.png"); zip.addLocalFile('teams-color.png', null, 'color.png');
zip.addLocalFile("teams-outline.png", null, "outline.png"); zip.addLocalFile('teams-outline.png', null, 'outline.png');
return zip.toBuffer(); return zip.toBuffer();
} }
public async getManifest (marketplaceId, botName, botDescription, id, packageName, yourName) { public async getManifest (marketplaceId, botName, botDescription, id, packageName, yourName) {
let content = fs.readFileSync('teams-manifest.json', 'utf8'); let content = Fs.readFileSync('teams-manifest.json', 'utf8');
content = content.replace(/\@\@marketplaceId/gi, marketplaceId); content = content.replace(/\@\@marketplaceId/gi, marketplaceId);
content = content.replace(/\@\@botName/gi, botName); content = content.replace(/\@\@botName/gi, botName);

View file

@ -1,8 +1,4 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {},
'pt-BR': {}
},
'pt-BR': {
}
}; };

View file

@ -38,7 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib'; import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript'; import { Sequelize } from 'sequelize-typescript';
import { WhatsappDirectLine } from './services/WhatsappDirectLine'; import { WhatsappDirectLine } from './services/WhatsappDirectLine.js';
/** /**
* Package for whatsapp.gblib * Package for whatsapp.gblib
@ -46,9 +46,7 @@ import { WhatsappDirectLine } from './services/WhatsappDirectLine';
export class GBWhatsappPackage implements IGBPackage { export class GBWhatsappPackage implements IGBPackage {
public sysPackages: IGBPackage[]; public sysPackages: IGBPackage[];
public async loadBot(min: GBMinInstance): Promise<void> { public async loadBot (min: GBMinInstance): Promise<void> {}
}
public async getDialogs (min: GBMinInstance) { public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`); GBLog.verbose(`getDialogs called.`);
@ -68,5 +66,4 @@ export class GBWhatsappPackage implements IGBPackage {
public async onExchangeData (min: GBMinInstance, kind: string, data: any) { public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`); GBLog.verbose(`onExchangeData called.`);
} }
} }

View file

@ -30,35 +30,29 @@
| | | |
\*****************************************************************************/ \*****************************************************************************/
const urlJoin = require('url-join'); import urlJoin from 'url-join';
import Swagger from 'swagger-client';
const Swagger = require('swagger-client'); import Path from 'path';
const fs = require('fs'); import Fs from 'fs';
const Path = require('path');
import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib'; import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import * as request from 'request-promise-native'; import { GBServer } from '../../../src/app.js';
import { GBServer } from '../../../src/app'; import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService'; import { SecService } from '../../security.gbapp/services/SecService.js';
import { SecService } from '../../security.gbapp/services/SecService'; import { Messages } from '../strings.js';
import { Messages } from '../strings'; import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { GuaribasUser } from '../../security.gbapp/models'; import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords'; import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService'; import * as wpp from 'whatsapp-web.js';
import { GBMinService } from '../../core.gbapp/services/GBMinService'; import qrcode from 'qrcode-terminal';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService'; import express from 'express';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
const puppeteer = require('puppeteer'); import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
const { MessageMedia, Client, LocalAuth } = require('whatsapp-web.js');
const qrcode = require('qrcode-terminal');
/** /**
* Support for Whatsapp. * Support for Whatsapp.
*/ */
export class WhatsappDirectLine extends GBService { export class WhatsappDirectLine extends GBService {
public static conversationIds = {}; public static conversationIds = {};
public static mobiles = {}; public static mobiles = {};
public static phones = {}; public static phones = {};
@ -102,8 +96,12 @@ export class WhatsappDirectLine extends GBService {
this.whatsappServiceKey = whatsappServiceKey; this.whatsappServiceKey = whatsappServiceKey;
this.whatsappServiceNumber = whatsappServiceNumber; this.whatsappServiceNumber = whatsappServiceNumber;
this.whatsappServiceUrl = whatsappServiceUrl; this.whatsappServiceUrl = whatsappServiceUrl;
this.provider = whatsappServiceKey === "internal" ? this.provider =
'GeneralBots' : whatsappServiceNumber.indexOf(';') > -1 ? 'maytapi' : 'chatapi'; whatsappServiceKey === 'internal'
? 'GeneralBots'
: whatsappServiceNumber.indexOf(';') > -1
? 'maytapi'
: 'chatapi';
this.groupId = groupId; this.groupId = groupId;
} }
@ -114,12 +112,13 @@ export class WhatsappDirectLine extends GBService {
} }
public async setup (setUrl) { public async setup (setUrl) {
this.directLineClient = new Swagger({
this.directLineClient = spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
new Swagger({ usePromise: true
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), usePromise: true
}); });
const client = await this.directLineClient; const client = await this.directLineClient;
let url;
let body;
client.clientAuthorizations.add( client.clientAuthorizations.add(
'AuthorizationBotConnector', 'AuthorizationBotConnector',
@ -129,25 +128,21 @@ export class WhatsappDirectLine extends GBService {
switch (this.provider) { switch (this.provider) {
case 'GeneralBots': case 'GeneralBots':
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
// TODO: REMOVE THIS. // TODO: REMOVE THIS.
if (minBoot.botId !== this.botId) { if (minBoot.botId !== this.botId) {
this.customClient = minBoot.whatsAppDirectLine.customClient; this.customClient = minBoot.whatsAppDirectLine.customClient;
} else {
}
else {
// Initialize the browser using a local profile for each bot. // Initialize the browser using a local profile for each bot.
const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${this.min.botId}.gbai`;
const localName = Path.join('work', gbaiName, 'profile'); const localName = Path.join('work', gbaiName, 'profile');
const createClient = async (browserWSEndpoint) => { const createClient = async browserWSEndpoint => {
let puppeteer: any = { let puppeteer: any = {
headless: false, args: [ headless: false,
args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',
'--disable-dev-shm-usage', '--disable-dev-shm-usage',
@ -158,27 +153,31 @@ export class WhatsappDirectLine extends GBService {
'--disable-gpu', '--disable-gpu',
'--disable-infobars', '--disable-infobars',
'--disable-features=site-per-process', '--disable-features=site-per-process',
`--user-data-dir=${localName}`] `--user-data-dir=${localName}`
]
}; };
if (browserWSEndpoint) { if (browserWSEndpoint) {
puppeteer = { browserWSEndpoint: browserWSEndpoint }; puppeteer = { browserWSEndpoint: browserWSEndpoint };
} }
const client = this.customClient = new Client({ const client = (this.customClient = new wpp.Client({
authStrategy: new LocalAuth({ authStrategy: new wpp.LocalAuth({
clientId: this.min.botId, clientId: this.min.botId,
dataPath: localName dataPath: localName
}), }),
puppeteer: puppeteer puppeteer: puppeteer
}); }));
client.on(
client.on('message', (async message => { 'message',
(async message => {
await this.WhatsAppCallback(message, null); await this.WhatsAppCallback(message, null);
}).bind(this)); }).bind(this)
);
client.on('qr', (async (qr) => {
client.on(
'qr',
(async qr => {
const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null); const adminNumber = this.min.core.getParam(this.min.instance, 'Bot Admin Number', null);
const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null); const adminEmail = this.min.core.getParam(this.min.instance, 'Bot Admin E-mail', null);
@ -190,21 +189,22 @@ export class WhatsappDirectLine extends GBService {
// While handling other bots uses boot instance of this class to send QR Codes. // While handling other bots uses boot instance of this class to send QR Codes.
// const s = new DialogKeywords(min., null, null, null); const s = new DialogKeywords(this.min, null, null);
// const qrBuf = await s.getQRCode(qr); const qrBuf = await s.getQRCode(qr);
// const gbaiName = `${this.min.botId}.gbai`; const gbaiName = `${this.min.botId}.gbai`;
// const localName = Path.join('work', gbaiName, 'cache', `qr${GBAdminService.getRndReadableIdentifier()}.png`); const localName = Path.join('work', gbaiName, 'cache', `qr${GBAdminService.getRndReadableIdentifier()}.png`);
// fs.writeFileSync(localName, qrBuf); Fs.writeFileSync(localName, qrBuf);
// const url = urlJoin( const url = urlJoin(
// GBServer.globals.publicAddress, GBServer.globals.publicAddress,
// this.min.botId, this.min.botId,
// 'cache', 'cache',
// Path.basename(localName) Path.basename(localName)
// ); );
// GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice(adminNumber, url, Path.basename(localName), msg); GBServer.globals.minBoot.whatsAppDirectLine.sendFileToDevice(adminNumber, url, Path.basename(localName), msg);
// s.sendEmail(adminEmail, `Check your WhatsApp for bot ${this.botId}`, msg);
}).bind(this)); s.sendEmail({to:adminEmail, subject:`Check your WhatsApp for bot ${this.botId}`, body:msg);
}).bind(this)
);
client.on('authenticated', async () => { client.on('authenticated', async () => {
this.browserWSEndpoint = client.pupBrowser.wsEndpoint(); this.browserWSEndpoint = client.pupBrowser.wsEndpoint();
@ -212,11 +212,13 @@ export class WhatsappDirectLine extends GBService {
}); });
client.on('ready', async () => { client.on('ready', async () => {
client.pupBrowser.on(
client.pupBrowser.on('disconnected', (async () => { 'disconnected',
(async () => {
GBLog.info(`Browser terminated. Restarting ${this.min.botId} WhatsApp native provider.`); GBLog.info(`Browser terminated. Restarting ${this.min.botId} WhatsApp native provider.`);
await (createClient.bind(this))(null); await createClient.bind(this)(null);
}).bind(this)); }).bind(this)
);
GBLog.verbose(`GBWhatsApp: Emptying chat list for ${this.botId}...`); GBLog.verbose(`GBWhatsApp: Emptying chat list for ${this.botId}...`);
@ -224,7 +226,6 @@ export class WhatsappDirectLine extends GBService {
const chats = await client.getChats(); const chats = await client.getChats();
await CollectionUtil.asyncForEach(chats, async chat => { await CollectionUtil.asyncForEach(chats, async chat => {
const sleep = ms => { const sleep = ms => {
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
@ -234,27 +235,25 @@ export class WhatsappDirectLine extends GBService {
await sleep(wait); await sleep(wait);
if (chat.isGroup) { if (chat.isGroup) {
await chat.clearMessages(); await chat.clearMessages();
} } else if (!chat.pinned) {
else if (!chat.pinned) {
await chat.delete(); await chat.delete();
} }
}); });
}); });
client.initialize(); client.initialize();
}; };
await (createClient.bind(this))(this.browserWSEndpoint); await createClient.bind(this)(this.browserWSEndpoint);
setUrl = false; setUrl = false;
} }
break; break;
case 'chatapi': case 'chatapi':
url = urlJoin(this.whatsappServiceUrl, 'webhook')
options = { options = {
method: 'POST', method: 'POST',
url: urlJoin(this.whatsappServiceUrl, 'webhook'), url: url,
timeout: 10000, timeout: 10000,
qs: { qs: {
token: this.whatsappServiceKey, token: this.whatsappServiceKey,
@ -268,35 +267,36 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'maytapi': case 'maytapi':
let phoneId = this.whatsappServiceNumber.split(';')[0]; let phoneId = this.whatsappServiceNumber.split(';')[0];
let productId = this.whatsappServiceNumber.split(';')[1] let productId = this.whatsappServiceNumber.split(';')[1];
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/config`; url = `${this.INSTANCE_URL}/${productId}/${phoneId}/config`;
body= {
webhook: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
ack_delivery: false
};
WhatsappDirectLine.phones[phoneId] = this.botId; WhatsappDirectLine.phones[phoneId] = this.botId;
options = { options = {
url: url, url: url,
method: 'POST', method: 'POST',
body: { body:body,
webhook: `${GBServer.globals.publicAddress}/webhooks/whatsapp/${this.botId}`,
"ack_delivery": false
},
headers: { headers: {
'x-maytapi-key': this.whatsappServiceKey, 'x-maytapi-key': this.whatsappServiceKey,
'Content-Type': 'application/json', 'Content-Type': 'application/json'
}, },
json: true, json: true
}; };
break; break;
} }
if (setUrl && options) { if (setUrl && options && this.whatsappServiceUrl) {
const express = require('express');
GBServer.globals.server.use(`/audios`, express.static('work')); GBServer.globals.server.use(`/audios`, express.static('work'));
if (options) { if (options) {
try { try {
await request.post(options);
const response: Response = await fetch(url,options);
} catch (error) { } catch (error) {
GBLog.error(`Error initializing 3rd party Whatsapp provider(1) ${error.message}`); GBLog.error(`Error initializing 3rd party Whatsapp provider(1) ${error.message}`);
} }
@ -327,16 +327,14 @@ export class WhatsappDirectLine extends GBService {
} }
public static providerFromRequest (req) { public static providerFromRequest (req) {
return req.body.messages ? 'chatapi' : return req.body.messages ? 'chatapi' : req.body.message ? 'maytapi' : 'GeneralBots';
req.body.message ? 'maytapi' : 'GeneralBots';
} }
public async received (req, res) { public async received (req, res) {
const provider = WhatsappDirectLine.providerFromRequest(req); const provider = WhatsappDirectLine.providerFromRequest(req);
let message, from, fromName, text; let message, from, fromName, text;
let group = ""; let group = '';
let answerText = null; let answerText = null;
let attachments = null; let attachments = null;
@ -344,15 +342,13 @@ export class WhatsappDirectLine extends GBService {
case 'GeneralBots': case 'GeneralBots':
message = req; message = req;
text = message.body; text = message.body;
from = message.from.endsWith('@g.us') ? from = message.from.endsWith('@g.us') ? message.author.split('@')[0] : message.from.split('@')[0];
message.author.split('@')[0] : message.from.split('@')[0];
fromName = message._data.notifyName; fromName = message._data.notifyName;
if (message.hasMedia) { if (message.hasMedia) {
const base64Image = await message.downloadMedia(); const base64Image = await message.downloadMedia();
attachments = []; attachments = [];
attachments.push( attachments.push({
{
name: 'uploaded.png', name: 'uploaded.png',
contentType: base64Image.mimetype, contentType: base64Image.mimetype,
contentUrl: `data:${base64Image.mimetype};base64,${base64Image.data}` contentUrl: `data:${base64Image.mimetype};base64,${base64Image.data}`
@ -369,8 +365,7 @@ export class WhatsappDirectLine extends GBService {
if (message.type !== 'chat') { if (message.type !== 'chat') {
attachments = []; attachments = [];
attachments.push( attachments.push({
{
name: 'uploaded', name: 'uploaded',
contentType: 'application/octet-stream', contentType: 'application/octet-stream',
contentUrl: message.body contentUrl: message.body
@ -405,18 +400,16 @@ export class WhatsappDirectLine extends GBService {
let botGroupID = WhatsappDirectLine.botGroups[this.min.botId]; let botGroupID = WhatsappDirectLine.botGroups[this.min.botId];
let botShortcuts = this.min.core.getParam<string>(this.min.instance, 'WhatsApp Group Shortcuts', null); let botShortcuts = this.min.core.getParam<string>(this.min.instance, 'WhatsApp Group Shortcuts', null);
if (!botShortcuts) { if (!botShortcuts) {
botShortcuts = new Array() botShortcuts = new Array();
} } else {
else {
botShortcuts = botShortcuts.split(' '); botShortcuts = botShortcuts.split(' ');
} }
if (provider === "chatapi") { if (provider === 'chatapi') {
if (message.chatName.charAt(0) !== '+') { if (message.chatName.charAt(0) !== '+') {
group = message.chatName; group = message.chatName;
} }
} } else if (provider === 'GeneralBots') {
else if (provider === "GeneralBots") {
if (message.from.endsWith('@g.us')) { if (message.from.endsWith('@g.us')) {
group = message.from; group = message.from;
} }
@ -428,7 +421,6 @@ export class WhatsappDirectLine extends GBService {
// Bot name must be specified on config. // Bot name must be specified on config.
if (botGroupID === group) { if (botGroupID === group) {
// Shortcut has been mentioned? // Shortcut has been mentioned?
let found = false; let found = false;
@ -440,21 +432,18 @@ export class WhatsappDirectLine extends GBService {
} }
}); });
// Verify if it is a group cache answer. // Verify if it is a group cache answer.
const questions = this.min['groupCache']; const questions = this.min['groupCache'];
if (questions && questions.length > 0) { if (questions && questions.length > 0) {
questions.forEach(q => { questions.forEach(q => {
if (q.content === e1 && !found) { if (q.content === e1 && !found) {
const answer = this.min.kbService['getAnswerById'](this.min.instance.instanceId, const answer = this.min.kbService['getAnswerById'](this.min.instance.instanceId, q.answerId);
q.answerId);
answerText = answer.content; answerText = answer.content;
} }
}); });
} }
// Ignore group messages without the mention to Bot. // Ignore group messages without the mention to Bot.
let smsServiceNumber = this.min.core.getParam<string>(this.min.instance, 'whatsappServiceNumber', null); let smsServiceNumber = this.min.core.getParam<string>(this.min.instance, 'whatsappServiceNumber', null);
@ -464,10 +453,8 @@ export class WhatsappDirectLine extends GBService {
return; return;
} }
} }
}); });
} }
} }
const botId = this.min.instance.botId; const botId = this.min.instance.botId;
@ -478,8 +465,7 @@ export class WhatsappDirectLine extends GBService {
await state.promise(null, text); await state.promise(null, text);
return; // Exit here. return; // Exit here.
}; }
// Processes .gbapp message interception. // Processes .gbapp message interception.
@ -488,8 +474,7 @@ export class WhatsappDirectLine extends GBService {
}); });
const sec = new SecService(); const sec = new SecService();
const user = await sec.ensureUser(this.min.instance.instanceId, from, const user = await sec.ensureUser(this.min.instance.instanceId, from, fromName, '', 'whatsapp', fromName, null);
fromName, '', 'whatsapp', fromName, null);
const locale = user.locale ? user.locale : 'pt'; const locale = user.locale ? user.locale : 'pt';
if (answerText) { if (answerText) {
@ -499,7 +484,6 @@ export class WhatsappDirectLine extends GBService {
} }
if (message.type === 'ptt') { if (message.type === 'ptt') {
if (process.env.AUDIO_DISABLED !== 'true') { if (process.env.AUDIO_DISABLED !== 'true') {
const options = { const options = {
url: provider ? message.body : message.text, url: provider ? message.body : message.text,
@ -512,11 +496,11 @@ export class WhatsappDirectLine extends GBService {
text = await GBConversationalService.getTextFromAudioBuffer( text = await GBConversationalService.getTextFromAudioBuffer(
this.min.instance.speechKey, this.min.instance.speechKey,
this.min.instance.cloudLocation, this.min.instance.cloudLocation,
buf, locale buf,
locale
); );
} else { } else {
await this.sendToDevice(user.userSystemId, await this.sendToDevice(user.userSystemId, `No momento estou apenas conseguindo ler mensagens de texto.`, null);
`No momento estou apenas conseguindo ler mensagens de texto.`, null);
} }
} }
@ -527,14 +511,11 @@ export class WhatsappDirectLine extends GBService {
// Check if this message is from a Human Agent itself. // Check if this message is from a Human Agent itself.
if (user.agentMode === 'self') { if (user.agentMode === 'self') {
// Check if there is someone being handled by this Human Agent. // Check if there is someone being handled by this Human Agent.
const manualUser = await sec.getUserFromAgentSystemId(from); const manualUser = await sec.getUserFromAgentSystemId(from);
if (manualUser === null) { if (manualUser === null) {
await sec.updateHumanAgent(from, this.min.instance.instanceId, null); await sec.updateHumanAgent(from, this.min.instance.instanceId, null);
} else { } else {
const agent = await sec.getUserFromSystemId(user.agentSystemId); const agent = await sec.getUserFromSystemId(user.agentSystemId);
@ -544,23 +525,38 @@ export class WhatsappDirectLine extends GBService {
const message = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename); const message = await this.min.kbService.getAnswerTextByMediaName(this.min.instance.instanceId, filename);
if (message === null) { if (message === null) {
await this.sendToDeviceEx(user.userSystemId, `File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`, await this.sendToDeviceEx(
locale, null); user.userSystemId,
`File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`,
locale,
null
);
} else { } else {
await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message); await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message);
} }
} else if (text === '/qt') { } else if (text === '/qt') {
// TODO: Transfers only in pt-br for now. // TODO: Transfers only in pt-br for now.
await this.sendToDeviceEx(manualUser.userSystemId, await this.sendToDeviceEx(
Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); manualUser.userSystemId,
Messages[this.locale].notify_end_transfer(this.min.instance.botId),
if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams. locale,
await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId)); null
} );
else {
await this.sendToDeviceEx(user.agentSystemId,
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(manualUser.userSystemId, this.min.instance.instanceId, null); await sec.updateHumanAgent(manualUser.userSystemId, this.min.instance.instanceId, null);
await sec.updateHumanAgent(user.agentSystemId, this.min.instance.instanceId, null); await sec.updateHumanAgent(user.agentSystemId, this.min.instance.instanceId, null);
@ -569,48 +565,50 @@ export class WhatsappDirectLine extends GBService {
await this.sendToDeviceEx(manualUser.userSystemId, `AGENTE: *${text}*`, locale, null); await this.sendToDeviceEx(manualUser.userSystemId, `AGENTE: *${text}*`, locale, null);
} }
} }
} else if (user.agentMode === 'human') { } else if (user.agentMode === 'human') {
const agent = await sec.getUserFromSystemId(user.agentSystemId); const agent = await sec.getUserFromSystemId(user.agentSystemId);
if (text === '/t') { if (text === '/t') {
await this.sendToDeviceEx(user.userSystemId, `Você já está sendo atendido por ${agent.userSystemId}.`, locale, null); await this.sendToDeviceEx(
user.userSystemId,
`Você já está sendo atendido por ${agent.userSystemId}.`,
locale,
null
);
} else if (text === '/qt' || text === 'Sair' || text === 'Fechar') { } else if (text === '/qt' || text === 'Sair' || text === 'Fechar') {
// TODO: Transfers only in pt-br for now. // TODO: Transfers only in pt-br for now.
await this.endTransfer(from, locale, user, agent, sec); await this.endTransfer(from, locale, user, agent, sec);
} else { } else {
GBLog.info(`USER (${from}) TO AGENT ${agent.userSystemId}: ${text}`); GBLog.info(`USER (${from}) TO AGENT ${agent.userSystemId}: ${text}`);
if (user.agentSystemId.charAt(2) === ":" || agent.userSystemId.indexOf("@") > -1) { // Agent is from Teams or Google Chat. if (user.agentSystemId.charAt(2) === ':' || agent.userSystemId.indexOf('@') > -1) {
// Agent is from Teams or Google Chat.
await this.min.conversationalService['sendOnConversation'](this.min, agent, text); await this.min.conversationalService['sendOnConversation'](this.min, agent, text);
} else {
await this.sendToDeviceEx(
user.agentSystemId,
`Bot: ${this.min.instance.botId}\n${from}: ${text}`,
locale,
null
);
} }
else {
await this.sendToDeviceEx(user.agentSystemId, `Bot: ${this.min.instance.botId}\n${from}: ${text}`, locale, null);
} }
}
} else if (user.agentMode === 'bot' || user.agentMode === null || user.agentMode === undefined) { } else if (user.agentMode === 'bot' || user.agentMode === null || user.agentMode === undefined) {
if (WhatsappDirectLine.conversationIds[botId + from + group] === undefined) { if (WhatsappDirectLine.conversationIds[botId + from + group] === undefined) {
GBLog.info(`GBWhatsapp: Starting new conversation on Bot.`); GBLog.info(`GBWhatsapp: Starting new conversation on Bot.`);
const response = await client.Conversations.Conversations_StartConversation(); const response = await client.Conversations.Conversations_StartConversation();
const generatedConversationId = response.obj.conversationId; const generatedConversationId = response.obj.conversationId;
WhatsappDirectLine.conversationIds[botId + from + group] = generatedConversationId; WhatsappDirectLine.conversationIds[botId + from + group] = generatedConversationId;
if (provider === "GeneralBots") { if (provider === 'GeneralBots') {
WhatsappDirectLine.chatIds[generatedConversationId] = message.from; WhatsappDirectLine.chatIds[generatedConversationId] = message.from;
} }
WhatsappDirectLine.mobiles[generatedConversationId] = from; WhatsappDirectLine.mobiles[generatedConversationId] = from;
WhatsappDirectLine.usernames[from] = fromName; WhatsappDirectLine.usernames[from] = fromName;
WhatsappDirectLine.chatIds[generatedConversationId] = message.chatId; WhatsappDirectLine.chatIds[generatedConversationId] = message.chatId;
this.pollMessages(client, generatedConversationId, from, fromName); this.pollMessages(client, generatedConversationId, from, fromName);
this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments); this.inputMessage(client, generatedConversationId, text, from, fromName, group, attachments);
} else { } else {
this.inputMessage(client, conversationId, text, from, fromName, group, attachments); this.inputMessage(client, conversationId, text, from, fromName, group, attachments);
} }
} else { } else {
@ -623,14 +621,22 @@ export class WhatsappDirectLine extends GBService {
} }
private async endTransfer (id: any, locale: string, user: GuaribasUser, agent: GuaribasUser, sec: SecService) { private async endTransfer (id: any, locale: string, user: GuaribasUser, agent: GuaribasUser, sec: SecService) {
await this.sendToDeviceEx(id, await this.sendToDeviceEx(id, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null);
if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams. if (user.agentSystemId.charAt(2) === ':') {
await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId)); // Agent is from Teams.
} await this.min.conversationalService['sendOnConversation'](
else { this.min,
await this.sendToDeviceEx(user.agentSystemId, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale, null); 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 sec.updateHumanAgent(id, this.min.instance.instanceId, null);
@ -669,8 +675,11 @@ export class WhatsappDirectLine extends GBService {
watermark = response.obj.watermark; watermark = response.obj.watermark;
await this.printMessages(response.obj.activities, conversationId, from, fromName); await this.printMessages(response.obj.activities, conversationId, from, fromName);
} catch (err) { } catch (err) {
GBLog.error(`Error calling printMessages on Whatsapp channel ${err.data === undefined ? GBLog.error(
err : err.data} ${err.errObj ? err.errObj.message : ''}`); `Error calling printMessages on Whatsapp channel ${err.data === undefined ? err : err.data} ${
err.errObj ? err.errObj.message : ''
}`
);
} }
}; };
setInterval(worker, this.pollInterval); setInterval(worker, this.pollInterval);
@ -725,16 +734,14 @@ export class WhatsappDirectLine extends GBService {
} }
public async sendFileToDevice (to, url, filename, caption, chatId) { public async sendFileToDevice (to, url, filename, caption, chatId) {
let options; let options;
switch (this.provider) { switch (this.provider) {
case 'GeneralBots': case 'GeneralBots':
const attachment = await MessageMedia.fromUrl(url); const attachment = await wpp.MessageMedia.fromUrl(url);
if (to.indexOf('@') == -1) { if (to.indexOf('@') == -1) {
if (to.length == 18) { if (to.length == 18) {
to = to + '@g.us'; to = to + '@g.us';
} } else {
else {
to = to + '@c.us'; to = to + '@c.us';
} }
} }
@ -743,7 +750,6 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'chatapi': case 'chatapi':
options = { options = {
method: 'POST', method: 'POST',
url: urlJoin(this.whatsappServiceUrl, 'sendFile'), url: urlJoin(this.whatsappServiceUrl, 'sendFile'),
@ -764,17 +770,16 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'maytapi': case 'maytapi':
let contents = 0; let contents = 0;
let body = { let body = {
to_number: to, to_number: to,
type: "media", type: 'media',
message: url, message: url,
text: caption text: caption
}; };
let phoneId = this.whatsappServiceNumber.split(';')[0]; let phoneId = this.whatsappServiceNumber.split(';')[0];
let productId = this.whatsappServiceNumber.split(';')[1] let productId = this.whatsappServiceNumber.split(';')[1];
options = { options = {
url: `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`, url: `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`,
@ -783,8 +788,8 @@ export class WhatsappDirectLine extends GBService {
body, body,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'x-maytapi-key': this.whatsappServiceKey, 'x-maytapi-key': this.whatsappServiceKey
}, }
}; };
break; break;
@ -801,18 +806,15 @@ export class WhatsappDirectLine extends GBService {
} }
public async sendAudioToDevice (to, url, chatId) { public async sendAudioToDevice (to, url, chatId) {
let options; let options;
switch (this.provider) { switch (this.provider) {
case 'GeneralBots': case 'GeneralBots':
const attachment = wpp.MessageMedia.fromUrl(url);
const attachment = MessageMedia.fromUrl(url);
await this.customClient.sendMessage(to, attachment); await this.customClient.sendMessage(to, attachment);
break; break;
case 'chatapi': case 'chatapi':
options = { options = {
method: 'POST', method: 'POST',
url: urlJoin(this.whatsappServiceUrl, 'sendPTT'), url: urlJoin(this.whatsappServiceUrl, 'sendPTT'),
@ -829,7 +831,6 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'maytapi': case 'maytapi':
options = {}; // TODO: Code Maytapi. options = {}; // TODO: Code Maytapi.
break; break;
@ -846,36 +847,29 @@ export class WhatsappDirectLine extends GBService {
} }
public async sendTextAsAudioToDevice (to, msg, chatId) { public async sendTextAsAudioToDevice (to, msg, chatId) {
const url = await GBConversationalService.getAudioBufferFromText(msg);
const url = await GBConversationalService.getAudioBufferFromText(
msg
);
await this.sendFileToDevice(to, url, 'Audio', msg, chatId); await this.sendFileToDevice(to, url, 'Audio', msg, chatId);
} }
public async sendToDevice (to: string, msg: string, conversationId) { public async sendToDevice (to: string, msg: string, conversationId) {
const cmd = '/audio '; const cmd = '/audio ';
let chatId = WhatsappDirectLine.chatIds[conversationId]; let chatId = WhatsappDirectLine.chatIds[conversationId];
if (typeof (msg) !== 'object' && msg.startsWith(cmd)) { if (typeof msg !== 'object' && msg.startsWith(cmd)) {
msg = msg.substr(cmd.length); msg = msg.substr(cmd.length);
return await this.sendTextAsAudioToDevice(to, msg, chatId); return await this.sendTextAsAudioToDevice(to, msg, chatId);
} else { } else {
let options; let options;
switch (this.provider) { switch (this.provider) {
case 'GeneralBots': case 'GeneralBots':
if (to.indexOf('@') == -1) { if (to.indexOf('@') == -1) {
if (to.length == 18) { if (to.length == 18) {
to = to + '@g.us'; to = to + '@g.us';
} } else {
else {
to = to + '@c.us'; to = to + '@c.us';
} }
} }
@ -884,8 +878,6 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'chatapi': case 'chatapi':
options = { options = {
method: 'POST', method: 'POST',
url: urlJoin(this.whatsappServiceUrl, 'message'), url: urlJoin(this.whatsappServiceUrl, 'message'),
@ -902,7 +894,7 @@ export class WhatsappDirectLine extends GBService {
break; break;
case 'maytapi': case 'maytapi':
let phoneId = this.whatsappServiceNumber.split(';')[0]; let phoneId = this.whatsappServiceNumber.split(';')[0];
let productId = this.whatsappServiceNumber.split(';')[1] let productId = this.whatsappServiceNumber.split(';')[1];
let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`; let url = `${this.INSTANCE_URL}/${productId}/${phoneId}/sendMessage`;
options = { options = {
@ -912,8 +904,8 @@ export class WhatsappDirectLine extends GBService {
body: { type: 'text', message: msg, to_number: to }, body: { type: 'text', message: msg, to_number: to },
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'x-maytapi-key': this.whatsappServiceKey, 'x-maytapi-key': this.whatsappServiceKey
}, }
}; };
break; break;
} }
@ -922,8 +914,7 @@ export class WhatsappDirectLine extends GBService {
try { try {
GBLog.info(`Message [${msg}] is being sent to ${to}...`); GBLog.info(`Message [${msg}] is being sent to ${to}...`);
await request.post(options); await request.post(options);
} } catch (error) {
catch (error) {
GBLog.error(`Error sending message to Whatsapp provider ${error.message}`); GBLog.error(`Error sending message to Whatsapp provider ${error.message}`);
// TODO: Handle Error: socket hang up and retry. // TODO: Handle Error: socket hang up and retry.
@ -933,18 +924,12 @@ export class WhatsappDirectLine extends GBService {
} }
public async sendToDeviceEx (to, text, locale, conversationId) { public async sendToDeviceEx (to, text, locale, conversationId) {
text = await this.min.conversationalService.translate( text = await this.min.conversationalService.translate(this.min, text, locale);
this.min,
text,
locale
);
await this.sendToDevice(to, text, conversationId); await this.sendToDevice(to, text, conversationId);
} }
private async WhatsAppCallback (req, res) { private async WhatsAppCallback (req, res) {
try { try {
if (req.body && req.body.webhook) { if (req.body && req.body.webhook) {
res.status(200); res.status(200);
res.end(); res.end();
@ -958,14 +943,11 @@ export class WhatsappDirectLine extends GBService {
let botId; let botId;
let text; let text;
switch (provider) { switch (provider) {
case "GeneralBots": case 'GeneralBots':
// Ignore E2E messages used during initialization. // Ignore E2E messages used during initialization.
if (req.type && req.type === 'e2e_notification') { if (req.type && req.type === 'e2e_notification') {
return; return;
} }
@ -973,11 +955,9 @@ export class WhatsappDirectLine extends GBService {
senderName = req._data.notifyName; senderName = req._data.notifyName;
text = req.body; text = req.body;
break; break;
case "chatapi": case 'chatapi':
if (req.body.ack) { if (req.body.ack) {
res.status(200); res.status(200);
res.end(); res.end();
@ -997,8 +977,7 @@ export class WhatsappDirectLine extends GBService {
botId = GBConfigService.get('BOT_ID'); botId = GBConfigService.get('BOT_ID');
} }
break; break;
case "maytapi": case 'maytapi':
if (req.body.type !== 'message') { if (req.body.type !== 'message') {
res.status(200); res.status(200);
res.end(); res.end();
@ -1023,8 +1002,7 @@ export class WhatsappDirectLine extends GBService {
let user = await sec.getUserFromSystemId(id); let user = await sec.getUserFromSystemId(id);
GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`); GBLog.info(`A WhatsApp mobile requested instance for: ${botId}.`);
let urlMin: any = GBServer.globals.minInstances.filter let urlMin: any = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
(p => p.instance.botId === botId)[0];
const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null; const botNumber = urlMin ? urlMin.core.getParam(urlMin.instance, 'Bot Number', null) : null;
let activeMin; let activeMin;
@ -1034,17 +1012,14 @@ export class WhatsappDirectLine extends GBService {
text = text.replace(/\@\d+ /gi, ''); text = text.replace(/\@\d+ /gi, '');
let group; let group;
if (provider === "chatapi") { if (provider === 'chatapi') {
// Ensures that the bot group is the active bot for the user (like switching). // Ensures that the bot group is the active bot for the user (like switching).
const message = req.body.messages[0]; const message = req.body.messages[0];
if (message.chatName.charAt(0) !== '+') { if (message.chatName.charAt(0) !== '+') {
group = message.chatName; group = message.chatName;
} }
} } else if (provider === 'GeneralBots') {
else if (provider === "GeneralBots") {
// Ensures that the bot group is the active bot for the user (like switching). // Ensures that the bot group is the active bot for the user (like switching).
const message = req; const message = req;
@ -1059,13 +1034,11 @@ export class WhatsappDirectLine extends GBService {
return Object.keys(object).find(key => object[key] === value); return Object.keys(object).find(key => object[key] === value);
} }
const botId = getKeyByValue(WhatsappDirectLine.botGroups, group); const botId = getKeyByValue(WhatsappDirectLine.botGroups, group);
if (botId && user.instanceId !== this.min.instance.instanceId || !user) { if ((botId && user.instanceId !== this.min.instance.instanceId) || !user) {
user = await sec.ensureUser(this.min.instance.instanceId, id, senderName, '', 'whatsApp', senderName, null); user = await sec.ensureUser(this.min.instance.instanceId, id, senderName, '', 'whatsApp', senderName, null);
} }
if (botId) { if (botId) {
activeMin = GBServer.globals.minInstances.filter activeMin = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
(p => p.instance.botId === botId)[0];
await (activeMin as any).whatsAppDirectLine.received(req, res); await (activeMin as any).whatsAppDirectLine.received(req, res);
return; // EXIT HERE. return; // EXIT HERE.
} }
@ -1074,7 +1047,6 @@ export class WhatsappDirectLine extends GBService {
// Detects if the welcome message is enabled. // Detects if the welcome message is enabled.
if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') { if (process.env.WHATSAPP_WELCOME_DISABLED !== 'true') {
// Tries to find if user wants to switch bots. // Tries to find if user wants to switch bots.
let toSwitchMin = GBServer.globals.minInstances.filter( let toSwitchMin = GBServer.globals.minInstances.filter(
@ -1088,8 +1060,7 @@ export class WhatsappDirectLine extends GBService {
// If bot has a fixed Find active bot instance. // If bot has a fixed Find active bot instance.
activeMin = botNumber ? urlMin : activeMin = botNumber ? urlMin : toSwitchMin ? toSwitchMin : GBServer.globals.minBoot;
toSwitchMin ? toSwitchMin : GBServer.globals.minBoot;
// If it is the first time for the user, tries to auto-execute // If it is the first time for the user, tries to auto-execute
// start dialog if any is specified in Config.xlsx. // start dialog if any is specified in Config.xlsx.
@ -1097,19 +1068,17 @@ export class WhatsappDirectLine extends GBService {
if (user === null || user.hearOnDialog) { if (user === null || user.hearOnDialog) {
user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null); user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null);
const startDialog = user.hearOnDialog ? const startDialog = user.hearOnDialog
user.hearOnDialog : ? user.hearOnDialog
activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); : activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
if (startDialog) { if (startDialog) {
GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`); GBLog.info(`Calling /start to Auto start ${startDialog} for ${activeMin.instance.instanceId}...`);
if (provider === "chatapi") { if (provider === 'chatapi') {
req.body.messages[0].body = `/start`; req.body.messages[0].body = `/start`;
} } else if (provider === 'maytapi') {
else if (provider === "maytapi") {
req.body.message = `/start`; req.body.message = `/start`;
} } else {
else {
req.body = `/start`; req.body = `/start`;
} }
@ -1121,19 +1090,19 @@ export class WhatsappDirectLine extends GBService {
} else { } else {
await (activeMin as any).whatsAppDirectLine.sendToDevice( await (activeMin as any).whatsAppDirectLine.sendToDevice(
id, id,
`Olá! Seja bem-vinda(o)!\nMe chamo ${activeMin.instance.title}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.` `Olá! Seja bem-vinda(o)!\nMe chamo ${
, null); activeMin.instance.title
}. Como posso ajudar? Pode me falar que eu te ouço, me manda um aúdio.`,
null
);
if (res) { if (res) {
res.end(); res.end();
} }
} }
} else { } else {
// User wants to switch bots. // User wants to switch bots.
if (toSwitchMin !== undefined) { if (toSwitchMin !== undefined) {
// So gets the new bot instance information and prepares to // So gets the new bot instance information and prepares to
// auto start dialog if any is specified. // auto start dialog if any is specified.
@ -1142,16 +1111,13 @@ export class WhatsappDirectLine extends GBService {
await (activeMin as any).whatsAppDirectLine.resetConversationId(activeMin.botId, id, ''); await (activeMin as any).whatsAppDirectLine.resetConversationId(activeMin.botId, id, '');
const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null); const startDialog = activeMin.core.getParam(activeMin.instance, 'Start Dialog', null);
if (startDialog) { if (startDialog) {
GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`); GBLog.info(`Calling /start for Auto start : ${startDialog} for ${activeMin.instance.botId}...`);
if (provider === "chatapi") { if (provider === 'chatapi') {
req.body.messages[0].body = `/start`; req.body.messages[0].body = `/start`;
} } else if (provider === 'maytapi') {
else if (provider === "maytapi") {
req.body.message = `/start`; req.body.message = `/start`;
} } else {
else {
req.body = `/start`; req.body = `/start`;
} }
@ -1166,14 +1132,15 @@ export class WhatsappDirectLine extends GBService {
if (res) { if (res) {
res.end(); res.end();
} }
} else { } else {
activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0]; activeMin = GBServer.globals.minInstances.filter(p => p.instance.instanceId === user.instanceId)[0];
if (activeMin === undefined) { if (activeMin === undefined) {
activeMin = GBServer.globals.minBoot; activeMin = GBServer.globals.minBoot;
await (activeMin as any).whatsAppDirectLine.sendToDevice( await (activeMin as any).whatsAppDirectLine.sendToDevice(
id, id,
`O outro Bot que você estava falando(${user.instanceId}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...` `O outro Bot que você estava falando(${
user.instanceId
}), não está mais disponível. Agora você está falando comigo, ${activeMin.instance.title}...`
); );
} }
await (activeMin as any).whatsAppDirectLine.received(req, res); await (activeMin as any).whatsAppDirectLine.received(req, res);
@ -1184,13 +1151,11 @@ export class WhatsappDirectLine extends GBService {
p => p.instance.botId.toLowerCase() === botId.toLowerCase() p => p.instance.botId.toLowerCase() === botId.toLowerCase()
)[0]; )[0];
// Just pass the message to the receiver. // Just pass the message to the receiver.
await minInstance.whatsAppDirectLine.received(req, res); await minInstance.whatsAppDirectLine.received(req, res);
} }
} catch (error) { } catch (error) {
GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`); GBLog.error(`Error on Whatsapp callback: ${error.data ? error.data : error} ${error.stack}`);
} }
} }

View file

@ -1,8 +1,8 @@
export const Messages = { export const Messages = {
'en-US': { 'en-US': {
notify_end_transfer: (botName) => `Now talking to ${botName} again.` notify_end_transfer: botName => `Now talking to ${botName} again.`
}, },
'pt-BR': { 'pt-BR': {
notify_end_transfer: (botName) => `Falando com o bot ${botName} novamente.` notify_end_transfer: botName => `Falando com o bot ${botName} novamente.`
} }
}; };

View file

@ -36,24 +36,24 @@
'use strict'; 'use strict';
const express = require('express'); import express from 'express';
const bodyParser = require('body-parser'); import bodyParser from 'body-parser';
const https = require('https'); import https from 'https';
const mkdirp = require('mkdirp'); import mkdirp from 'mkdirp';
const Path = require('path'); import Path from 'path';
import * as Fs from 'fs';
import * as fs from 'fs';
import { GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; import { GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService'; import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService.js';
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService'; import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService.js';
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService'; import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService.js';
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService'; import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService.js';
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer'; import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService'; import { GBImporter } from '../packages/core.gbapp/services/GBImporterService.js';
import { GBMinService } from '../packages/core.gbapp/services/GBMinService'; import { GBMinService } from '../packages/core.gbapp/services/GBMinService.js';
var auth = require('basic-auth'); import auth from 'basic-auth';
import child_process from 'child_process';
import * as winston from 'winston-logs-display';
/** /**
* Global shared server data; * Global shared server data;
@ -70,6 +70,7 @@ export class RootData {
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. public debugConversationId: any; // Used to self-message during debug.
public debuggers: any[]; // Client of attached Debugger instances by botId.
} }
/** /**
* General Bots open-core entry point. * General Bots open-core entry point.
@ -82,36 +83,43 @@ export class GBServer {
*/ */
public static run () { public static run () {
GBLog.info(`The Bot Server is in STARTING mode...`); GBLog.info(`The Bot Server is in STARTING mode...`);
GBServer.globals = new RootData(); GBServer.globals = new RootData();
GBConfigService.init(); GBConfigService.init();
const port = GBConfigService.getServerPort(); const port = GBConfigService.getServerPort();
if (process.env.TEST_SHELL) {
GBLog.info(`Running TEST_SHELL: ${process.env.TEST_SHELL}...`);
try {
child_process.execSync(process.env.TEST_SHELL);
} catch (error) {
GBLog.error(`Running TEST_SHELL ERROR: ${error}...`);
}
}
const server = express(); const server = express();
GBServer.globals.server = server; GBServer.globals.server = server;
GBServer.globals.appPackages = []; GBServer.globals.appPackages = [];
GBServer.globals.sysPackages = []; GBServer.globals.sysPackages = [];
GBServer.globals.minInstances = []; GBServer.globals.minInstances = [];
GBServer.globals.wwwroot = null; GBServer.globals.wwwroot = null;
GBServer.globals.entryPointDialog = null; GBServer.globals.entryPointDialog = null;
GBServer.globals.debuggers = [];
server.use(bodyParser.json()); server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true })); server.use(bodyParser.urlencoded({ extended: true }));
// Creates working directory. // Creates working directory.
const workDir = Path.join(process.env.PWD, 'work'); const workDir = Path.join(process.env.PWD, 'work');
if (!fs.existsSync(workDir)) { if (!Fs.existsSync(workDir)) {
mkdirp.sync(workDir); mkdirp.sync(workDir);
} }
const mainCallback = () => { const mainCallback = () => {
(async () => { (async () => {
try { try {
GBLog.info(`Now accepting connections on ${port}...`); GBLog.info(`Now accepting connections on ${port}...`);
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
@ -146,9 +154,11 @@ export class GBServer {
await core.initStorage(); await core.initStorage();
} catch (error) { } catch (error) {
GBLog.verbose(`Error initializing storage: ${error}`); GBLog.verbose(`Error initializing storage: ${error}`);
GBServer.globals.bootInstance = GBServer.globals.bootInstance = await core.createBootInstance(
await core.createBootInstance(core, azureDeployer, GBServer.globals.publicAddress); core,
azureDeployer,
GBServer.globals.publicAddress
);
} }
core.ensureAdminIsSecured(); core.ensureAdminIsSecured();
@ -176,7 +186,6 @@ export class GBServer {
); );
if (instances.length === 0) { if (instances.length === 0) {
const instance = await importer.importIfNotExistsBotPackage( const instance = await importer.importIfNotExistsBotPackage(
GBConfigService.get('BOT_ID'), GBConfigService.get('BOT_ID'),
'boot.gbot', 'boot.gbot',
@ -201,30 +210,30 @@ export class GBServer {
await minService.buildMin(instances); await minService.buildMin(instances);
if (process.env.ENABLE_WEBLOG) { if (process.env.ENABLE_WEBLOG) {
var admins = { const admins = {
'admin': { password: process.env.ADMIN_PASS }, admin: { password: process.env.ADMIN_PASS }
}; };
// ... some not authenticated middlewares // ... some not authenticated middlewares
server.use((req, res, next) => { server.use(async (req, res, next) => {
if (req.originalUrl.startsWith('/logs')) { if (req.originalUrl.startsWith('/logs')) {
var user = auth(req); const user = auth(req);
if (!user || !admins[user.name] || admins[user.name].password !== user.pass) { if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
res.set('WWW-Authenticate', 'Basic realm="example"'); res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send(); return res.status(401).send();
} }
} } else {
return next(); return next();
}
}); });
// If global log enabled, reorders transports adding web logging. // If global log enabled, reorders transports adding web logging.
const loggers = GBLog.getLogger(); const loggers = GBLog.getLogger();
require('winston-logs-display')(server, loggers[1]); winston.default(server, loggers[1]);
} }
GBLog.info(`The Bot Server is in RUNNING mode...`); GBLog.info(`The Bot Server is in RUNNING mode...`);
// Opens Navigator. // Opens Navigator.
@ -237,22 +246,20 @@ export class GBServer {
})(); })();
}; };
if (process.env.CERTIFICATE_PFX) { if (process.env.CERTIFICATE_PFX) {
let options = { const options = {
passphrase: process.env.CERTIFICATE_PASSPHRASE, passphrase: process.env.CERTIFICATE_PASSPHRASE,
pfx: fs.readFileSync(process.env.CERTIFICATE_PFX) pfx: Fs.readFileSync(process.env.CERTIFICATE_PFX)
}; };
const httpsServer = https.createServer(options, server).listen(port, mainCallback); const httpsServer = https.createServer(options, server).listen(port, mainCallback);
if (process.env.CERTIFICATE2_PFX) { if (process.env.CERTIFICATE2_PFX) {
let options = { const options = {
passphrase: process.env.CERTIFICATE2_PASSPHRASE, passphrase: process.env.CERTIFICATE2_PASSPHRASE,
pfx: fs.readFileSync(process.env.CERTIFICATE2_PFX) pfx: Fs.readFileSync(process.env.CERTIFICATE2_PFX)
}; };
httpsServer.addContext(process.env.CERTIFICATE2_DOMAIN, options); httpsServer.addContext(process.env.CERTIFICATE2_DOMAIN, options);
} }
} } else {
else {
server.listen(port, mainCallback); server.listen(port, mainCallback);
} }
} }

View file

@ -6,10 +6,12 @@
"declaration": false, "declaration": false,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"mapRoot": "./dist/", "mapRoot": "./dist/",
"module": "commonjs", "moduleResolution": "Node",
"moduleResolution": "node", "module": "ESNext",
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"outDir": "./dist", "outDir": "./dist",
"paths": { "paths": {