Merge branch 'master' of https://github.com/GeneralBots/BotServer
This commit is contained in:
commit
5e725a6b82
78 changed files with 3766 additions and 4450 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -15,3 +15,6 @@
|
||||||
/work
|
/work
|
||||||
/packages/default.gbdialog/bot.js
|
/packages/default.gbdialog/bot.js
|
||||||
/packages/default.gbdialog/bot.ts
|
/packages/default.gbdialog/bot.ts
|
||||||
|
*.vbs.compiled
|
||||||
|
*.vbs.js
|
||||||
|
*.vbs.ts
|
||||||
|
|
|
@ -5,4 +5,5 @@
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true
|
"singleQuote": true
|
||||||
|
|
||||||
}
|
}
|
8
FEATURES.md
Normal file
8
FEATURES.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# General Bots Features
|
||||||
|
|
||||||
|
| Feature | BF | GB |
|
||||||
|
|----------------------------------------------------------------------------|----|----|
|
||||||
|
| Use of conversational administration to manage bot packages (Talk to admin | - | X |
|
||||||
|
| F5 to run on VSCode | - | X |
|
||||||
|
| Isolated code on packages | - | X |
|
||||||
|
| Breaking changes protected | - | X |
|
262
README.md
262
README.md
|
@ -6,69 +6,18 @@
|
||||||
| Building & Quality | [](https://travis-ci.com/pragmatismo-io/BotServer) [](https://coveralls.io/github/pragmatismo-io/BotServer) [](https://github.com/prettier/prettier) |
|
| Building & Quality | [](https://travis-ci.com/pragmatismo-io/BotServer) [](https://coveralls.io/github/pragmatismo-io/BotServer) [](https://github.com/prettier/prettier) |
|
||||||
| Packaging | [](https://badge.fury.io) [](https://david-dm.org) [](https://greenkeeper.io/) [](http://commitizen.github.io/cz-cli/) |
|
| Packaging | [](https://badge.fury.io) [](https://david-dm.org) [](https://greenkeeper.io/) [](http://commitizen.github.io/cz-cli/) |
|
||||||
| Releases | [](https://www.npmjs.com/package/botserver/) [](https://www.npmjs.com/package/botlib/) [](https://github.com/semantic-release/semantic-release)|
|
| Releases | [](https://www.npmjs.com/package/botserver/) [](https://www.npmjs.com/package/botlib/) [](https://github.com/semantic-release/semantic-release)|
|
||||||
|
| Samples | [default.gbdialog (VBA)](https://github.com/pragmatismo-io/BotServer/tree/master/packages/default.gbdialog) [AzureADPasswordReset.gbapp (TypeScript)](https://github.com/pragmatismo-io/AzureADPasswordReset.gbapp)|
|
||||||
| [Docker Image](https://github.com/lpicanco/docker-botserver) |      <br/> *Provided by [@lpicanco](https://github.com/lpicanco/docker-botserver)* |
|
| [Docker Image](https://github.com/lpicanco/docker-botserver) |      <br/> *Provided by [@lpicanco](https://github.com/lpicanco/docker-botserver)* |
|
||||||
|
|
||||||
|
General Bots
|
||||||
#### Watch a video about easeness authoring of bot packages, development environment and self-deployment
|
------------------
|
||||||
|
|
||||||
* Now with the General Bots server you can press F5 on Visual Studio to get a bot factory on your environment* published on November 10th, 2018.
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=AfKTwljoMOs)
|
|
||||||
|
|
||||||
|
|
||||||
Welcome to General Bot Community Edition
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
General Bot is a strongly typed package based chat bot server focused in convention over configuration and code-less approaches, which brings software packages and application server concepts to help parallel bot development.
|
General Bot is a strongly typed package based chat bot server focused in convention over configuration and code-less approaches, which brings software packages and application server concepts to help parallel bot development.
|
||||||
|
|
||||||
|
|
||||||
## Sample Package #1: [default.gbdialog (VBA)](https://github.com/pragmatismo-io/BotServer/tree/master/packages/default.gbdialog)
|
|
||||||
|
|
||||||
|
|
||||||
* See how easy is to use 'hear' and 'talk' to build Microsoft BOT Framework v4 logic with plain BASIC * published on December 3rd, 2018.
|
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=yX1sF9n9628)
|
|
||||||
|
|
||||||
|
|
||||||
## Sample Package #2: [AzureADPasswordReset.gbapp (TypeScript)](https://github.com/pragmatismo-io/AzureADPasswordReset.gbapp)
|
|
||||||
|
|
||||||
|
|
||||||
Custom dialogs for reseting user password in Azure Active Directory, Office 365, Dynamics 365 or any app published through Azure AD. See also [IntranetBotQuickStart.gbai](https://github.com/pragmatismo-io/IntranetBotQuickStart.gbai) and related MSDN article **[Applied AI – Using a Bot for Password Reset](https://blogs.msdn.microsoft.com/buckwoody/2018/09/25/applied-ai-using-a-bot-for-password-reset)** by *Rodrigo Souza*.
|
|
||||||
|
|
||||||
|
|
||||||
### Bot Administrator - Setup Security
|
|
||||||
|
|
||||||
So the Bot provides an **admin** mode allowing the user having the Directory.AccessAsUser.All permission to be logged on Administrative interface to obtain and save its token into the database.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Bot User - Reset Password
|
|
||||||
|
|
||||||
|
|
||||||
With the access token stored in the database, any user can access anonymously the
|
|
||||||
bot and through a combination of e-mail and mobile received code, the user will be able to reset her or his password.
|
|
||||||
|
|
||||||
#### Reset password via Web
|
|
||||||
|
|
||||||
Any user can use a web address to talk to a reset password bot. Just provide credentials that are confronted with Microsoft Graph to ensure security.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
general-bots-reset-ad-password.gif
|
|
||||||
|
|
||||||
#### Reset password via Skype
|
|
||||||
|
|
||||||
This is the case when user does not have the password to login on Skype for Business or Teams, and they need to reset their password, so the right tool is Skype.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## What is a Bot Server?
|
## What is a Bot Server?
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Bot Server accelerates the process of developing a bot. It provisions all code
|
Bot Server accelerates the process of developing a bot. It provisions all code
|
||||||
base, resources and deployment to the cloud, and gives you templates you can
|
base, resources and deployment to the cloud, and gives you templates you can
|
||||||
choose from whenever you need a new bot. The server has a database and service
|
choose from whenever you need a new bot. The server has a database and service
|
||||||
|
@ -78,191 +27,16 @@ no code. The Bot Server also provides a framework to develop bot packages in a m
|
||||||
advanced fashion writing custom code in editors like Visual Studio Code, Atom or Brackets.
|
advanced fashion writing custom code in editors like Visual Studio Code, Atom or Brackets.
|
||||||
|
|
||||||
Everyone can create bots by just copying and pasting some files and using their
|
Everyone can create bots by just copying and pasting some files and using their
|
||||||
favorite tools like Excel (or any text editor) or Photoshop (or any image
|
favorite tools from Office (or any text editor) or Photoshop (or any image
|
||||||
editor).
|
editor). BASIC can be used to build custom dialogs so Bot can be extended just like VBA for Excel (currently in alpha).
|
||||||
|
|
||||||
Package Quick Reference
|
## Samples
|
||||||
------------
|
|
||||||
|Whatsapp|Web|Core|KB|
|
|
||||||
|----|-----|----|----|
|
|
||||||
|[whatsapp.gblib](https://github.com/pragmatismo-io/BotServer/tree/master/packages/whatsapp.gblib)|[default.gbui](https://github.com/pragmatismo-io/BotServer/tree/master/packages/default.gbui)|[core.gbapp](https://github.com/pragmatismo-io/BotServer/tree/master/packages/core.gbapp)|[kb.gbapp](https://github.com/pragmatismo-io/BotServer/tree/master/packages/kb.gbapp)|
|
|
||||||
|
|
||||||
### The bot development stack
|
[General Bots Samples Repository](https://github.com/GeneralBots-Samples).
|
||||||
|
|
||||||

|
## Guide
|
||||||
|
|
||||||
### The Bot Factory
|
[Read the General Bots Guide](https://github.com/GeneralBots/BotBook/tree/master/book).
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
GeneralBots aims to delivery bots in azure in a very easy and fast fashion. Use Office tools like Word or Excel to edit your Bot - using code (JavaScript or TypeScript) just to empower custom requirements.
|
|
||||||
|
|
||||||
|
|
||||||
#### Use Excel for (Hierarchical) Knowledge Base Editing
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Use Visual Studio for a complete .gbai package building system
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
How To
|
|
||||||
------
|
|
||||||
|
|
||||||
### Run the server locally
|
|
||||||
|
|
||||||
1. Install [Node.js](https://www.npmjs.com/get-npm) the current generation General Bot code execution platform;
|
|
||||||
2. Open a **Terminal** on Linux and Mac or a **Command Prompt** window on Windows;
|
|
||||||
3. Type `npm install -g botserver` and press *ENTER*;
|
|
||||||
4. Type `gbot` to run the server core.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
* [*nodejs.install* Chocolatey Package](https://chocolatey.org/packages/nodejs.install) is also available.
|
|
||||||
* The zip source code of General Bot is also available for [Download](https://codeload.github.com/pragmatismo-io/BotServer/zip/master);
|
|
||||||
|
|
||||||
### Configure the server to deploy specific directory
|
|
||||||
|
|
||||||
1. Create/Edit the .env file and add the ADDITIONAL_DEPLOY_PATH key pointing to the .gbai local parent folder of .gbapp, .gbot, .gbtheme, .gbkb package directories.
|
|
||||||
2. Specify STORAGE_SYNC to TRUE so database sync is run when the server is run.
|
|
||||||
3. In case of Microsoft SQL Server add the following keys: STORAGE_SERVER, STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT to `mssql`.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
|
|
||||||
* You can specify several bots separated by semicolon, the BotServer will serve all of them at once.
|
|
||||||
|
|
||||||
## Setup development environment (Windows)
|
|
||||||
|
|
||||||
1. [Optional] Install [Chocolatey](https://chocolatey.org/install), a Windows Package Manager;
|
|
||||||
2. Install [git](`https://git-scm.com/`), a Software Configuration Management (SCM).;
|
|
||||||
3. Install [Node.js](npmjs.com/get-npm), a [Runtime system](https://en.wikipedia.org/wiki/Runtime_system).
|
|
||||||
(https://www.npmjs.com/get-npm) (suggested: LTS 8.x.x);
|
|
||||||
4. Install npm install -g npm-check-updates;
|
|
||||||
5. Install [Visual Studio Code](https://chocolatey.org/packages/nodejs.install), Brackets or Atom as an editor of your choice;
|
|
||||||
6. [Fork](https://en.wikipedia.org/wiki/Fork_(software_development)) by visiting https://github.com/pragmatismo-io/BotServer/fork
|
|
||||||
7. Clone the just forked repository by running `git clone <your-forked-repository-url>/BotServer.git` ;
|
|
||||||
8. Run `npm install -g typescript`;
|
|
||||||
9. Compile the bot server by `tsc`.
|
|
||||||
10. Run the bot server by `npm start`.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
|
|
||||||
* Whenever you are ready to turn your open-source bot ideas in form of .gbapp (source-code) and artifacts like .gbkb, .gbtheme, .gbot or the .gbai full package read [CONTRIBUTING.md](https://github.com/pragmatismo-io/BotServer/blob/master/CONTRIBUTING.md) about performing Pull Requests (PR) and creating other public custom packages repositories of your own personal or organization General Bot Community Edition powered packages.
|
|
||||||
|
|
||||||
### Running unit tests
|
|
||||||
|
|
||||||
1. Enter the BotServer root folder.
|
|
||||||
2. Run tests by `npm test`.
|
|
||||||
|
|
||||||
### Just copy the source code to your machine
|
|
||||||
|
|
||||||
1. [Download] the Zip file of (https://codeload.github.com/pragmatismo-io/BotServer/zip/master)
|
|
||||||
|
|
||||||
### Updating the Bot Knoledge Base (.gbkb folder)
|
|
||||||
|
|
||||||
The subjects.json file contains all information related to the subject tree and can be used to build the menu carrousel as well give a set of words to be used as subject catcher in the conversation. A hierarchy can be specified.
|
|
||||||
|
|
||||||
### Creating a new Theme folder (.gbtheme folder)
|
|
||||||
|
|
||||||
A theme is composed of some CSS files and images. That set of files can change
|
|
||||||
everything in the General Bot UI. Use them extensively before going to change
|
|
||||||
the UI application itself (HTML & JS).
|
|
||||||
|
|
||||||
Package Types
|
|
||||||
-------------
|
|
||||||
|
|
||||||
### .gbai
|
|
||||||
|
|
||||||
Embraces all packages types (content, logic & conversation) into a pluggable bot
|
|
||||||
directory. [A sample .gbai is available](https://github.com/pragmatismo-io/IntranetBotQuickStart.gbai).
|
|
||||||
|
|
||||||
### .gbapp
|
|
||||||
|
|
||||||
The artificial intelligence extensions in form of pluggable apps. Dialogs,
|
|
||||||
Services and all model related to data. A set of interactions, use cases,
|
|
||||||
integrations in form of conversationals dialogs.
|
|
||||||
The .gbapp adds the General Bot base library (botlib) for building Node.js TypeScript Apps packages.
|
|
||||||
|
|
||||||
|
|
||||||
Four components builds up a General Bot App:
|
|
||||||
|
|
||||||
* dialogs
|
|
||||||
* models
|
|
||||||
* services
|
|
||||||
* tests
|
|
||||||
|
|
||||||
#### Dialogs
|
|
||||||
|
|
||||||
All code contained in a dialog builds the flow to custom conversations in
|
|
||||||
built-in and additional packages .
|
|
||||||
|
|
||||||
|
|
||||||
#### Models
|
|
||||||
|
|
||||||
Models builds the foundation of data relationships in form of entities.
|
|
||||||
|
|
||||||
|
|
||||||
#### Services
|
|
||||||
|
|
||||||
Services are a façade for bot back-end logic and other custom processing.
|
|
||||||
|
|
||||||
#### Tests
|
|
||||||
|
|
||||||
Tests try to automate code execution validation before crashing in production.
|
|
||||||
|
|
||||||
|
|
||||||
### .gbot
|
|
||||||
|
|
||||||
An expression of an artificial inteligence entity. A .gbot file defines
|
|
||||||
all bots dependencies related to services and other resources.
|
|
||||||
|
|
||||||
### .gbtheme
|
|
||||||
|
|
||||||
A theme of a bot at a given time. CSS files & images that can compose all UI
|
|
||||||
presentation and using it a branding can be done. [A sample .gbtheme is available](https://github.com/pragmatismo-io/Office365.gbtheme)
|
|
||||||
|
|
||||||
### .gbkb
|
|
||||||
|
|
||||||
A set of subjects that bot knows in a form of hierarchical menu-based QnA. [A sample .gbkb is available](https://github.com/pragmatismo-io/ProjectOnline.gbkb).
|
|
||||||
|
|
||||||
### .gblib
|
|
||||||
|
|
||||||
Shared code that can be used across bot apps.
|
|
||||||
|
|
||||||
Reference
|
|
||||||
---------
|
|
||||||
|
|
||||||
### GeneralBots admin commands
|
|
||||||
|
|
||||||
General Bot can be controlled by the same chat window people talk to, so
|
|
||||||
here is a list of admin commands related to deploying .gb* files.
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
|-----------------|-----------------------------------------------------------------------------------------------------------------|
|
|
||||||
| deployPackage | Deploy a KB package. Usage **deployPackage** [package-name]. Then, you need to run rebuildIndex. |
|
|
||||||
| undeployPackage | Undeploy a KB. Usage **undeployPackage** [package-name]. |
|
|
||||||
| redeployPackage | Undeploy and then deploys the KB. Usage **redeployPackage** [package-name]. Then, you need to run rebuildIndex. |
|
|
||||||
| setupSecurity | Setup connection to user directories. |
|
|
||||||
|
|
||||||
Discontinued commands:
|
|
||||||
|
|
||||||
| Command | Description |Reason |
|
|
||||||
|-----------------| -----------------------------------------------------------------------------------------------------------------|------|
|
|
||||||
| rebuildIndex | Rebuild Azure Search indexes, must be run after **deployPackage** or **redeployPackage**. | Now it is called automatically |
|
|
||||||
|
|
||||||
### Credits & Inspiration
|
|
||||||
|
|
||||||
* Rodrigo Rodriguez (me@rodrigorodriguez.com) - Coding, Docs & Architecture.
|
|
||||||
* David Lerner (david.lerner@hotmail.com) - UI, UX & Theming.
|
|
||||||
* Eduardo Romeiro (eromeirosp@outlook.com) - Content & UX.
|
|
||||||
* Jorge Ramos (jramos@pobox.com) - Coding, Docs & Architecture.
|
|
||||||
* PH Nascimento (ph.an@outlook.com) - Product Manager
|
|
||||||
|
|
||||||
Powered by Microsoft [BOT Framework](https://dev.botframework.com/) and [Azure](http://www.azure.com).
|
|
||||||
|
|
||||||
General Bot Code Name is [Guaribas](https://en.wikipedia.org/wiki/Guaribas), the name of a city in Brasil, state of Piaui.
|
|
||||||
[Roberto Mangabeira Unger](http://www.robertounger.com/en/): "No one should have to do work that can be done by a machine".
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -300,5 +74,21 @@ trademark license. Therefore any rights, title and interest in
|
||||||
our trademarks remain entirely with us.
|
our trademarks remain entirely with us.
|
||||||
|
|
||||||
|
|
||||||
<a href="https://stackoverflow.com/questions/ask?tags=generalbots">:speech_balloon: Ask a question</a> <a href="/docs">:book: Read the Docs</a>
|
<a href="https://stackoverflow.com/questions/ask?tags=generalbots">:speech_balloon: Ask a question</a> <a href="https://github.com/GeneralBots/BotBook/blob/master/01%20-%20Overview.md">:book: Read the Docs</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
# Videos
|
||||||
|
|
||||||
|
## Easeness authoring of bot packages, development environment and self-deployment.
|
||||||
|
|
||||||
|
* Now with the General Bots server you can press F5 on Visual Studio to get a bot factory on your environment* published on November 10th, 2018.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=AfKTwljoMOs)
|
||||||
|
|
||||||
|
## Using TALK and HEAR to build Virtual Assistants with BASIC
|
||||||
|
|
||||||
|
* See how easy is to use 'hear' and 'talk' to build Microsoft BOT Framework v4 logic with plain BASIC * published on December 3rd, 2018.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=yX1sF9n9628)
|
||||||
|
|
||||||
|
|
||||||
|
|
6
ROADMAP.md
Normal file
6
ROADMAP.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
| Title | Priority | Release | Status |
|
||||||
|
|-------------------------------|------------------------------------------------------------------------------------------------------------|---------|--------|
|
||||||
|
| Isolation of .gbapp per .gbot | Today .gbapp loaded is shared across all bot instances and must be associated to one or more individually. | Medium | 2019Q4 |
|
||||||
|
| Python based .gbapps | Write conversational login in Python | Low | - |
|
15
gbot.cmd
15
gbot.cmd
|
@ -1,2 +1,15 @@
|
||||||
@echo off
|
@ECHO off
|
||||||
|
|
||||||
|
ECHO General Bots Command Line
|
||||||
|
|
||||||
|
IF EXIST node_modules goto COMPILE
|
||||||
|
ECHO Installing Packages for the first time use...
|
||||||
|
CALL npm install --silent
|
||||||
|
|
||||||
|
:COMPILE
|
||||||
|
IF EXIST dist goto ALLSET
|
||||||
|
ECHO Compiling...
|
||||||
|
CALL tsc
|
||||||
|
|
||||||
|
:ALLSET
|
||||||
node dist/src/app.js
|
node dist/src/app.js
|
||||||
|
|
3066
package-lock.json
generated
3066
package-lock.json
generated
File diff suppressed because it is too large
Load diff
80
package.json
80
package.json
|
@ -23,7 +23,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
|
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
|
||||||
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose",
|
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose -e ./packages/default.gbui/**/* -e ./packages/**/*.gbdialog/**/*",
|
||||||
"build": "npm install && npm run build-server && npm run build-gbui && npm run build-docs",
|
"build": "npm install && npm run build-server && npm run build-gbui && npm run build-docs",
|
||||||
"build-server": "tsc",
|
"build-server": "tsc",
|
||||||
"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",
|
||||||
|
@ -38,7 +38,6 @@
|
||||||
"ban": "ban",
|
"ban": "ban",
|
||||||
"issues": "git-issues",
|
"issues": "git-issues",
|
||||||
"license": "license-checker --production --onlyunknown --csv",
|
"license": "license-checker --production --onlyunknown --csv",
|
||||||
"pretslint": "npm run pretty",
|
|
||||||
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
|
"pretty": "prettier-standard 'src/*.ts' 'packages/**/*.ts'",
|
||||||
"secure": "nsp check",
|
"secure": "nsp check",
|
||||||
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";",
|
||||||
|
@ -48,65 +47,68 @@
|
||||||
"commit": "git-cz"
|
"commit": "git-cz"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/microsoft-graph-client": "1.4.0",
|
"@microsoft/microsoft-graph-client": "1.5.2",
|
||||||
"@semantic-release/exec": "^3.3.2",
|
"@semantic-release/exec": "^3.3.2",
|
||||||
"adal-node": "0.1.28",
|
"adal-node": "0.1.28",
|
||||||
"async": "2.6.1",
|
"async": "2.6.2",
|
||||||
"async-promises": "0.2.1",
|
"async-promises": "0.2.1",
|
||||||
"azure-arm-cognitiveservices": "2.4.1",
|
"azure-arm-cognitiveservices": "2.4.1",
|
||||||
"azure-arm-resource": "7.3.0",
|
"azure-arm-resource": "7.3.0",
|
||||||
"azure-arm-search": "^1.3.0-preview",
|
"azure-arm-search": "^1.3.0-preview",
|
||||||
"azure-arm-sql": "5.6.0",
|
"azure-arm-sql": "5.6.0",
|
||||||
"azure-arm-website": "5.7.0",
|
"azure-arm-website": "5.7.0",
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.4",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"botbuilder": "^4.1.7",
|
"botbuilder": "4.3.4",
|
||||||
"botbuilder-ai": "^4.2.0",
|
"botbuilder-ai": "4.3.4",
|
||||||
"botbuilder-azure": "^4.2.0",
|
"botbuilder-azure": "4.3.4",
|
||||||
"botbuilder-choices": "^4.0.0-preview1.2",
|
"botbuilder-choices": "4.0.0-preview1.2",
|
||||||
"botbuilder-dialogs": "^4.2.0",
|
"botbuilder-dialogs": "4.3.4",
|
||||||
"botbuilder-prompts": "^4.0.0-preview1.2",
|
"botbuilder-prompts": "4.0.0-preview1.2",
|
||||||
"botlib": "^0.1.10",
|
"botlib": "0.1.24",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
"chokidar": "2.0.4",
|
"chokidar": "2.1.5",
|
||||||
"cli-spinner": "^0.2.8",
|
"cli-spinner": "^0.2.10",
|
||||||
"csv-parse": "4.3.1",
|
"csv-parse": "4.3.4",
|
||||||
"dotenv-extended": "2.3.0",
|
"dotenv-extended": "2.4.0",
|
||||||
"express": "4.16.4",
|
"express": "4.16.4",
|
||||||
"express-promise-router": "3.0.3",
|
"express-promise-router": "3.0.3",
|
||||||
"fs-extra": "7.0.1",
|
"fs-extra": "7.0.1",
|
||||||
"fs-walk": "0.0.2",
|
|
||||||
"ip": "^1.1.5",
|
"ip": "^1.1.5",
|
||||||
|
"js-beautify": "^1.9.1",
|
||||||
"localize": "0.4.7",
|
"localize": "0.4.7",
|
||||||
"marked": "0.6.0",
|
"marked": "0.6.2",
|
||||||
"mocha": "5.2.0",
|
"mocha": "6.0.2",
|
||||||
"mocha-typescript": "1.1.17",
|
"mocha-typescript": "1.1.17",
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.1",
|
||||||
"ms-rest-azure": "2.6.0",
|
"ms-rest-azure": "2.6.0",
|
||||||
|
"ms-rest-js": "^1.0.1",
|
||||||
"nexmo": "2.4.1",
|
"nexmo": "2.4.1",
|
||||||
"ngrok": "^3.1.0",
|
"ngrok": "3.1.1",
|
||||||
"nyc": "^13.1.0",
|
"nyc": "13.3.0",
|
||||||
"opn": "^5.4.0",
|
"opn": "6.0.0",
|
||||||
"pragmatismo-io-framework": "1.0.19",
|
"pragmatismo-io-framework": "1.0.19",
|
||||||
"process-exists": "^3.1.0",
|
"process-exists": "3.1.0",
|
||||||
"public-ip": "^3.0.0",
|
"public-ip": "^3.0.0",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"request-promise-native": "1.0.5",
|
"request-promise": "4.2.4",
|
||||||
|
"request-promise-native": "1.0.7",
|
||||||
"scanf": "^1.0.2",
|
"scanf": "^1.0.2",
|
||||||
"sequelize": "4.42.0",
|
"sequelize": "^5.2.12",
|
||||||
"sequelize-typescript": "0.6.7",
|
"sequelize-typescript": "0.6.9",
|
||||||
"shx": "^0.3.2",
|
"shx": "0.3.2",
|
||||||
"simple-git": "^1.107.0",
|
"simple-git": "1.110.0",
|
||||||
"sqlite3": "4.0.6",
|
"sqlite3": "4.0.6",
|
||||||
"strict-password-generator": "^1.1.2",
|
"strict-password-generator": "^1.1.2",
|
||||||
"swagger-client": "3.8.22",
|
"swagger-client": "3.8.25",
|
||||||
"tedious": "4.1.3",
|
"tedious": "6.1.0",
|
||||||
"ts-node": "8.0.2",
|
"temperature-js": "^0.1.0",
|
||||||
|
"ts-node": "8.0.3",
|
||||||
"typedoc": "0.14.2",
|
"typedoc": "0.14.2",
|
||||||
"typedoc-plugin-external-module-name": "^2.0.0",
|
"typedoc-plugin-external-module-name": "^2.0.0",
|
||||||
"typedoc-plugin-markdown": "^1.1.25",
|
"typedoc-plugin-markdown": "^1.1.27",
|
||||||
"typescript": "3.3.1",
|
"typescript": "3.4.1",
|
||||||
"url-join": "4.0.0",
|
"url-join": "4.0.0",
|
||||||
"vbscript-to-typescript": "^1.0.8",
|
"vbscript-to-typescript": "^1.0.8",
|
||||||
"wait-until": "0.0.2",
|
"wait-until": "0.0.2",
|
||||||
|
@ -121,13 +123,13 @@
|
||||||
"@semantic-release/npm": "^5.1.4",
|
"@semantic-release/npm": "^5.1.4",
|
||||||
"@semantic-release/release-notes-generator": "^7.1.4",
|
"@semantic-release/release-notes-generator": "^7.1.4",
|
||||||
"@types/chai": "4.1.7",
|
"@types/chai": "4.1.7",
|
||||||
"@types/mocha": "5.2.5",
|
"@types/mocha": "5.2.6",
|
||||||
"@types/sequelize": "4.27.34",
|
"@types/sequelize": "4.27.46",
|
||||||
"@types/url-join": "4.0.0",
|
"@types/url-join": "4.0.0",
|
||||||
"@types/winston": "2.4.4",
|
"@types/winston": "2.4.4",
|
||||||
"ban-sensitive-files": "1.9.2",
|
"ban-sensitive-files": "1.9.2",
|
||||||
"commitizen": "^3.0.5",
|
"commitizen": "^3.0.7",
|
||||||
"coveralls": "^3.0.2",
|
"coveralls": "^3.0.3",
|
||||||
"cz-conventional-changelog": "^2.1.0",
|
"cz-conventional-changelog": "^2.1.0",
|
||||||
"dependency-check": "3.3.0",
|
"dependency-check": "3.3.0",
|
||||||
"deps-ok": "1.4.1",
|
"deps-ok": "1.4.1",
|
||||||
|
@ -140,8 +142,8 @@
|
||||||
"standard": "12.0.1",
|
"standard": "12.0.1",
|
||||||
"travis-deploy-once": "5.0.11",
|
"travis-deploy-once": "5.0.11",
|
||||||
"ts-loader": "^5.3.3",
|
"ts-loader": "^5.3.3",
|
||||||
"tslint": "^5.12.1",
|
"tslint": "^5.15.0",
|
||||||
"tslint-microsoft-contrib": "^6.0.0"
|
"tslint-microsoft-contrib": "^6.1.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"env": {
|
"env": {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,28 +36,24 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { WaterfallDialog, WaterfallStep, WaterfallStepContext } from 'botbuilder-dialogs';
|
import urlJoin = require('url-join');
|
||||||
import { GBMinInstance } from 'botlib';
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
|
||||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||||
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
|
import { GBImporter } from '../../core.gbapp/services/GBImporterService';
|
||||||
import { GBAdminService } from '../services/GBAdminService';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialogs for administration tasks.
|
* Dialogs for administration tasks.
|
||||||
*/
|
*/
|
||||||
export class AdminDialog extends IGBDialog {
|
export class AdminDialog extends IGBDialog {
|
||||||
public static async createFarmCommand(text: any, min: GBMinInstance) {}
|
public static async undeployPamand(text: any, min: GBMinInstance) {
|
||||||
|
|
||||||
public static async undeployPackageCommand(text: any, min: GBMinInstance) {
|
|
||||||
const packageName = text.split(' ')[1];
|
const packageName = text.split(' ')[1];
|
||||||
const importer = new GBImporter(min.core);
|
const importer = new GBImporter(min.core);
|
||||||
const deployer = new GBDeployer(min.core, importer);
|
const deployer = new GBDeployer(min.core, importer);
|
||||||
await deployer.undeployPackageFromLocalPath(min.instance, UrlJoin('packages', packageName));
|
await deployer.undeployPackageFromLocalPath(min.instance, urlJoin('packages', packageName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isSharePointPath(path: string) {
|
public static isSharePointPath(path: string) {
|
||||||
|
@ -67,26 +63,20 @@ export class AdminDialog extends IGBDialog {
|
||||||
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
||||||
const packageName = text.split(' ')[1];
|
const packageName = text.split(' ')[1];
|
||||||
|
|
||||||
if (AdminDialog.isSharePointPath(packageName)) {
|
if (!AdminDialog.isSharePointPath(packageName)) {
|
||||||
await deployer.deployFromSharePoint(min.instance.instanceId, packageName);
|
|
||||||
} else {
|
|
||||||
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
||||||
if (!additionalPath) {
|
if (additionalPath === undefined) {
|
||||||
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
|
throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
|
||||||
}
|
}
|
||||||
await deployer.deployPackageFromLocalPath(min, UrlJoin(additionalPath, packageName));
|
await deployer.deployPackage(min, urlJoin(additionalPath, packageName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async rebuildIndexPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
|
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
await deployer.rebuildIndex(min.instance);
|
await deployer.rebuildIndex(
|
||||||
}
|
min.instance,
|
||||||
|
new AzureDeployerService(deployer).getKBSearchSchema(min.instance.searchIndex)
|
||||||
public static async addConnectionCommand(min: GBMinInstance, text: any) {
|
);
|
||||||
const packageName = text.split(' ')[1];
|
|
||||||
const importer = new GBImporter(min.core);
|
|
||||||
const admin = new GBAdminService(min.core);
|
|
||||||
// TODO: await admin.addConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +85,7 @@ export class AdminDialog extends IGBDialog {
|
||||||
* @param bot The bot adapter.
|
* @param bot The bot adapter.
|
||||||
* @param min The minimal bot instance data.
|
* @param min The minimal bot instance data.
|
||||||
*/
|
*/
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
public static setup(min: GBMinInstance) {
|
||||||
// Setup services.
|
// Setup services.
|
||||||
|
|
||||||
const importer = new GBImporter(min.core);
|
const importer = new GBImporter(min.core);
|
||||||
|
@ -113,9 +103,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const password = step.result;
|
const sensitive = step.result;
|
||||||
|
|
||||||
if (password === GBConfigService.get('ADMIN_PASS')) {
|
if (sensitive === GBConfigService.get('ADMIN_PASS')) {
|
||||||
await step.context.sendActivity(Messages[locale].welcome);
|
await step.context.sendActivity(Messages[locale].welcome);
|
||||||
|
|
||||||
return await step.prompt('textPrompt', Messages[locale].which_task);
|
return await step.prompt('textPrompt', Messages[locale].which_task);
|
||||||
|
@ -126,8 +116,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale: string = step.context.activity.locale;
|
||||||
const text = step.result;
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const text: string = step.result;
|
||||||
const cmdName = text.split(' ')[0];
|
const cmdName = text.split(' ')[0];
|
||||||
|
|
||||||
step.context.sendActivity(Messages[locale].working(cmdName));
|
step.context.sendActivity(Messages[locale].working(cmdName));
|
||||||
|
@ -135,28 +126,16 @@ export class AdminDialog extends IGBDialog {
|
||||||
|
|
||||||
if (text === 'quit') {
|
if (text === 'quit') {
|
||||||
return await step.replaceDialog('/');
|
return await step.replaceDialog('/');
|
||||||
} else if (cmdName === 'createFarm') {
|
|
||||||
await AdminDialog.createFarmCommand(text, deployer);
|
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
|
||||||
} else if (cmdName === 'deployPackage') {
|
} else if (cmdName === 'deployPackage') {
|
||||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'redeployPackage') {
|
} else if (cmdName === 'redeployPackage') {
|
||||||
await AdminDialog.undeployPackageCommand(text, min);
|
|
||||||
await AdminDialog.deployPackageCommand(min, text, deployer);
|
await AdminDialog.deployPackageCommand(min, text, deployer);
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'rebuildIndex') {
|
} else if (cmdName === 'rebuildIndex') {
|
||||||
await AdminDialog.rebuildIndexPackageCommand(min, text, deployer);
|
await AdminDialog.rebuildIndexPackageCommand(min, deployer);
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
|
||||||
} else if (cmdName === 'addConnection') {
|
|
||||||
await AdminDialog.addConnectionCommand(min, text);
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
|
||||||
} else if (cmdName === 'undeployPackage') {
|
|
||||||
await AdminDialog.undeployPackageCommand(text, min);
|
|
||||||
|
|
||||||
return await step.replaceDialog('/admin', { firstRun: false });
|
return await step.replaceDialog('/admin', { firstRun: false });
|
||||||
} else if (cmdName === 'setupSecurity') {
|
} else if (cmdName === 'setupSecurity') {
|
||||||
|
@ -178,7 +157,7 @@ export class AdminDialog extends IGBDialog {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static setupSecurityDialogs(min: any) {
|
private static setupSecurityDialogs(min: GBMinInstance) {
|
||||||
min.dialogs.add(
|
min.dialogs.add(
|
||||||
new WaterfallDialog('/setupSecurity', [
|
new WaterfallDialog('/setupSecurity', [
|
||||||
async step => {
|
async step => {
|
||||||
|
@ -220,13 +199,13 @@ export class AdminDialog extends IGBDialog {
|
||||||
);
|
);
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const state = `${min.instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
|
const state = `${min.instance.instanceId}${crypto.getRandomValues(new Uint32Array(16))[0]}`;
|
||||||
|
|
||||||
await min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
|
||||||
|
|
||||||
const url = `https://login.microsoftonline.com/${min.instance.authenticatorTenant}/oauth2/authorize?client_id=${
|
const url = `https://login.microsoftonline.com/${
|
||||||
min.instance.authenticatorClientId
|
min.instance.authenticatorTenant
|
||||||
}&response_type=code&redirect_uri=${UrlJoin(
|
}/oauth2/authorize?client_id=${min.instance.authenticatorClientId}&response_type=code&redirect_uri=${urlJoin(
|
||||||
min.instance.botEndpoint,
|
min.instance.botEndpoint,
|
||||||
min.instance.botId,
|
min.instance.botId,
|
||||||
'/token'
|
'/token'
|
||||||
|
@ -234,10 +213,9 @@ export class AdminDialog extends IGBDialog {
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].consent(url));
|
await step.context.sendActivity(Messages[locale].consent(url));
|
||||||
|
|
||||||
return await step.replaceDialog('/ask', {isReturning: true});
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,21 +36,35 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import urlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { 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';
|
||||||
import { GuaribasAdmin } from './models/AdminModel';
|
import { GuaribasAdmin } from './models/AdminModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The package for admin.gbapp.
|
||||||
|
*/
|
||||||
export class GBAdminPackage implements IGBPackage {
|
export class GBAdminPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
core.sequelize.addModels([GuaribasAdmin]);
|
core.sequelize.addModels([GuaribasAdmin]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
AdminDialog.setup(min.bot, min);
|
AdminDialog.setup(min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -39,12 +39,15 @@
|
||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
CreatedAt,
|
||||||
|
DataType,
|
||||||
Model,
|
Model,
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt,
|
UpdatedAt
|
||||||
DataType
|
|
||||||
} from 'sequelize-typescript';
|
} from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General settings store.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasAdmin extends Model<GuaribasAdmin> {
|
export class GuaribasAdmin extends Model<GuaribasAdmin> {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,17 +37,17 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { AuthenticationContext, TokenResponse } from 'adal-node';
|
import { AuthenticationContext, TokenResponse } from 'adal-node';
|
||||||
import { IGBCoreService } from 'botlib';
|
import { IGBAdminService, IGBCoreService, IGBInstance } from 'botlib';
|
||||||
|
import urlJoin = require('url-join');
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
import { GuaribasAdmin } from '../models/AdminModel';
|
import { GuaribasAdmin } from '../models/AdminModel';
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const msRestAzure = require('ms-rest-azure');
|
const msRestAzure = require('ms-rest-azure');
|
||||||
const PasswordGenerator = require('strict-password-generator').default;
|
const PasswordGenerator = require('strict-password-generator').default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services for server administration.
|
* Services for server administration.
|
||||||
*/
|
*/
|
||||||
export class GBAdminService {
|
export class GBAdminService implements IGBAdminService {
|
||||||
public static GB_PROMPT: string = 'GeneralBots: ';
|
public static GB_PROMPT: string = 'GeneralBots: ';
|
||||||
public static masterBotInstanceId = 0;
|
public static masterBotInstanceId = 0;
|
||||||
|
|
||||||
|
@ -65,18 +65,15 @@ export class GBAdminService {
|
||||||
|
|
||||||
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);
|
||||||
const accessToken = credentials.tokenCache._entries[0].accessToken;
|
|
||||||
|
|
||||||
return accessToken;
|
return credentials.tokenCache._entries[0].accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getADALCredentialsFromUsername(username: string, password: string) {
|
public static async getADALCredentialsFromUsername(username: string, password: string) {
|
||||||
const credentials = await msRestAzure.loginWithUsernamePassword(username, password);
|
return await msRestAzure.loginWithUsernamePassword(username, password);
|
||||||
|
|
||||||
return credentials;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getRndPassword() {
|
public static getRndPassword(): string {
|
||||||
const passwordGenerator = new PasswordGenerator();
|
const passwordGenerator = new PasswordGenerator();
|
||||||
const options = {
|
const options = {
|
||||||
upperCaseAlpha: true,
|
upperCaseAlpha: true,
|
||||||
|
@ -88,6 +85,7 @@ export class GBAdminService {
|
||||||
};
|
};
|
||||||
let password = passwordGenerator.generatePassword(options);
|
let password = passwordGenerator.generatePassword(options);
|
||||||
password = password.replace(/[\@\[\=\:\;\?]/g, '#');
|
password = password.replace(/[\@\[\=\:\;\?]/g, '#');
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,11 +99,11 @@ export class GBAdminService {
|
||||||
minimumLength: 12,
|
minimumLength: 12,
|
||||||
maximumLength: 14
|
maximumLength: 14
|
||||||
};
|
};
|
||||||
const name = passwordGenerator.generatePassword(options);
|
|
||||||
return name;
|
return passwordGenerator.generatePassword(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setValue(instanceId: number, key: string, value: string): Promise<GuaribasAdmin> {
|
public async setValue(instanceId: number, key: string, value: string) {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { key: key };
|
options.where = { key: key };
|
||||||
let admin = await GuaribasAdmin.findOne(options);
|
let admin = await GuaribasAdmin.findOne(options);
|
||||||
|
@ -115,8 +113,7 @@ export class GBAdminService {
|
||||||
}
|
}
|
||||||
admin.value = value;
|
admin.value = value;
|
||||||
admin.instanceId = instanceId;
|
admin.instanceId = instanceId;
|
||||||
|
await admin.save();
|
||||||
return admin.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateSecurityInfo(
|
public async updateSecurityInfo(
|
||||||
|
@ -125,7 +122,7 @@ export class GBAdminService {
|
||||||
authenticatorAuthorityHostUrl: string,
|
authenticatorAuthorityHostUrl: string,
|
||||||
authenticatorClientId: string,
|
authenticatorClientId: string,
|
||||||
authenticatorClientSecret: string
|
authenticatorClientSecret: string
|
||||||
): Promise<GuaribasInstance> {
|
): Promise<IGBInstance> {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { instanceId: instanceId };
|
options.where = { instanceId: instanceId };
|
||||||
const item = await GuaribasInstance.findOne(options);
|
const item = await GuaribasInstance.findOne(options);
|
||||||
|
@ -137,7 +134,7 @@ export class GBAdminService {
|
||||||
return item.save();
|
return item.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getValue(instanceId: number, key: string) {
|
public async getValue(instanceId: number, key: string): Promise<string> {
|
||||||
const options = { where: {} };
|
const options = { where: {} };
|
||||||
options.where = { key: key, instanceId: instanceId };
|
options.where = { key: key, instanceId: instanceId };
|
||||||
const obj = await GuaribasAdmin.findOne(options);
|
const obj = await GuaribasAdmin.findOne(options);
|
||||||
|
@ -154,7 +151,7 @@ export class GBAdminService {
|
||||||
const accessToken = await this.getValue(instanceId, 'accessToken');
|
const accessToken = await this.getValue(instanceId, 'accessToken');
|
||||||
resolve(accessToken);
|
resolve(accessToken);
|
||||||
} else {
|
} else {
|
||||||
const authorizationUrl = UrlJoin(
|
const authorizationUrl = urlJoin(
|
||||||
instance.authenticatorAuthorityHostUrl,
|
instance.authenticatorAuthorityHostUrl,
|
||||||
instance.authenticatorTenant,
|
instance.authenticatorTenant,
|
||||||
'/oauth2/authorize'
|
'/oauth2/authorize'
|
||||||
|
@ -169,7 +166,7 @@ export class GBAdminService {
|
||||||
instance.authenticatorClientSecret,
|
instance.authenticatorClientSecret,
|
||||||
resource,
|
resource,
|
||||||
async (err, res) => {
|
async (err, res) => {
|
||||||
if (err) {
|
if (err !== undefined) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
const token = res as TokenResponse;
|
const token = res as TokenResponse;
|
||||||
|
|
|
@ -16,7 +16,8 @@ export const Messages = {
|
||||||
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): ',
|
||||||
enter_authenticator_client_id: 'Enter the Client Id [Application Id](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview) GUID:',
|
enter_authenticator_client_id: `Enter the Client Id GUID: Get from
|
||||||
|
[this url](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview)`,
|
||||||
enter_authenticator_client_secret: 'Enter the Client Secret:'
|
enter_authenticator_client_secret: 'Enter the Client Secret:'
|
||||||
},
|
},
|
||||||
'pt-BR': {
|
'pt-BR': {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,18 +36,30 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .gblib Package handler.
|
||||||
|
*/
|
||||||
export class GBAnalyticsPackage implements IGBPackage {
|
export class GBAnalyticsPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public unloadPackage(core: IGBCoreService): void {}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {}
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public unloadBot(min: GBMinInstance): void {}
|
GBLog.verbose(`loadPackage called.`);
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
}
|
||||||
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,12 +36,6 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
|
@ -65,6 +59,56 @@ import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBMod
|
||||||
import { GuaribasSubject } from '../../kb.gbapp/models';
|
import { GuaribasSubject } from '../../kb.gbapp/models';
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single message in a conversation.
|
||||||
|
*/
|
||||||
|
@Table
|
||||||
|
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
|
||||||
|
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
public conversationMessageId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasSubject)
|
||||||
|
@Column
|
||||||
|
public subjectId: number;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@CreatedAt
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@UpdatedAt
|
||||||
|
public updatedAt: Date;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
|
@ForeignKey(() => GuaribasConversation)
|
||||||
|
@Column
|
||||||
|
public conversationId: number;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
|
@BelongsTo(() => GuaribasConversation)
|
||||||
|
public conversation: GuaribasConversation;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasInstance)
|
||||||
|
@Column
|
||||||
|
public instanceId: number;
|
||||||
|
|
||||||
|
@ForeignKey(() => GuaribasUser)
|
||||||
|
@Column
|
||||||
|
public userId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => GuaribasUser)
|
||||||
|
public user: GuaribasUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A conversation that groups many messages.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasConversation extends Model<GuaribasConversation> {
|
export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||||
|
|
||||||
|
@ -106,45 +150,3 @@ export class GuaribasConversation extends Model<GuaribasConversation> {
|
||||||
@BelongsTo(() => GuaribasUser)
|
@BelongsTo(() => GuaribasUser)
|
||||||
public startedBy: GuaribasUser;
|
public startedBy: GuaribasUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Table
|
|
||||||
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
@AutoIncrement
|
|
||||||
@Column
|
|
||||||
public conversationMessageId: number;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasSubject)
|
|
||||||
@Column
|
|
||||||
public subjectId: number;
|
|
||||||
|
|
||||||
@Column(DataType.TEXT)
|
|
||||||
public content: string;
|
|
||||||
|
|
||||||
@Column
|
|
||||||
@CreatedAt
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Column
|
|
||||||
@UpdatedAt
|
|
||||||
public updatedAt: Date;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasConversation)
|
|
||||||
@Column
|
|
||||||
public conversationId: number;
|
|
||||||
|
|
||||||
@BelongsTo(() => GuaribasConversation)
|
|
||||||
public conversation: GuaribasConversation;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasInstance)
|
|
||||||
@Column
|
|
||||||
public instanceId: number;
|
|
||||||
|
|
||||||
@ForeignKey(() => GuaribasUser)
|
|
||||||
@Column
|
|
||||||
public userId: number;
|
|
||||||
|
|
||||||
@BelongsTo(() => GuaribasUser)
|
|
||||||
public user: GuaribasUser;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,6 +37,9 @@
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
import { GuaribasConversation, GuaribasConversationMessage } from '../models';
|
import { GuaribasConversation, GuaribasConversationMessage } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base services for Bot Analytics.
|
||||||
|
*/
|
||||||
export class AnalyticsService {
|
export class AnalyticsService {
|
||||||
public async createConversation(
|
public async createConversation(
|
||||||
user: GuaribasUser
|
user: GuaribasUser
|
||||||
|
@ -52,7 +55,7 @@ export class AnalyticsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public createMessage(
|
public async createMessage(
|
||||||
conversation: GuaribasConversation,
|
conversation: GuaribasConversation,
|
||||||
user: GuaribasUser,
|
user: GuaribasUser,
|
||||||
content: string
|
content: string
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview General Bots server core.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
|
||||||
|
|
||||||
export class BotFarmDialog extends IGBDialog {
|
|
||||||
/**
|
|
||||||
* Setup dialogs flows and define services call.
|
|
||||||
*
|
|
||||||
* @param bot The bot adapter.
|
|
||||||
* @param min The minimal bot instance data.
|
|
||||||
*/
|
|
||||||
public static setup(bot: BotAdapter, min: GBMinInstance) {
|
|
||||||
min.dialogs.add('/createBotFarm', [
|
|
||||||
async step => {
|
|
||||||
const locale = step.context.activity.locale;
|
|
||||||
await step.prompt('choicePrompt', Messages[locale].what_about_me, [
|
|
||||||
'1',
|
|
||||||
'2',
|
|
||||||
'3',
|
|
||||||
'4',
|
|
||||||
'5'
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
async step => {
|
|
||||||
const locale = step.context.activity.locale;
|
|
||||||
await step.context.sendActivity(Messages[locale].thanks);
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
227
packages/azuredeployer.gbapp/dialogs/StartDialog.ts
Normal file
227
packages/azuredeployer.gbapp/dialogs/StartDialog.ts
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
/*****************************************************************************\
|
||||||
|
| ( )_ _ |
|
||||||
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
|
| | | ( )_) | |
|
||||||
|
| (_) \___/' |
|
||||||
|
| |
|
||||||
|
| 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. |
|
||||||
|
| |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview General Bots server core.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||||
|
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
||||||
|
const scanf = require('scanf');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles command-line dialog for getting info for Boot Bot.
|
||||||
|
*/
|
||||||
|
export class StartDialog {
|
||||||
|
public static async createBaseInstance(installationDeployer: IGBInstallationDeployer) {
|
||||||
|
// No .env so asks for cloud credentials to start a new farm.
|
||||||
|
|
||||||
|
if (!fs.existsSync(`.env`)) {
|
||||||
|
process.stdout.write(
|
||||||
|
'A empty enviroment is detected. To start automatic deploy, please enter some information:\n'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let botId: string;
|
||||||
|
while (botId === undefined) {
|
||||||
|
botId = this.retrieveBotId();
|
||||||
|
}
|
||||||
|
|
||||||
|
let username: string;
|
||||||
|
while (username === undefined) {
|
||||||
|
username = this.retrieveUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
let password: string;
|
||||||
|
while (password === undefined) {
|
||||||
|
password = this.retrievePassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connects to the cloud and retrieves subscriptions.
|
||||||
|
|
||||||
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
|
const list = await installationDeployer.getSubscriptions(credentials);
|
||||||
|
|
||||||
|
let subscriptionId: string;
|
||||||
|
while (subscriptionId === undefined) {
|
||||||
|
subscriptionId = this.retrieveSubscriptionId(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
let location: string;
|
||||||
|
while (location === undefined) {
|
||||||
|
location = this.retrieveLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
let appId: string;
|
||||||
|
while (appId === undefined) {
|
||||||
|
appId = this.retrieveAppId();
|
||||||
|
}
|
||||||
|
|
||||||
|
let appPassword: string;
|
||||||
|
while (appPassword === undefined) {
|
||||||
|
appPassword = this.retrieveAppPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
let authoringKey: string;
|
||||||
|
while (authoringKey === undefined) {
|
||||||
|
authoringKey = this.retrieveAuthoringKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(`${GBAdminService.GB_PROMPT}Thank you. That is enough information.\nNow building farm...`);
|
||||||
|
|
||||||
|
// Prepares the first instance on bot farm.
|
||||||
|
const instance = <IGBInstance>{};
|
||||||
|
|
||||||
|
instance.botId = botId;
|
||||||
|
instance.cloudUsername = username;
|
||||||
|
instance.cloudPassword = password;
|
||||||
|
instance.cloudSubscriptionId = subscriptionId;
|
||||||
|
instance.cloudLocation = location;
|
||||||
|
instance.nlpAuthoringKey = authoringKey;
|
||||||
|
instance.marketplaceId = appId;
|
||||||
|
instance.marketplacePassword = appPassword;
|
||||||
|
instance.adminPass = GBAdminService.getRndPassword();
|
||||||
|
|
||||||
|
return { instance, credentials, subscriptionId };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveUsername() {
|
||||||
|
let value = GBConfigService.get('CLOUD_USERNAME');
|
||||||
|
if (value === undefined) {
|
||||||
|
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_USERNAME:`);
|
||||||
|
value = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrievePassword() {
|
||||||
|
let password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
|
if (password === undefined) {
|
||||||
|
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_PASSWORD:`);
|
||||||
|
password = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveBotId() {
|
||||||
|
let botId = GBConfigService.get('BOT_ID');
|
||||||
|
if (botId === undefined) {
|
||||||
|
process.stdout.write(
|
||||||
|
`${GBAdminService.GB_PROMPT}Choose a unique bot Id containing lowercase letters, digits or
|
||||||
|
dashes (cannot use dash as the first two or last one characters),
|
||||||
|
cannot start or end with or contain consecutive dashes and having 4 to 42 characters long.\n`
|
||||||
|
);
|
||||||
|
process.stdout.write(`${GBAdminService.GB_PROMPT}BOT_ID:`);
|
||||||
|
botId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return botId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveAuthoringKey() {
|
||||||
|
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
||||||
|
if (authoringKey === undefined) {
|
||||||
|
process.stdout.write(
|
||||||
|
`${
|
||||||
|
GBAdminService.GB_PROMPT
|
||||||
|
}Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n`
|
||||||
|
);
|
||||||
|
process.stdout.write(
|
||||||
|
`${
|
||||||
|
GBAdminService.GB_PROMPT
|
||||||
|
}Please enter your LUIS Authoring Key, get it here: https://www.luis.ai/user/settings and paste it to me:`
|
||||||
|
);
|
||||||
|
authoringKey = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return authoringKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveAppId() {
|
||||||
|
let appId = GBConfigService.get('MARKETPLACE_ID');
|
||||||
|
if (appId === undefined) {
|
||||||
|
process.stdout.write(
|
||||||
|
`Sorry, this part cannot be automated yet due to Microsoft schedule,
|
||||||
|
please go to https://apps.dev.microsoft.com/portal/register-app to
|
||||||
|
generate manually an App ID and App Secret.\n`
|
||||||
|
);
|
||||||
|
process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
|
||||||
|
appId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveAppPassword() {
|
||||||
|
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
||||||
|
if (appPassword === undefined) {
|
||||||
|
process.stdout.write('Generated Password (MARKETPLACE_SECRET):');
|
||||||
|
appPassword = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return appPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveSubscriptionId(list) {
|
||||||
|
let subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||||
|
const map = {};
|
||||||
|
let index = 1;
|
||||||
|
list.forEach(element => {
|
||||||
|
GBLog.info(`${index}: ${element.displayName} (${element.subscriptionId})`);
|
||||||
|
map[index++] = element;
|
||||||
|
});
|
||||||
|
let subscriptionIndex;
|
||||||
|
if (!subscriptionIndex) {
|
||||||
|
process.stdout.write('CLOUD_SUBSCRIPTIONID (type a number):');
|
||||||
|
subscriptionIndex = scanf('%d');
|
||||||
|
subscriptionId = map[subscriptionIndex].subscriptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveLocation() {
|
||||||
|
let location = GBConfigService.get('CLOUD_LOCATION');
|
||||||
|
if (location === undefined) {
|
||||||
|
process.stdout.write('CLOUD_LOCATION (eg. westus):');
|
||||||
|
location = scanf('%s');
|
||||||
|
}
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,19 +36,30 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for Azure Deployer.
|
||||||
|
*/
|
||||||
export class GBAzureDeployerPackage implements IGBPackage {
|
export class GBAzureDeployerPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
public sysPackages: IGBPackage[] = null;
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public unloadPackage(core: IGBCoreService): void {}
|
GBLog.verbose(`loadPackage called.`);
|
||||||
|
}
|
||||||
public loadBot(min: GBMinInstance): void {}
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
public unloadBot(min: GBMinInstance): void {}
|
}
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {}
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,34 +37,33 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
||||||
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource';
|
||||||
import {
|
|
||||||
ResourceManagementClient,
|
|
||||||
SubscriptionClient
|
|
||||||
} from 'azure-arm-resource';
|
|
||||||
import { SearchManagementClient } from 'azure-arm-search';
|
import { SearchManagementClient } from 'azure-arm-search';
|
||||||
import { SqlManagementClient } from 'azure-arm-sql';
|
import { SqlManagementClient } from 'azure-arm-sql';
|
||||||
import { WebSiteManagementClient } from 'azure-arm-website';
|
import { WebSiteManagementClient } from 'azure-arm-website';
|
||||||
|
//tslint:disable-next-line:no-submodule-imports
|
||||||
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
||||||
import { GBService, IGBInstance } from 'botlib';
|
import { GBLog, IGBInstallationDeployer, IGBInstance } from 'botlib';
|
||||||
import { HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
import { HttpHeaders, HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
||||||
import { GBDeployer } from 'packages/core.gbapp/services/GBDeployer';
|
|
||||||
import * as simplegit from 'simple-git/promise';
|
|
||||||
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||||
import { GBCorePackage } from '../../../packages/core.gbapp';
|
import { GBCorePackage } from '../../../packages/core.gbapp';
|
||||||
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
||||||
|
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer';
|
||||||
|
|
||||||
const Spinner = require('cli-spinner').Spinner;
|
const Spinner = require('cli-spinner').Spinner;
|
||||||
const scanf = require('scanf');
|
|
||||||
const git = simplegit();
|
// tslint:disable-next-line:no-submodule-imports
|
||||||
const logger = require('../../../src/logger');
|
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const iconUrl =
|
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
|
||||||
'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
|
|
||||||
const publicIp = require('public-ip');
|
const publicIp = require('public-ip');
|
||||||
|
|
||||||
export class AzureDeployerService extends GBService {
|
/**
|
||||||
public static apiVersion = '2017-12-01';
|
* Deployer for Microsoft cloud.
|
||||||
|
*/
|
||||||
|
export class AzureDeployerService implements IGBInstallationDeployer {
|
||||||
|
public apiVersion = '2017-12-01';
|
||||||
|
public defaultEndPoint = 'http://localhost:4242';
|
||||||
public instance: IGBInstance;
|
public instance: IGBInstance;
|
||||||
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
||||||
public webSiteClient: WebSiteManagementClient;
|
public webSiteClient: WebSiteManagementClient;
|
||||||
|
@ -80,15 +79,28 @@ export class AzureDeployerService extends GBService {
|
||||||
public deployer: GBDeployer;
|
public deployer: GBDeployer;
|
||||||
|
|
||||||
constructor(deployer: GBDeployer) {
|
constructor(deployer: GBDeployer) {
|
||||||
super();
|
|
||||||
this.deployer = deployer;
|
this.deployer = deployer;
|
||||||
}
|
}
|
||||||
public static async getSubscriptions(credentials) {
|
|
||||||
|
private static createRequestObject(url: string, accessToken: string, verb: HttpMethods, body: string) {
|
||||||
|
const req = new WebResource();
|
||||||
|
req.method = verb;
|
||||||
|
req.url = url;
|
||||||
|
req.headers.set('Content-Type', 'application/json');
|
||||||
|
req.headers.set('accept-language', '*');
|
||||||
|
req.headers.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
req.body = body;
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSubscriptions(credentials) {
|
||||||
const subscriptionClient = new SubscriptionClient.default(credentials);
|
const subscriptionClient = new SubscriptionClient.default(credentials);
|
||||||
|
|
||||||
return subscriptionClient.subscriptions.list();
|
return subscriptionClient.subscriptions.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getKBSearchSchema(indexName) {
|
public getKBSearchSchema(indexName) {
|
||||||
return {
|
return {
|
||||||
name: indexName,
|
name: indexName,
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -184,44 +196,79 @@ export class AzureDeployerService extends GBService {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
scoringProfiles: [],
|
scoringProfiles: [],
|
||||||
defaultScoringProfile: null,
|
defaultScoringProfile: undefined,
|
||||||
corsOptions: null
|
corsOptions: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployFarm(proxyAddress: string): Promise<IGBInstance> {
|
public async updateBotProxy(botId, group, endpoint) {
|
||||||
|
const baseUrl = `https://management.azure.com/`;
|
||||||
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
|
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||||
|
|
||||||
|
const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
|
||||||
|
const httpClient = new ServiceClient();
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
properties: {
|
||||||
|
endpoint: endpoint
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||||
|
this.provider
|
||||||
|
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||||
|
const url = urlJoin(baseUrl, query);
|
||||||
|
const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
|
||||||
|
const res = await httpClient.sendRequest(req);
|
||||||
|
// CHECK
|
||||||
|
if (!JSON.parse(res.bodyAsText).id) {
|
||||||
|
throw res.bodyAsText;
|
||||||
|
}
|
||||||
|
GBLog.info(`Bot proxy updated at: ${endpoint}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openStorageFirewall(groupName, serverName) {
|
||||||
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
|
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
||||||
|
|
||||||
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
|
const storageClient = new SqlManagementClient(credentials, subscriptionId);
|
||||||
|
|
||||||
|
const ip = await publicIp.v4();
|
||||||
|
const params = {
|
||||||
|
startIpAddress: ip,
|
||||||
|
endIpAddress: ip
|
||||||
|
};
|
||||||
|
|
||||||
|
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deployFarm(
|
||||||
|
proxyAddress: string,
|
||||||
|
instance: IGBInstance,
|
||||||
|
credentials,
|
||||||
|
subscriptionId: string
|
||||||
|
): Promise<IGBInstance> {
|
||||||
const culture = 'en-us';
|
const culture = 'en-us';
|
||||||
|
|
||||||
// Tries do get information from .env file otherwise asks in command-line.
|
this.initServices(credentials, subscriptionId);
|
||||||
|
|
||||||
let instance: IGBInstance = {};
|
|
||||||
instance = await this.ensureConfiguration(instance);
|
|
||||||
instance.marketplacePassword = GBAdminService.getRndPassword();
|
|
||||||
|
|
||||||
const spinner = new Spinner('%s');
|
const spinner = new Spinner('%s');
|
||||||
spinner.start();
|
spinner.start();
|
||||||
spinner.setSpinnerString('|/-\\');
|
spinner.setSpinnerString('|/-\\');
|
||||||
|
|
||||||
let keys: any;
|
let keys: any;
|
||||||
const name = instance.botId;
|
const name = instance.botId;
|
||||||
|
|
||||||
logger.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);
|
||||||
|
|
||||||
logger.info(`Deploying Bot Server...`);
|
GBLog.info(`Deploying Bot Server...`);
|
||||||
const serverFarm = await this.createHostingPlan(
|
const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, instance.cloudLocation);
|
||||||
name,
|
await this.createServer(serverFarm.id, name, `${name}-server`, instance.cloudLocation);
|
||||||
`${name}-server-plan`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
await this.createServer(
|
|
||||||
serverFarm.id,
|
|
||||||
name,
|
|
||||||
`${name}-server`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info(`Deploying Bot Storage...`);
|
GBLog.info(`Deploying Bot Storage...`);
|
||||||
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
||||||
const administratorPassword = GBAdminService.getRndPassword();
|
const administratorPassword = GBAdminService.getRndPassword();
|
||||||
const storageServer = `${name.toLowerCase()}-storage-server`;
|
const storageServer = `${name.toLowerCase()}-storage-server`;
|
||||||
|
@ -234,355 +281,113 @@ export class AzureDeployerService extends GBService {
|
||||||
storageServer,
|
storageServer,
|
||||||
instance.cloudLocation
|
instance.cloudLocation
|
||||||
);
|
);
|
||||||
await this.createStorage(
|
await this.createStorage(name, storageServer, storageName, instance.cloudLocation);
|
||||||
name,
|
|
||||||
storageServer,
|
|
||||||
storageName,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
instance.storageUsername = administratorLogin;
|
instance.storageUsername = administratorLogin;
|
||||||
instance.storagePassword = administratorPassword;
|
instance.storagePassword = administratorPassword;
|
||||||
instance.storageName = storageName;
|
instance.storageName = storageName;
|
||||||
instance.storageDialect = 'mssql';
|
instance.storageDialect = 'mssql';
|
||||||
instance.storageServer = storageServer;
|
instance.storageServer = storageServer;
|
||||||
|
|
||||||
logger.info(`Deploying Search...`);
|
GBLog.info(`Deploying Search...`);
|
||||||
const searchName = `${name}-search`.toLowerCase();
|
const searchName = `${name}-search`.toLowerCase();
|
||||||
await this.createSearch(name, searchName, instance.cloudLocation);
|
await this.createSearch(name, searchName, instance.cloudLocation);
|
||||||
const searchKeys = await this.searchClient.adminKeys.get(
|
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
|
||||||
name,
|
|
||||||
searchName
|
|
||||||
);
|
|
||||||
instance.searchHost = `${searchName}.search.windows.net`;
|
instance.searchHost = `${searchName}.search.windows.net`;
|
||||||
instance.searchIndex = 'azuresql-index';
|
instance.searchIndex = 'azuresql-index';
|
||||||
instance.searchIndexer = 'azuresql-indexer';
|
instance.searchIndexer = 'azuresql-indexer';
|
||||||
instance.searchKey = searchKeys.primaryKey;
|
instance.searchKey = searchKeys.primaryKey;
|
||||||
this.deployer.rebuildIndex(instance);
|
this.deployer.rebuildIndex(instance, this.deployer);
|
||||||
|
|
||||||
logger.info(`Deploying Speech...`);
|
GBLog.info(`Deploying Speech...`);
|
||||||
const speech = await this.createSpeech(
|
const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation);
|
||||||
name,
|
|
||||||
`${name}-speech`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
||||||
instance.speechKeyEndpoint = speech.endpoint;
|
instance.speechEndpoint = speech.endpoint;
|
||||||
instance.speechKey = keys.key1;
|
instance.speechKey = keys.key1;
|
||||||
|
|
||||||
logger.info(`Deploying SpellChecker...`);
|
GBLog.info(`Deploying SpellChecker...`);
|
||||||
const spellChecker = await this.createSpellChecker(
|
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`);
|
||||||
name,
|
keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
|
||||||
`${name}-spellchecker`,
|
instance.spellcheckerKey = keys.key1;
|
||||||
instance.cloudLocation
|
instance.spellcheckerEndpoint = spellChecker.endpoint;
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(
|
|
||||||
name,
|
|
||||||
spellChecker.name
|
|
||||||
);
|
|
||||||
instance.spellCheckerKey = keys.key1;
|
|
||||||
instance.spellCheckerEndpoint = spellChecker.endpoint;
|
|
||||||
|
|
||||||
logger.info(`Deploying Text Analytics...`);
|
GBLog.info(`Deploying Text Analytics...`);
|
||||||
const textAnalytics = await this.createTextAnalytics(
|
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
|
||||||
name,
|
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
|
||||||
`${name}-textanalytics`,
|
|
||||||
instance.cloudLocation
|
instance.textAnalyticsEndpoint = textAnalytics.endpoint.replace(`/text/analytics/v2.0`, '');
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(
|
|
||||||
name,
|
|
||||||
textAnalytics.name
|
|
||||||
);
|
|
||||||
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
|
|
||||||
instance.textAnalyticsKey = keys.key1;
|
instance.textAnalyticsKey = keys.key1;
|
||||||
|
|
||||||
logger.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);
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
||||||
const nlpAppId = await this.createLUISApp(
|
const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
|
||||||
name,
|
|
||||||
name,
|
|
||||||
instance.cloudLocation,
|
|
||||||
culture,
|
|
||||||
instance.nlpAuthoringKey
|
|
||||||
);
|
|
||||||
|
|
||||||
instance.nlpEndpoint = nlp.endpoint;
|
instance.nlpEndpoint = nlp.endpoint;
|
||||||
instance.nlpKey = keys.key1;
|
instance.nlpKey = keys.key1;
|
||||||
instance.nlpAppId = nlpAppId;
|
instance.nlpAppId = nlpAppId;
|
||||||
|
|
||||||
logger.info(`Deploying Bot...`);
|
GBLog.info(`Deploying Bot...`);
|
||||||
|
instance.botEndpoint = this.defaultEndPoint;
|
||||||
|
|
||||||
// TODO: Default endpoint, will be updated when it runs in production.
|
instance = await this.internalDeployBot(
|
||||||
|
|
||||||
instance.botEndpoint = 'http://localhost:4242';
|
|
||||||
|
|
||||||
instance = await this.deployBootBot(
|
|
||||||
instance,
|
instance,
|
||||||
|
this.accessToken,
|
||||||
name,
|
name,
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
'General BootBot',
|
||||||
`${proxyAddress}/api/messages/${name}`,
|
`${proxyAddress}/api/messages/${name}`,
|
||||||
|
'global',
|
||||||
instance.nlpAppId,
|
instance.nlpAppId,
|
||||||
instance.nlpKey,
|
instance.nlpKey,
|
||||||
|
instance.marketplaceId,
|
||||||
|
instance.marketplacePassword,
|
||||||
instance.cloudSubscriptionId
|
instance.cloudSubscriptionId
|
||||||
);
|
);
|
||||||
|
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openStorageFirewall(groupName, serverName) {
|
public async deployToCloud(
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
title: string,
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
username: string,
|
||||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
password: string,
|
||||||
|
cloudLocation: string,
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(
|
authoringKey: string,
|
||||||
username,
|
appId: string,
|
||||||
password
|
appPassword: string,
|
||||||
);
|
subscriptionId: string
|
||||||
const storageClient = new SqlManagementClient(credentials, subscriptionId);
|
|
||||||
|
|
||||||
const ip = await publicIp.v4();
|
|
||||||
const params = {
|
|
||||||
startIpAddress: ip,
|
|
||||||
endIpAddress: ip
|
|
||||||
};
|
|
||||||
|
|
||||||
await storageClient.firewallRules.createOrUpdate(
|
|
||||||
groupName,
|
|
||||||
serverName,
|
|
||||||
'gb',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deployBootBot(
|
|
||||||
instance,
|
|
||||||
botId,
|
|
||||||
endpoint,
|
|
||||||
nlpAppId,
|
|
||||||
nlpKey,
|
|
||||||
subscriptionId
|
|
||||||
) {
|
) {
|
||||||
let appId = GBConfigService.get('MARKETPLACE_ID');
|
const instance = <IGBInstance>{};
|
||||||
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
|
||||||
|
|
||||||
if (!appId || !appPassword) {
|
instance.botId = title;
|
||||||
process.stdout.write(
|
|
||||||
'Sorry, this part cannot be automated yet due to Microsoft schedule, please go to https://apps.dev.microsoft.com/portal/register-app to generate manually an App ID and App Secret.\n'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const retriveAppId = () => {
|
|
||||||
if (!appId) {
|
|
||||||
process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
|
|
||||||
appId = scanf('%s').replace(/(\n|\r)+$/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const retriveAppPassword = () => {
|
|
||||||
if (!appPassword) {
|
|
||||||
process.stdout.write('Generated Password (MARKETPLACE_SECRET):');
|
|
||||||
appPassword = scanf('%s').replace(/(\n|\r)+$/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
retriveAppId();
|
|
||||||
retriveAppPassword();
|
|
||||||
|
|
||||||
await this.internalDeployBot(
|
|
||||||
instance,
|
|
||||||
this.accessToken,
|
|
||||||
botId,
|
|
||||||
botId,
|
|
||||||
botId,
|
|
||||||
'General BootBot',
|
|
||||||
endpoint,
|
|
||||||
'global',
|
|
||||||
nlpAppId,
|
|
||||||
nlpKey,
|
|
||||||
appId,
|
|
||||||
appPassword,
|
|
||||||
subscriptionId
|
|
||||||
);
|
|
||||||
instance.marketplaceId = appId;
|
|
||||||
instance.marketplacePassword = appPassword;
|
|
||||||
instance.botId = botId;
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateBotProxy(botId, group, endpoint) {
|
|
||||||
const baseUrl = `https://management.azure.com/`;
|
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
|
||||||
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
|
||||||
|
|
||||||
const accessToken = await GBAdminService.getADALTokenFromUsername(
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
);
|
|
||||||
const httpClient = new ServiceClient();
|
|
||||||
|
|
||||||
const parameters = {
|
|
||||||
properties: {
|
|
||||||
endpoint: endpoint
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
|
||||||
this.provider
|
|
||||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
|
||||||
const url = UrlJoin(baseUrl, query);
|
|
||||||
const req = this.createRequestObject(
|
|
||||||
url,
|
|
||||||
accessToken,
|
|
||||||
'PATCH',
|
|
||||||
JSON.stringify(parameters)
|
|
||||||
);
|
|
||||||
const res = await httpClient.sendRequest(req);
|
|
||||||
if (!(res.bodyAsJson as any).id) {
|
|
||||||
throw res.bodyAsText;
|
|
||||||
}
|
|
||||||
logger.info(`Bot proxy updated at: ${endpoint}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deployGeneralBotsToAzure() {
|
|
||||||
const status = await git.status();
|
|
||||||
// TODO: Copy github to webapp.
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ensureConfiguration(instance: IGBInstance) {
|
|
||||||
let username = GBConfigService.get('CLOUD_USERNAME');
|
|
||||||
let password = GBConfigService.get('CLOUD_PASSWORD');
|
|
||||||
let subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
|
||||||
let location = GBConfigService.get('CLOUD_LOCATION');
|
|
||||||
let botId = GBConfigService.get('BOT_ID');
|
|
||||||
|
|
||||||
// No .env so asks for cloud credentials to start a new farm.
|
|
||||||
if (!username || !password || !subscriptionId || !location || !botId) {
|
|
||||||
process.stdout.write(
|
|
||||||
'A empty enviroment is detected. To start automatic deploy, please enter some information:\n'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const retriveUsername = () => {
|
|
||||||
if (!username) {
|
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_USERNAME:`);
|
|
||||||
username = scanf('%s').replace(/(\n|\r)+$/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const retrivePassword = () => {
|
|
||||||
if (!password) {
|
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}CLOUD_PASSWORD:`);
|
|
||||||
password = scanf('%s').replace(/(\n|\r)+$/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const retrieveBotId = () => {
|
|
||||||
if (!botId) {
|
|
||||||
process.stdout.write(
|
|
||||||
`${GBAdminService.GB_PROMPT}Choose a unique bot Id containing lowercase letters, digits or dashes (cannot use dash as the first two or last one characters), cannot start or end with or contain consecutive dashes and having 4 to 42 characters long.\n`
|
|
||||||
);
|
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}BOT_ID:`);
|
|
||||||
botId = scanf('%s').replace(/(\n|\r)+$/, ''); // TODO: Update this regexp to match description of it.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
|
||||||
const retriveAuthoringKey = () => {
|
|
||||||
if (!authoringKey) {
|
|
||||||
process.stdout.write(
|
|
||||||
`${GBAdminService.GB_PROMPT}Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n`
|
|
||||||
);
|
|
||||||
process.stdout.write(`${GBAdminService.GB_PROMPT}Please enter your LUIS Authoring Key:`);
|
|
||||||
authoringKey = scanf('%s').replace(/(\n|\r)+$/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
while (!authoringKey) {
|
|
||||||
retriveAuthoringKey();
|
|
||||||
}
|
|
||||||
while (!botId) {
|
|
||||||
retrieveBotId();
|
|
||||||
}
|
|
||||||
while (!username) {
|
|
||||||
retriveUsername();
|
|
||||||
}
|
|
||||||
while (!password) {
|
|
||||||
retrivePassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connects to the cloud and retrives subscriptions.
|
|
||||||
|
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
);
|
|
||||||
if (!subscriptionId) {
|
|
||||||
const map = {};
|
|
||||||
let index = 1;
|
|
||||||
const list = await AzureDeployerService.getSubscriptions(credentials);
|
|
||||||
list.forEach(element => {
|
|
||||||
console.log(
|
|
||||||
`${index}: ${element.displayName} (${element.subscriptionId})`
|
|
||||||
);
|
|
||||||
map[index++] = element;
|
|
||||||
});
|
|
||||||
let subscriptionIndex;
|
|
||||||
const retrieveSubscription = () => {
|
|
||||||
if (!subscriptionIndex) {
|
|
||||||
process.stdout.write('CLOUD_SUBSCRIPTIONID (type a number):');
|
|
||||||
subscriptionIndex = scanf('%d');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
while (!subscriptionIndex) {
|
|
||||||
retrieveSubscription();
|
|
||||||
}
|
|
||||||
subscriptionId = map[subscriptionIndex].subscriptionId;
|
|
||||||
}
|
|
||||||
const retriveLocation = () => {
|
|
||||||
if (!location) {
|
|
||||||
process.stdout.write('CLOUD_LOCATION (eg. \'westus\'):');
|
|
||||||
location = scanf('%s');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
while (!location) {
|
|
||||||
retriveLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepares the first instance on bot farm.
|
|
||||||
|
|
||||||
instance.botId = botId;
|
|
||||||
instance.cloudUsername = username;
|
instance.cloudUsername = username;
|
||||||
instance.cloudPassword = password;
|
instance.cloudPassword = password;
|
||||||
instance.cloudSubscriptionId = subscriptionId;
|
instance.cloudSubscriptionId = subscriptionId;
|
||||||
instance.cloudLocation = location;
|
instance.cloudLocation = cloudLocation;
|
||||||
instance.nlpAuthoringKey = authoringKey;
|
instance.nlpAuthoringKey = authoringKey;
|
||||||
|
instance.marketplaceId = appId;
|
||||||
|
instance.marketplacePassword = appPassword;
|
||||||
instance.adminPass = GBAdminService.getRndPassword();
|
instance.adminPass = GBAdminService.getRndPassword();
|
||||||
|
|
||||||
this.resourceClient = new ResourceManagementClient.default(
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
credentials,
|
// tslint:disable-next-line:no-http-string
|
||||||
subscriptionId
|
const url = `http://${instance.botId}.azurewebsites.net`;
|
||||||
);
|
this.deployFarm(url, instance, credentials, subscriptionId);
|
||||||
this.webSiteClient = new WebSiteManagementClient(
|
|
||||||
credentials,
|
|
||||||
subscriptionId
|
|
||||||
);
|
|
||||||
this.storageClient = new SqlManagementClient(credentials, subscriptionId);
|
|
||||||
this.cognitiveClient = new CognitiveServicesManagementClient(
|
|
||||||
credentials,
|
|
||||||
subscriptionId
|
|
||||||
);
|
|
||||||
|
|
||||||
this.searchClient = new SearchManagementClient(credentials, subscriptionId);
|
|
||||||
this.accessToken = credentials.tokenCache._entries[0].accessToken;
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createStorageServer(
|
private initServices(credentials: any, subscriptionId: string) {
|
||||||
group,
|
this.resourceClient = new ResourceManagementClient.default(credentials, subscriptionId);
|
||||||
name,
|
this.webSiteClient = new WebSiteManagementClient(credentials, subscriptionId);
|
||||||
administratorLogin,
|
this.storageClient = new SqlManagementClient(credentials, subscriptionId);
|
||||||
administratorPassword,
|
this.cognitiveClient = new CognitiveServicesManagementClient(credentials, subscriptionId);
|
||||||
serverName,
|
this.searchClient = new SearchManagementClient(credentials, subscriptionId);
|
||||||
location
|
this.accessToken = credentials.tokenCache._entries[0].accessToken;
|
||||||
) {
|
}
|
||||||
|
|
||||||
|
private async createStorageServer(group, name, administratorLogin, administratorPassword, serverName, location) {
|
||||||
const params = {
|
const params = {
|
||||||
location: location,
|
location: location,
|
||||||
administratorLogin: administratorLogin,
|
administratorLogin: administratorLogin,
|
||||||
|
@ -594,23 +399,18 @@ export class AzureDeployerService extends GBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async registerProviders(subscriptionId, baseUrl, accessToken) {
|
private async registerProviders(subscriptionId, baseUrl, accessToken) {
|
||||||
const query = `subscriptions/${subscriptionId}/providers/${
|
const query = `subscriptions/${subscriptionId}/providers/${this.provider}/register?api-version=2018-02-01`;
|
||||||
this.provider
|
const requestUrl = urlJoin(baseUrl, query);
|
||||||
}/register?api-version=2018-02-01`;
|
|
||||||
const requestUrl = UrlJoin(baseUrl, query);
|
|
||||||
|
|
||||||
const req = new WebResource();
|
const req = new WebResource();
|
||||||
req.method = 'POST';
|
req.method = 'POST';
|
||||||
req.url = requestUrl;
|
req.url = requestUrl;
|
||||||
req.headers = {};
|
req.headers = <any>{};
|
||||||
req.headers['Content-Type'] = 'application/json; charset=utf-8';
|
req.headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||||
req.headers['accept-language'] = '*';
|
req.headers['accept-language'] = '*';
|
||||||
req.headers.Authorization = 'Bearer ' + accessToken;
|
(req.headers as any).Authorization = `Bearer ${accessToken}`;
|
||||||
|
|
||||||
const httpClient = new ServiceClient();
|
|
||||||
const res = await httpClient.sendRequest(req);
|
|
||||||
// TODO: Check res for error.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json
|
* @see https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json
|
||||||
*/
|
*/
|
||||||
|
@ -628,7 +428,7 @@ export class AzureDeployerService extends GBService {
|
||||||
appId,
|
appId,
|
||||||
appPassword,
|
appPassword,
|
||||||
subscriptionId
|
subscriptionId
|
||||||
) {
|
): Promise<IGBInstance> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const baseUrl = `https://management.azure.com/`;
|
const baseUrl = `https://management.azure.com/`;
|
||||||
await this.registerProviders(subscriptionId, baseUrl, accessToken);
|
await this.registerProviders(subscriptionId, baseUrl, accessToken);
|
||||||
|
@ -661,62 +461,36 @@ export class AzureDeployerService extends GBService {
|
||||||
const httpClient = new ServiceClient();
|
const httpClient = new ServiceClient();
|
||||||
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${
|
||||||
this.provider
|
this.provider
|
||||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
}/botServices/${botId}?api-version=${this.apiVersion}`;
|
||||||
let url = UrlJoin(baseUrl, query);
|
let url = urlJoin(baseUrl, query);
|
||||||
let req = this.createRequestObject(
|
let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
|
||||||
url,
|
|
||||||
accessToken,
|
|
||||||
'PUT',
|
|
||||||
JSON.stringify(parameters)
|
|
||||||
);
|
|
||||||
const res = await httpClient.sendRequest(req);
|
const res = await httpClient.sendRequest(req);
|
||||||
if (!(res.bodyAsJson as any).id) {
|
if (!JSON.parse(res.bodyAsText).id) {
|
||||||
reject(res.bodyAsText);
|
reject(res.bodyAsText);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
|
//tslint:disable-next-line:max-line-length
|
||||||
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${
|
||||||
AzureDeployerService.apiVersion
|
this.apiVersion
|
||||||
}`;
|
}`;
|
||||||
url = UrlJoin(baseUrl, query);
|
url = urlJoin(baseUrl, query);
|
||||||
req = this.createRequestObject(
|
req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters));
|
||||||
url,
|
|
||||||
accessToken,
|
|
||||||
'GET',
|
|
||||||
JSON.stringify(parameters)
|
|
||||||
);
|
|
||||||
const resChannel = await httpClient.sendRequest(req);
|
const resChannel = await httpClient.sendRequest(req);
|
||||||
const key = (resChannel.bodyAsJson as any).properties.properties
|
const key = JSON.parse(resChannel.bodyAsText).properties.properties.sites[0].key;
|
||||||
.sites[0].key;
|
|
||||||
instance.webchatKey = key;
|
instance.webchatKey = key;
|
||||||
resolve(instance);
|
resolve(instance);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
}, 20000);
|
}, 20000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createRequestObject(
|
private async createNLPService(
|
||||||
url: string,
|
|
||||||
accessToken: string,
|
|
||||||
verb: HttpMethods,
|
|
||||||
body: string
|
|
||||||
) {
|
|
||||||
const req = new WebResource();
|
|
||||||
req.method = verb;
|
|
||||||
req.url = url;
|
|
||||||
req.headers = {};
|
|
||||||
req.headers['Content-Type'] = 'application/json';
|
|
||||||
req.headers['accept-language'] = '*';
|
|
||||||
req.headers.Authorization = 'Bearer ' + accessToken;
|
|
||||||
req.body = body;
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createLUISApp(
|
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
location: string,
|
location: string,
|
||||||
|
@ -730,23 +504,11 @@ export class AzureDeployerService extends GBService {
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = JSON.stringify(parameters);
|
const body = JSON.stringify(parameters);
|
||||||
const apps = await this.makeNlpRequest(
|
const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps');
|
||||||
location,
|
const app = JSON.parse(apps.bodyAsText).filter(x => x.name === name)[0];
|
||||||
authoringKey,
|
|
||||||
null,
|
|
||||||
'GET',
|
|
||||||
'apps'
|
|
||||||
);
|
|
||||||
const app = (apps.bodyAsJson as any).filter(x => x.name == name)[0];
|
|
||||||
let id: string;
|
let id: string;
|
||||||
if (!app) {
|
if (!app) {
|
||||||
const res = await this.makeNlpRequest(
|
const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');
|
||||||
location,
|
|
||||||
authoringKey,
|
|
||||||
body,
|
|
||||||
'POST',
|
|
||||||
'apps'
|
|
||||||
);
|
|
||||||
id = res.bodyAsText;
|
id = res.bodyAsText;
|
||||||
} else {
|
} else {
|
||||||
id = app.id;
|
id = app.id;
|
||||||
|
@ -765,14 +527,13 @@ export class AzureDeployerService extends GBService {
|
||||||
const req = new WebResource();
|
const req = new WebResource();
|
||||||
req.method = method;
|
req.method = method;
|
||||||
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
|
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
|
||||||
req.headers = {};
|
req.headers.set('Content-Type', 'application/json');
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers.set('accept-language', '*');
|
||||||
req.headers['accept-language'] = '*';
|
req.headers.set('Ocp-Apim-Subscription-Key', authoringKey);
|
||||||
req.headers['Ocp-Apim-Subscription-Key'] = authoringKey;
|
|
||||||
req.body = body;
|
req.body = body;
|
||||||
const httpClient = new ServiceClient();
|
const httpClient = new ServiceClient();
|
||||||
const res = await httpClient.sendRequest(req);
|
|
||||||
return res;
|
return await httpClient.sendRequest(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSearch(group, name, location) {
|
private async createSearch(group, name, location) {
|
||||||
|
@ -791,20 +552,10 @@ export class AzureDeployerService extends GBService {
|
||||||
location: location
|
location: location
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.storageClient.databases.createOrUpdate(
|
return this.storageClient.databases.createOrUpdate(group, serverName, name, params);
|
||||||
group,
|
|
||||||
serverName,
|
|
||||||
name,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createCognitiveServices(
|
private async createCognitiveServices(group, name, location, kind): Promise<CognitiveServicesAccount> {
|
||||||
group,
|
|
||||||
name,
|
|
||||||
location,
|
|
||||||
kind
|
|
||||||
): Promise<CognitiveServicesAccount> {
|
|
||||||
const params = {
|
const params = {
|
||||||
sku: { name: 'F0' },
|
sku: { name: 'F0' },
|
||||||
createMode: 'Default',
|
createMode: 'Default',
|
||||||
|
@ -816,63 +567,29 @@ export class AzureDeployerService extends GBService {
|
||||||
return await this.cognitiveClient.accounts.create(group, name, params);
|
return await this.cognitiveClient.accounts.create(group, name, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSpeech(
|
private async createSpeech(group, name, location): Promise<CognitiveServicesAccount> {
|
||||||
group,
|
return await this.createCognitiveServices(group, name, location, 'SpeechServices');
|
||||||
name,
|
|
||||||
location
|
|
||||||
): Promise<CognitiveServicesAccount> {
|
|
||||||
return await this.createCognitiveServices(
|
|
||||||
group,
|
|
||||||
name,
|
|
||||||
location,
|
|
||||||
'SpeechServices'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createNLP(
|
private async createNLP(group, name, location): Promise<CognitiveServicesAccount> {
|
||||||
group,
|
|
||||||
name,
|
|
||||||
location
|
|
||||||
): Promise<CognitiveServicesAccount> {
|
|
||||||
return await this.createCognitiveServices(group, name, location, 'LUIS');
|
return await this.createCognitiveServices(group, name, location, 'LUIS');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSpellChecker(
|
private async createSpellChecker(group, name): Promise<CognitiveServicesAccount> {
|
||||||
group,
|
return await this.createCognitiveServices(group, name, 'global', 'Bing.SpellCheck.v7');
|
||||||
name,
|
|
||||||
location
|
|
||||||
): Promise<CognitiveServicesAccount> {
|
|
||||||
return await this.createCognitiveServices(
|
|
||||||
group,
|
|
||||||
name,
|
|
||||||
'global',
|
|
||||||
'Bing.SpellCheck.v7'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createTextAnalytics(
|
private async createTextAnalytics(group, name, location): Promise<CognitiveServicesAccount> {
|
||||||
group,
|
return await this.createCognitiveServices(group, name, location, 'TextAnalytics');
|
||||||
name,
|
|
||||||
location
|
|
||||||
): Promise<CognitiveServicesAccount> {
|
|
||||||
return await this.createCognitiveServices(
|
|
||||||
group,
|
|
||||||
name,
|
|
||||||
location,
|
|
||||||
'TextAnalytics'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createDeployGroup(name, location) {
|
private async createDeployGroup(name, location) {
|
||||||
const params = { location: location };
|
const params = { location: location };
|
||||||
|
|
||||||
return this.resourceClient.resourceGroups.createOrUpdate(name, params);
|
return this.resourceClient.resourceGroups.createOrUpdate(name, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createHostingPlan(
|
private async createHostingPlan(group, name, location): Promise<AppServicePlan> {
|
||||||
group,
|
|
||||||
name,
|
|
||||||
location
|
|
||||||
): Promise<AppServicePlan> {
|
|
||||||
const params = {
|
const params = {
|
||||||
serverFarmWithRichSkuName: name,
|
serverFarmWithRichSkuName: name,
|
||||||
location: location,
|
location: location,
|
||||||
|
@ -883,11 +600,7 @@ export class AzureDeployerService extends GBService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.webSiteClient.appServicePlans.createOrUpdate(
|
return this.webSiteClient.appServicePlans.createOrUpdate(group, name, params);
|
||||||
group,
|
|
||||||
name,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createServer(farmId, group, name, location) {
|
private async createServer(farmId, group, name, location) {
|
||||||
|
@ -895,23 +608,7 @@ export class AzureDeployerService extends GBService {
|
||||||
location: location,
|
location: location,
|
||||||
serverFarmId: farmId
|
serverFarmId: farmId
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.webSiteClient.webApps.createOrUpdate(group, name, parameters);
|
return this.webSiteClient.webApps.createOrUpdate(group, name, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateWebisteConfig(group, serverFarmId, name, location) {
|
|
||||||
const siteConfig = {
|
|
||||||
location: location,
|
|
||||||
serverFarmId: serverFarmId,
|
|
||||||
numberOfWorkers: 1,
|
|
||||||
phpVersion: '5.5'
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Copy .env to app settings.
|
|
||||||
|
|
||||||
return this.webSiteClient.webApps.createOrUpdateConfiguration(
|
|
||||||
group,
|
|
||||||
name,
|
|
||||||
siteConfig
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,30 +36,33 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for console.glib.
|
||||||
|
*/
|
||||||
export class GBConsolePackage implements IGBPackage {
|
export class GBConsolePackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public channel: ConsoleDirectLine;
|
public channel: ConsoleDirectLine;
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
GBLog.verbose(`loadPackage called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
this.channel = new ConsoleDirectLine(min.instance.webchatKey);
|
this.channel = new ConsoleDirectLine(min.instance.webchatKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,193 +1,176 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Swagger = require('swagger-client');
|
const Swagger = require('swagger-client');
|
||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
import { GBService } from 'botlib';
|
import { GBLog, GBService } from 'botlib';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bot simulator in terminal window.
|
||||||
|
*/
|
||||||
export class ConsoleDirectLine extends GBService {
|
export class ConsoleDirectLine extends GBService {
|
||||||
|
public pollInterval: number = 1000;
|
||||||
|
public directLineSecret: string = '';
|
||||||
|
public directLineClientName: string = 'DirectLineClient';
|
||||||
|
public directLineSpecUrl: string = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||||
|
|
||||||
public pollInterval = 1000;
|
constructor(directLineSecret: string) {
|
||||||
public directLineSecret = '';
|
super();
|
||||||
public directLineClientName = 'DirectLineClient';
|
|
||||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
|
||||||
|
|
||||||
constructor(directLineSecret) {
|
this.directLineSecret = directLineSecret;
|
||||||
super();
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const directLineClient = rp(this.directLineSpecUrl)
|
||||||
this.directLineSecret = directLineSecret;
|
.then((spec: string) => {
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
// TODO: Migrate to Swagger 3.
|
return new Swagger({
|
||||||
const directLineClient = rp(this.directLineSpecUrl)
|
spec: JSON.parse(spec.trim()),
|
||||||
.then(function (spec) {
|
usePromise: true
|
||||||
return new Swagger({
|
});
|
||||||
spec: JSON.parse(spec.trim()),
|
})
|
||||||
usePromise: true
|
.then(client => {
|
||||||
});
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
})
|
client.clientAuthorizations.add(
|
||||||
.then(function (client) {
|
'AuthorizationBotConnector',
|
||||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));
|
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${directLineSecret}`, 'header')
|
||||||
return client;
|
);
|
||||||
})
|
|
||||||
.catch(function (err) {
|
return client;
|
||||||
console.error('Error initializing DirectLine client', err);
|
})
|
||||||
});
|
.catch(err => {
|
||||||
|
GBLog.error(`Error initializing DirectLine client ${err}`);
|
||||||
// TODO: Remove *this* issue.
|
});
|
||||||
const _this_ = this;
|
|
||||||
directLineClient.then((client) => {
|
const _this_ = this;
|
||||||
client.Conversations.Conversations_StartConversation()
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
.then(function (response) {
|
directLineClient.then(client => {
|
||||||
return response.obj.conversationId;
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
})
|
client.Conversations.Conversations_StartConversation()
|
||||||
.then(function (conversationId) {
|
.then(response => {
|
||||||
_this_.sendMessagesFromConsole(client, conversationId);
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
_this_.pollMessages(client, conversationId);
|
return response.obj.conversationId;
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.then(conversationId => {
|
||||||
console.error('Error starting conversation', err);
|
_this_.sendMessagesFromConsole(client, conversationId);
|
||||||
});
|
_this_.pollMessages(client, conversationId);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error starting conversation ${err}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendMessagesFromConsole(client, conversationId) {
|
||||||
|
const _this_ = this;
|
||||||
|
process.stdin.resume();
|
||||||
|
const stdin = process.stdin;
|
||||||
|
process.stdout.write('Command> ');
|
||||||
|
stdin.addListener('data', e => {
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
const input: string = e.toString().trim();
|
||||||
|
if (input !== undefined) {
|
||||||
|
// exit
|
||||||
|
if (input.toLowerCase() === 'exit') {
|
||||||
|
return process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
|
client.Conversations.Conversations_PostActivity({
|
||||||
|
conversationId: conversationId,
|
||||||
|
activity: {
|
||||||
|
textFormat: 'plain',
|
||||||
|
text: input,
|
||||||
|
type: 'message',
|
||||||
|
from: {
|
||||||
|
id: _this_.directLineClientName,
|
||||||
|
name: _this_.directLineClientName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
GBLog.error(`Error sending message: ${err}`);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
public sendMessagesFromConsole(client, conversationId) {
|
|
||||||
const _this_ = this;
|
|
||||||
process.stdin.resume();
|
|
||||||
const stdin = process.stdin;
|
|
||||||
process.stdout.write('Command> ');
|
process.stdout.write('Command> ');
|
||||||
stdin.addListener('data', function (e) {
|
}
|
||||||
const input = e.toString().trim();
|
});
|
||||||
if (input) {
|
}
|
||||||
// exit
|
|
||||||
if (input.toLowerCase() === 'exit') {
|
|
||||||
return process.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Conversations.Conversations_PostActivity(
|
public pollMessages(client, conversationId) {
|
||||||
{
|
const _this_ = this;
|
||||||
conversationId: conversationId,
|
GBLog.info(`Starting polling message for conversationId: ${conversationId}`);
|
||||||
activity: {
|
let watermark;
|
||||||
textFormat: 'plain',
|
setInterval(() => {
|
||||||
text: input,
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
type: 'message',
|
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
||||||
from: {
|
.then(response => {
|
||||||
id: _this_.directLineClientName,
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
name: _this_.directLineClientName
|
watermark = response.obj.watermark;
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(function (err) {
|
|
||||||
console.error('Error sending message:', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.stdout.write('Command> ');
|
// tslint:disable-next-line:no-unsafe-any
|
||||||
}
|
return response.obj.activities;
|
||||||
});
|
})
|
||||||
|
.then(_this_.printMessages, _this_.directLineClientName);
|
||||||
|
// tslint:disable-next-line:align
|
||||||
|
}, this.pollInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
|
public printMessages(activities, directLineClientName) {
|
||||||
|
if (activities && activities.length) {
|
||||||
|
// ignore own messages
|
||||||
|
activities = activities.filter(m => {
|
||||||
|
return m.from.id !== directLineClientName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activities.length) {
|
||||||
|
// print other messages
|
||||||
|
activities.forEach(activity => {
|
||||||
|
GBLog.info(activity.text);
|
||||||
|
// tslint:disable-next-line:align
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
process.stdout.write('Command> ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
|
public printMessage(activity) {
|
||||||
|
if (activity.text) {
|
||||||
|
GBLog.info(activity.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TBD: Poll Messages from conversation using DirectLine client */
|
if (activity.attachments) {
|
||||||
public pollMessages(client, conversationId) {
|
activity.attachments.forEach(attachment => {
|
||||||
const _this_ = this;
|
switch (attachment.contentType) {
|
||||||
console.log('Starting polling message for conversationId: ' + conversationId);
|
case 'application/vnd.microsoft.card.hero':
|
||||||
let watermark = null;
|
this.renderHeroCard(attachment);
|
||||||
setInterval(function () {
|
break;
|
||||||
client.Conversations.Conversations_GetActivities({ conversationId: conversationId, watermark: watermark })
|
|
||||||
.then(function (response) {
|
|
||||||
watermark = response.obj.watermark; // use watermark so subsequent requests skip old messages
|
|
||||||
return response.obj.activities;
|
|
||||||
})
|
|
||||||
.then(_this_.printMessages, _this_.directLineClientName);
|
|
||||||
}, this.pollInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessages(activities, directLineClientName) {
|
case 'image/png':
|
||||||
|
GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
|
||||||
|
open(attachment.contentUrl);
|
||||||
|
break;
|
||||||
|
|
||||||
if (activities && activities.length) {
|
default:
|
||||||
// ignore own messages
|
GBLog.info(`Unknown contentType: ${attachment.contentType}`);
|
||||||
activities = activities.filter(function (m) { return m.from.id !== directLineClientName; });
|
break;
|
||||||
|
|
||||||
if (activities.length) {
|
|
||||||
|
|
||||||
// print other messages
|
|
||||||
activities.forEach(activity => {
|
|
||||||
console.log(activity.text);
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
process.stdout.write('Command> ');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
public printMessage(activity) {
|
// tslint:disable:no-unsafe-any
|
||||||
if (activity.text) {
|
public renderHeroCard(attachment) {
|
||||||
console.log(activity.text);
|
const width = 70;
|
||||||
}
|
const contentLine = content => {
|
||||||
|
return `${' '.repeat((width - content.length) / 2)}content${' '.repeat((width - content.length) / 2)}`;
|
||||||
|
};
|
||||||
|
|
||||||
if (activity.attachments) {
|
GBLog.info(`/${'*'.repeat(width + 1)}`);
|
||||||
activity.attachments.forEach(function (attachment) {
|
GBLog.info(`*${contentLine(attachment.content.title)}*`);
|
||||||
switch (attachment.contentType) {
|
GBLog.info(`*${' '.repeat(width)}*`);
|
||||||
case 'application/vnd.microsoft.card.hero':
|
GBLog.info(`*${contentLine(attachment.content.text)}*`);
|
||||||
this.renderHeroCard(attachment);
|
GBLog.info(`${'*'.repeat(width + 1)}/`);
|
||||||
break;
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
case 'image/png':
|
|
||||||
console.log('Opening the requested image ' + attachment.contentUrl);
|
|
||||||
open(attachment.contentUrl);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderHeroCard(attachment) {
|
|
||||||
const width = 70;
|
|
||||||
const contentLine = function (content) {
|
|
||||||
return ' '.repeat((width - content.length) / 2) +
|
|
||||||
content +
|
|
||||||
' '.repeat((width - content.length) / 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('/' + '*'.repeat(width + 1));
|
|
||||||
console.log('*' + contentLine(attachment.content.title) + '*');
|
|
||||||
console.log('*' + ' '.repeat(width) + '*');
|
|
||||||
console.log('*' + contentLine(attachment.content.text) + '*');
|
|
||||||
console.log('*'.repeat(width + 1) + '/');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,10 +38,12 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import {WaterfallDialog } from 'botbuilder-dialogs';
|
import {WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for Welcoming people.
|
||||||
|
*/
|
||||||
export class WelcomeDialog extends IGBDialog {
|
export class WelcomeDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -73,9 +75,9 @@ export class WelcomeDialog extends IGBDialog {
|
||||||
await step.replaceDialog('/ask', { firstTime: true });
|
await step.replaceDialog('/ask', { firstTime: true });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
step.context.activity &&
|
step.context.activity !== undefined &&
|
||||||
step.context.activity.type == 'message' &&
|
step.context.activity.type === 'message' &&
|
||||||
step.context.activity.text != ''
|
step.context.activity.text !== ''
|
||||||
) {
|
) {
|
||||||
await step.replaceDialog('/answer', { query: step.context.activity.text });
|
await step.replaceDialog('/answer', { query: step.context.activity.text });
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,10 +38,11 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
/**
|
||||||
|
* Dialog for the bot explains about itself.
|
||||||
|
*/
|
||||||
export class WhoAmIDialog extends IGBDialog {
|
export class WhoAmIDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -55,7 +56,7 @@ export class WhoAmIDialog extends IGBDialog {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
await step.context.sendActivity(`${min.instance.description}`);
|
await step.context.sendActivity(`${min.instance.description}`);
|
||||||
|
|
||||||
if (min.instance.whoAmIVideo) {
|
if (min.instance.whoAmIVideo !== undefined) {
|
||||||
await step.context.sendActivity(Messages[locale].show_video);
|
await step.context.sendActivity(Messages[locale].show_video);
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'video',
|
playerType: 'video',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,42 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
|
||||||
|
|
||||||
import { IGBCoreService} from 'botlib';
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { WelcomeDialog } from './dialogs/WelcomeDialog';
|
import { WelcomeDialog } from './dialogs/WelcomeDialog';
|
||||||
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
|
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
|
||||||
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
|
import { GuaribasChannel, GuaribasException, GuaribasInstance, GuaribasPackage } from './models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for core.gbapp.
|
||||||
|
*/
|
||||||
export class GBCorePackage implements IGBPackage {
|
export class GBCorePackage implements IGBPackage {
|
||||||
public static CurrentEngineName = 'guaribas-1.0.0';
|
public static CurrentEngineName = 'guaribas-1.0.0';
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
core.sequelize.addModels([
|
core.sequelize.addModels([GuaribasInstance, GuaribasPackage, GuaribasChannel, GuaribasException]);
|
||||||
GuaribasInstance,
|
|
||||||
GuaribasPackage,
|
|
||||||
GuaribasChannel,
|
|
||||||
GuaribasException
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
WelcomeDialog.setup(min.bot, min);
|
WelcomeDialog.setup(min.bot, min);
|
||||||
WhoAmIDialog.setup(min.bot, min);
|
WhoAmIDialog.setup(min.bot, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -31,46 +31,20 @@
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Logging support.
|
* @fileoverview General Bots server core.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { createLogger, format, transports } = require('winston');
|
'use strict';
|
||||||
|
|
||||||
const config = {
|
import {
|
||||||
levels: {
|
AutoIncrement,
|
||||||
error: 0,
|
BelongsTo,
|
||||||
debug: 1,
|
Column,
|
||||||
warn: 2,
|
CreatedAt,
|
||||||
data: 3,
|
ForeignKey,
|
||||||
info: 4,
|
Model,
|
||||||
verbose: 5,
|
PrimaryKey,
|
||||||
silly: 6,
|
Table,
|
||||||
custom: 7
|
UpdatedAt
|
||||||
},
|
} from 'sequelize-typescript';
|
||||||
colors: {
|
import { GuaribasInstance } from './GBModel';
|
||||||
error: 'red',
|
|
||||||
debug: 'blue',
|
|
||||||
warn: 'yellow',
|
|
||||||
data: 'grey',
|
|
||||||
info: 'green',
|
|
||||||
verbose: 'cyan',
|
|
||||||
silly: 'magenta',
|
|
||||||
custom: 'yellow'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const logger = createLogger({
|
|
||||||
format: format.combine(
|
|
||||||
format.colorize(),
|
|
||||||
format.simple(),
|
|
||||||
format.label({ label: 'GeneralBots' }),
|
|
||||||
format.timestamp(),
|
|
||||||
format.printf(nfo => {
|
|
||||||
return `${nfo.timestamp} [${nfo.label}] ${nfo.level}: ${nfo.message}`;
|
|
||||||
})
|
|
||||||
),
|
|
||||||
levels: config.levels,
|
|
||||||
transports: [new transports.Console()]
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = logger;
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -51,9 +51,13 @@ import {
|
||||||
|
|
||||||
import { IGBInstance } from 'botlib';
|
import { IGBInstance } from 'botlib';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Column
|
||||||
|
@ -80,8 +84,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
@Column
|
@Column
|
||||||
public enabledAdmin: boolean;
|
public enabledAdmin: boolean;
|
||||||
|
|
||||||
/* Services section on bot.json */
|
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public engineName: string;
|
public engineName: string;
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
public speechKey: string;
|
public speechKey: string;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public speechKeyEndpoint: string;
|
public speechEndpoint: string;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
public spellcheckerKey: string;
|
public spellcheckerKey: string;
|
||||||
|
@ -218,8 +220,6 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
@Column
|
@Column
|
||||||
public adminPass: string;
|
public adminPass: string;
|
||||||
|
|
||||||
/* Settings section of bot.json */
|
|
||||||
|
|
||||||
@Column(DataType.FLOAT)
|
@Column(DataType.FLOAT)
|
||||||
public nlpVsSearch: number;
|
public nlpVsSearch: number;
|
||||||
|
|
||||||
|
@ -238,6 +238,9 @@ export class GuaribasInstance extends Model<GuaribasInstance>
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each packaged listed for use in a bot instance.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasPackage extends Model<GuaribasPackage> {
|
export class GuaribasPackage extends Model<GuaribasPackage> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -264,6 +267,9 @@ export class GuaribasPackage extends Model<GuaribasPackage> {
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bot channel.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasChannel extends Model<GuaribasChannel> {
|
export class GuaribasChannel extends Model<GuaribasChannel> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -283,7 +289,11 @@ export class GuaribasChannel extends Model<GuaribasChannel> {
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception that has been thrown.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
|
//tslint:disable-next-line:max-classes-per-file
|
||||||
export class GuaribasException extends Model<GuaribasException> {
|
export class GuaribasException extends Model<GuaribasException> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@AutoIncrement
|
@AutoIncrement
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -34,42 +34,110 @@
|
||||||
|
|
||||||
import { TurnContext } from 'botbuilder';
|
import { TurnContext } from 'botbuilder';
|
||||||
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
import { GBLog, GBMinInstance } from 'botlib';
|
||||||
const WaitUntil = require('wait-until');
|
import * as request from 'request-promise-native';
|
||||||
|
import urlJoin = require('url-join');
|
||||||
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||||
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
|
import { GBDeployer } from './GBDeployer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview General Bots server core.
|
* @fileoverview General Bots server core.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BASIC system class for extra manipulation of bot behaviour.
|
||||||
|
*/
|
||||||
|
class SysClass {
|
||||||
|
public min: GBMinInstance;
|
||||||
|
private readonly deployer: GBDeployer;
|
||||||
|
|
||||||
|
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
|
this.min = min;
|
||||||
|
this.deployer = deployer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async wait(seconds: number) {
|
||||||
|
// tslint:disable-next-line no-string-based-set-timeout
|
||||||
|
const timeout = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
await timeout(seconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public generatePassword() {
|
||||||
|
return GBAdminService.getRndPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createABotFarmUsing(
|
||||||
|
botId: string,
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
location: string,
|
||||||
|
nlpAuthoringKey: string,
|
||||||
|
appId: string,
|
||||||
|
appPassword: string,
|
||||||
|
subscriptionId: string
|
||||||
|
) {
|
||||||
|
const service = new AzureDeployerService(this.deployer);
|
||||||
|
await service.deployToCloud(
|
||||||
|
botId,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
location,
|
||||||
|
nlpAuthoringKey,
|
||||||
|
appId,
|
||||||
|
appPassword,
|
||||||
|
subscriptionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to call any REST API.
|
||||||
|
*/
|
||||||
|
public async sendEmail(to, subject, body) {
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
|
GBLog.info(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to call any REST API.
|
||||||
|
*/
|
||||||
|
public async httpGet(url: string, qs) {
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
uri: urlJoin(url , qs)
|
||||||
|
};
|
||||||
|
|
||||||
|
return request.get(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base services of conversation to be called by BASIC.
|
||||||
|
*/
|
||||||
export class DialogClass {
|
export class DialogClass {
|
||||||
|
|
||||||
public min: GBMinInstance;
|
public min: GBMinInstance;
|
||||||
public context: TurnContext;
|
public context: TurnContext;
|
||||||
public step: WaterfallStepContext;
|
public step: WaterfallStepContext;
|
||||||
|
public internalSys: SysClass;
|
||||||
|
|
||||||
constructor(min: GBMinInstance) {
|
constructor(min: GBMinInstance, deployer: GBDeployer) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
|
this.internalSys = new SysClass(min, deployer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sys(): SysClass {
|
||||||
|
return this.internalSys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async hear(cb) {
|
public async hear(cb) {
|
||||||
let idCallback = Math.floor(Math.random() * 1000000000000);
|
const idCallback = crypto.getRandomValues(new Uint32Array(16))[0];
|
||||||
this.min.cbMap[idCallback] = cb;
|
this.min.cbMap[idCallback] = cb;
|
||||||
await this.step.beginDialog('/hear', { id: idCallback});
|
await this.step.beginDialog('/hear', { id: idCallback });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async talk(text: string) {
|
public async talk(text: string) {
|
||||||
return await this.context.sendActivity(text);
|
return await this.context.sendActivity(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic function to call any REST API.
|
|
||||||
*/
|
|
||||||
public sendEmail(to, subject, body) {
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log(`[E-mail]: to:${to}, subject: ${subject}, body: ${body}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic function to call any REST API.
|
|
||||||
*/
|
|
||||||
public post(url: string, data) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -30,16 +30,29 @@
|
||||||
| |
|
| |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
'use strict';
|
||||||
import * as fs from 'fs';
|
|
||||||
|
import { GBLog } from 'botlib';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview General Bots server core.
|
* @fileoverview General Bots server core.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
/**
|
||||||
|
* Base configuration for the server like storage.
|
||||||
|
*/
|
||||||
export class GBConfigService {
|
export class GBConfigService {
|
||||||
|
public static getServerPort(): number {
|
||||||
|
if (process.env.port !== undefined) {
|
||||||
|
return Number(process.env.port);
|
||||||
|
}
|
||||||
|
if (process.env.PORT !== undefined) {
|
||||||
|
return Number(process.env.PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4242;
|
||||||
|
}
|
||||||
|
|
||||||
public static init(): any {
|
public static init(): any {
|
||||||
try {
|
try {
|
||||||
require('dotenv-extended').load({
|
require('dotenv-extended').load({
|
||||||
|
@ -49,7 +62,7 @@ export class GBConfigService {
|
||||||
overrideProcessEnv: true
|
overrideProcessEnv: true
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message);
|
GBLog.error(e.message);
|
||||||
process.exit(3);
|
process.exit(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +70,7 @@ export class GBConfigService {
|
||||||
public static get(key: string): string | undefined {
|
public static get(key: string): string | undefined {
|
||||||
let value = GBConfigService.tryGet(key);
|
let value = GBConfigService.tryGet(key);
|
||||||
|
|
||||||
if (!value) {
|
if (value === undefined) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'CLOUD_USERNAME':
|
case 'CLOUD_USERNAME':
|
||||||
value = undefined;
|
value = undefined;
|
||||||
|
@ -74,6 +87,12 @@ export class GBConfigService {
|
||||||
case 'CLOUD_LOCATION':
|
case 'CLOUD_LOCATION':
|
||||||
value = undefined;
|
value = undefined;
|
||||||
break;
|
break;
|
||||||
|
case 'MARKETPLACE_ID':
|
||||||
|
value = undefined;
|
||||||
|
break;
|
||||||
|
case 'MARKETPLACE_SECRET':
|
||||||
|
value = undefined;
|
||||||
|
break;
|
||||||
case 'NLP_AUTHORING_KEY':
|
case 'NLP_AUTHORING_KEY':
|
||||||
value = undefined;
|
value = undefined;
|
||||||
break;
|
break;
|
||||||
|
@ -102,18 +121,20 @@ export class GBConfigService {
|
||||||
value = 'true';
|
value = 'true';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.warn(`Invalid key on .env file: '${key}'`);
|
GBLog.warn(`Invalid key on .env file: '${key}'`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static tryGet(key: string) {
|
public static tryGet(key: string): any {
|
||||||
let value = process.env['container:' + key];
|
let value = process.env[`container:${key}`];
|
||||||
if (!value) {
|
if (value === undefined) {
|
||||||
value = process.env[key];
|
value = process.env[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,13 +36,11 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
import { MessageFactory, RecognizerResult } from 'botbuilder';
|
||||||
import { MessageFactory } from 'botbuilder';
|
|
||||||
import { LuisRecognizer } from 'botbuilder-ai';
|
import { LuisRecognizer } from 'botbuilder-ai';
|
||||||
import { GBMinInstance, IGBConversationalService } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBConversationalService, IGBCoreService } from 'botlib';
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { GBCoreService } from './GBCoreService';
|
|
||||||
const Nexmo = require('nexmo');
|
const Nexmo = require('nexmo');
|
||||||
|
|
||||||
export interface LanguagePickerSettings {
|
export interface LanguagePickerSettings {
|
||||||
|
@ -50,18 +48,22 @@ export interface LanguagePickerSettings {
|
||||||
supportedLocales?: string[];
|
supportedLocales?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides basic services for handling messages and dispatching to back-end
|
||||||
|
* services like NLP or Search.
|
||||||
|
*/
|
||||||
export class GBConversationalService implements IGBConversationalService {
|
export class GBConversationalService implements IGBConversationalService {
|
||||||
public coreService: GBCoreService;
|
public coreService: IGBCoreService;
|
||||||
|
|
||||||
constructor(coreService: GBCoreService) {
|
constructor(coreService: IGBCoreService) {
|
||||||
this.coreService = coreService;
|
this.coreService = coreService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentLanguage(step: any) {
|
public getCurrentLanguage(step: GBDialogStep) {
|
||||||
return step.context.activity.locale;
|
return step.context.activity.locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendEvent(step: any, name: string, value: any): Promise<any> {
|
public async sendEvent(step: GBDialogStep, name: string, value: Object): Promise<any> {
|
||||||
if (step.context.activity.channelId === 'webchat') {
|
if (step.context.activity.channelId === 'webchat') {
|
||||||
const msg = MessageFactory.text('');
|
const msg = MessageFactory.text('');
|
||||||
msg.value = value;
|
msg.value = value;
|
||||||
|
@ -72,6 +74,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tslint:disable:no-unsafe-any due to Nexmo.
|
||||||
public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise<any> {
|
public async sendSms(min: GBMinInstance, mobile: string, text: string): Promise<any> {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
(resolve: any, reject: any): any => {
|
(resolve: any, reject: any): any => {
|
||||||
|
@ -79,6 +82,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
apiKey: min.instance.smsKey,
|
apiKey: min.instance.smsKey,
|
||||||
apiSecret: min.instance.smsSecret
|
apiSecret: min.instance.smsSecret
|
||||||
});
|
});
|
||||||
|
// 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) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@ -89,48 +93,55 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
|
|
||||||
public async routeNLP(step: any, min: GBMinInstance, text: string): Promise<boolean> {
|
public async routeNLP(step: GBDialogStep, min: GBMinInstance, text: string): Promise<boolean> {
|
||||||
// Invokes LUIS.
|
// Invokes LUIS.
|
||||||
|
|
||||||
|
const endpoint = min.instance.nlpEndpoint.replace('/luis/v2.0', '');
|
||||||
|
|
||||||
const model = new LuisRecognizer({
|
const model = new LuisRecognizer({
|
||||||
applicationId: min.instance.nlpAppId,
|
applicationId: min.instance.nlpAppId,
|
||||||
endpointKey: min.instance.nlpKey,
|
endpointKey: min.instance.nlpKey,
|
||||||
endpoint: min.instance.nlpEndpoint
|
endpoint: endpoint
|
||||||
});
|
});
|
||||||
|
|
||||||
let nlp: any;
|
let nlp: RecognizerResult;
|
||||||
try {
|
try {
|
||||||
nlp = await model.recognize(step.context);
|
nlp = await model.recognize(step.context);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
if (error.statusCode === 404) {
|
if (error.statusCode === 404) {
|
||||||
logger.warn('NLP application still not publish and there are no other options for answering, please associate the key on NLP portal.');
|
GBLog.warn('NLP application still not publish and there are no other options for answering.');
|
||||||
|
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
} else {
|
} else {
|
||||||
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
|
const msg = `Error calling NLP, check if you have a published model and assigned keys. Error: ${
|
||||||
error.statusCode ? error.statusCode : ''
|
error.statusCode ? error.statusCode : ''
|
||||||
} ${error.message}`;
|
} {error.message; }`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves intents returned from LUIS.
|
// Resolves intents returned from LUIS.
|
||||||
|
|
||||||
const topIntent = LuisRecognizer.topIntent(nlp);
|
const topIntent = LuisRecognizer.topIntent(nlp);
|
||||||
if (topIntent) {
|
if (topIntent !== undefined) {
|
||||||
const intent = topIntent;
|
const intent = topIntent;
|
||||||
const entity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : null;
|
// tslint:disable:no-unsafe-any
|
||||||
|
const firstEntity = nlp.entities && nlp.entities.length > 0 ? nlp.entities[0].entity.toUpperCase() : undefined;
|
||||||
|
// tslint:ensable:no-unsafe-any
|
||||||
|
|
||||||
if (intent === 'None') {
|
if (intent === 'None') {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`NLP called: ${intent}, ${entity}`);
|
GBLog.info(`NLP called: ${intent} ${firstEntity}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await step.replace(`/${intent}`, nlp.entities);
|
await step.replaceDialog(` /${intent}`, nlp.entities);
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -143,7 +154,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkLanguage(step, min, text) {
|
public async checkLanguage(step: GBDialogStep, min, text) {
|
||||||
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text);
|
const locale = await AzureText.getLocale(min.instance.textAnalyticsKey, min.instance.textAnalyticsEndpoint, text);
|
||||||
if (locale !== step.context.activity.locale.split('-')[0]) {
|
if (locale !== step.context.activity.locale.split('-')[0]) {
|
||||||
switch (locale) {
|
switch (locale) {
|
||||||
|
@ -156,7 +167,7 @@ export class GBConversationalService implements IGBConversationalService {
|
||||||
await step.context.sendActivity(Messages[locale].changing_language);
|
await step.context.sendActivity(Messages[locale].changing_language);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
await step.context.sendActivity(`Unknown language: ${locale}`);
|
await step.context.sendActivity(`; Unknown; language: $;{locale;}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,13 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
import { GBLog, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { GBAdminPackage } from '../../admin.gbapp/index';
|
import { GBAdminPackage } from '../../admin.gbapp/index';
|
||||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog';
|
||||||
import { GBCorePackage } from '../../core.gbapp';
|
import { GBCorePackage } from '../../core.gbapp';
|
||||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||||
import { GBKBPackage } from '../../kb.gbapp';
|
import { GBKBPackage } from '../../kb.gbapp';
|
||||||
|
@ -50,9 +50,7 @@ import { GBSecurityPackage } from '../../security.gblib';
|
||||||
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
|
import { GBWhatsappPackage } from '../../whatsapp.gblib/index';
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
import { GuaribasInstance } from '../models/GBModel';
|
||||||
import { GBConfigService } from './GBConfigService';
|
import { GBConfigService } from './GBConfigService';
|
||||||
import { GBImporter } from './GBImporterService';
|
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const opn = require('opn');
|
const opn = require('opn');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,11 +118,11 @@ export class GBCoreService implements IGBCoreService {
|
||||||
throw new Error(`Unknown dialect: ${this.dialect}.`);
|
throw new Error(`Unknown dialect: ${this.dialect}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const logging: any =
|
const logging: boolean | Function =
|
||||||
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
GBConfigService.get('STORAGE_LOGGING') === 'true'
|
||||||
? (str: string): void => {
|
? (str: string): void => {
|
||||||
logger.info(str);
|
GBLog.info(str);
|
||||||
}
|
}
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
|
||||||
|
@ -139,9 +137,10 @@ export class GBCoreService implements IGBCoreService {
|
||||||
dialect: this.dialect,
|
dialect: this.dialect,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
dialectOptions: {
|
dialectOptions: {
|
||||||
encrypt: encrypt
|
options: {
|
||||||
},
|
encrypt: encrypt
|
||||||
pool: {
|
}
|
||||||
|
}, pool: {
|
||||||
max: 32,
|
max: 32,
|
||||||
min: 8,
|
min: 8,
|
||||||
idle: 40000,
|
idle: 40000,
|
||||||
|
@ -152,32 +151,36 @@ export class GBCoreService implements IGBCoreService {
|
||||||
|
|
||||||
if (this.dialect === 'mssql') {
|
if (this.dialect === 'mssql') {
|
||||||
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
|
this.queryGenerator = this.sequelize.getQueryInterface().QueryGenerator;
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
this.createTableQuery = this.queryGenerator.createTableQuery;
|
this.createTableQuery = this.queryGenerator.createTableQuery;
|
||||||
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
|
this.queryGenerator.createTableQuery = (tableName, attributes, options) =>
|
||||||
this.createTableQueryOverride(tableName, attributes, options);
|
this.createTableQueryOverride(tableName, attributes, options);
|
||||||
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
|
this.changeColumnQuery = this.queryGenerator.changeColumnQuery;
|
||||||
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
|
this.queryGenerator.changeColumnQuery = (tableName, attributes) =>
|
||||||
this.changeColumnQueryOverride(tableName, attributes);
|
this.changeColumnQueryOverride(tableName, attributes);
|
||||||
|
// tslint:enable:no-unsafe-any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkStorage(azureDeployer: AzureDeployerService) {
|
public async checkStorage(installationDeployer: IGBInstallationDeployer) {
|
||||||
try {
|
try {
|
||||||
await this.sequelize.authenticate();
|
await this.sequelize.authenticate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.info('Opening storage firewall on infrastructure...');
|
GBLog.info('Opening storage firewall on infrastructure...');
|
||||||
|
// tslint:disable:no-unsafe-any
|
||||||
if (error.parent.code === 'ELOGIN') {
|
if (error.parent.code === 'ELOGIN') {
|
||||||
await this.openStorageFrontier(azureDeployer);
|
await this.openStorageFrontier(installationDeployer);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
// tslint:ensable:no-unsafe-any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async syncDatabaseStructure() {
|
public async syncDatabaseStructure() {
|
||||||
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
|
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
|
||||||
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
|
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
|
||||||
logger.info('Syncing database...');
|
GBLog.info('Syncing database...');
|
||||||
|
|
||||||
return this.sequelize.sync({
|
return this.sequelize.sync({
|
||||||
alter: alter,
|
alter: alter,
|
||||||
|
@ -185,21 +188,21 @@ export class GBCoreService implements IGBCoreService {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const msg = `Database synchronization is disabled.`;
|
const msg = `Database synchronization is disabled.`;
|
||||||
logger.info(msg);
|
GBLog.info(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all items to start several listeners.
|
* Loads all items to start several listeners.
|
||||||
*/
|
*/
|
||||||
public async loadInstances(): Promise<IGBInstance> {
|
public async loadInstances(): Promise<IGBInstance[]> {
|
||||||
return GuaribasInstance.findAll({});
|
return GuaribasInstance.findAll({});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads just one Bot instance by its internal Id.
|
* Loads just one Bot instance by its internal Id.
|
||||||
*/
|
*/
|
||||||
public async loadInstanceById(instanceId: string): Promise<IGBInstance> {
|
public async loadInstanceById(instanceId: number): Promise<IGBInstance> {
|
||||||
const options = { where: { instanceId: instanceId } };
|
const options = { where: { instanceId: instanceId } };
|
||||||
|
|
||||||
return GuaribasInstance.findOne(options);
|
return GuaribasInstance.findOne(options);
|
||||||
|
@ -239,12 +242,19 @@ STORAGE_SYNC=true
|
||||||
|
|
||||||
public async ensureProxy(port): Promise<string> {
|
public async ensureProxy(port): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const ngrok = require('ngrok');
|
if (fs.existsSync('node_modules/ngrok/bin/ngrok.exe')) {
|
||||||
return await ngrok.connect({ port: port });
|
const ngrok = require('ngrok');
|
||||||
|
|
||||||
|
return await ngrok.connect({ port: port });
|
||||||
|
} else {
|
||||||
|
GBLog.warn('ngrok executable not found (only tested on Windows). Check installation or node_modules folder.');
|
||||||
|
|
||||||
|
return 'localhost';
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// There are false positive from ngrok regarding to no memory, but it's just
|
// There are false positive from ngrok regarding to no memory, but it's just
|
||||||
// lack of connection.
|
// lack of connection.
|
||||||
logger.verbose(error);
|
GBLog.verbose(error);
|
||||||
throw new Error('Error connecting to remote ngrok server, please check network connection.');
|
throw new Error('Error connecting to remote ngrok server, please check network connection.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,34 +276,42 @@ STORAGE_SYNC=true
|
||||||
* @param azureDeployer
|
* @param azureDeployer
|
||||||
* @param proxyAddress
|
* @param proxyAddress
|
||||||
*/
|
*/
|
||||||
public async loadAllInstances(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
public async loadAllInstances(
|
||||||
logger.info(`Loading instances from storage...`);
|
core: IGBCoreService,
|
||||||
let instances: GuaribasInstance[];
|
installationDeployer: IGBInstallationDeployer,
|
||||||
|
proxyAddress: string
|
||||||
|
) {
|
||||||
|
GBLog.info(`Loading instances from storage...`);
|
||||||
|
let instances: IGBInstance[];
|
||||||
try {
|
try {
|
||||||
instances = await core.loadInstances();
|
instances = await core.loadInstances();
|
||||||
const instance = instances[0];
|
const instance = instances[0];
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
GBLog.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||||
await azureDeployer.updateBotProxy(
|
await installationDeployer.updateBotProxy(
|
||||||
instance.botId,
|
instance.botId,
|
||||||
instance.botId,
|
instance.botId,
|
||||||
`${proxyAddress}/api/messages/${instance.botId}`
|
`${proxyAddress}/api/messages/${instance.botId}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Check if storage is empty and needs formatting.
|
if (error.parent === undefined) {
|
||||||
const isInvalidObject = error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
|
|
||||||
if (isInvalidObject) {
|
|
||||||
if (GBConfigService.get('STORAGE_SYNC') != 'true') {
|
|
||||||
throw new Error(
|
|
||||||
`Operating storage is out of sync or there is a storage connection error.
|
|
||||||
Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logger.info(`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
|
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
|
||||||
|
} else {
|
||||||
|
// Check if storage is empty and needs formatting.
|
||||||
|
const isInvalidObject = error.parent.number === 208 || error.parent.errno === 1; // MSSQL or SQLITE.
|
||||||
|
if (isInvalidObject) {
|
||||||
|
if (GBConfigService.get('STORAGE_SYNC') !== 'true') {
|
||||||
|
throw new Error(
|
||||||
|
`Operating storage is out of sync or there is a storage connection error.
|
||||||
|
Try setting STORAGE_SYNC to true in .env file. Error: ${error.message}.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
GBLog.info(`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,10 +325,10 @@ STORAGE_SYNC=true
|
||||||
* @param bootInstance
|
* @param bootInstance
|
||||||
* @param core
|
* @param core
|
||||||
*/
|
*/
|
||||||
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
|
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {
|
||||||
if (!instances) {
|
if (instances === undefined) {
|
||||||
const saveInstance = new GuaribasInstance(bootInstance);
|
const instance = new GuaribasInstance();
|
||||||
await saveInstance.save();
|
await instance.save();
|
||||||
instances = await core.loadInstances();
|
instances = await core.loadInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,10 +346,10 @@ STORAGE_SYNC=true
|
||||||
GBCorePackage,
|
GBCorePackage,
|
||||||
GBSecurityPackage,
|
GBSecurityPackage,
|
||||||
GBKBPackage,
|
GBKBPackage,
|
||||||
GBCustomerSatisfactionPackage,
|
GBCustomerSatisfactionPackage
|
||||||
GBWhatsappPackage
|
// GBWhatsappPackage
|
||||||
].forEach(e => {
|
].forEach(e => {
|
||||||
logger.info(`Loading sys package: ${e.name}...`);
|
GBLog.info(`Loading sys package: ${e.name}...`);
|
||||||
const p = Object.create(e.prototype) as IGBPackage;
|
const p = Object.create(e.prototype) as IGBPackage;
|
||||||
p.loadPackage(core, core.sequelize);
|
p.loadPackage(core, core.sequelize);
|
||||||
});
|
});
|
||||||
|
@ -346,23 +364,32 @@ STORAGE_SYNC=true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
public async createBootInstance(
|
||||||
let instance: IGBInstance;
|
core: GBCoreService,
|
||||||
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
installationDeployer: IGBInstallationDeployer,
|
||||||
|
proxyAddress: string
|
||||||
|
) {
|
||||||
|
GBLog.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||||
try {
|
try {
|
||||||
instance = await azureDeployer.deployFarm(proxyAddress);
|
const { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance(installationDeployer);
|
||||||
|
const changedInstance = await installationDeployer.deployFarm(
|
||||||
|
proxyAddress,
|
||||||
|
instance,
|
||||||
|
credentials,
|
||||||
|
subscriptionId
|
||||||
|
);
|
||||||
|
core.writeEnv(changedInstance);
|
||||||
|
GBLog.info(`File .env written, starting General Bots...`);
|
||||||
|
GBConfigService.init();
|
||||||
|
|
||||||
|
return changedInstance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(
|
GBLog.warn(
|
||||||
`In case of error, please cleanup any infrastructure objects
|
`In case of error, please cleanup any infrastructure objects
|
||||||
created during this procedure and .env before running again.`
|
created during this procedure and .env before running again.`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
core.writeEnv(instance);
|
|
||||||
logger.info(`File .env written, starting General Bots...`);
|
|
||||||
GBConfigService.init();
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openBrowserInDevelopment() {
|
public openBrowserInDevelopment() {
|
||||||
|
@ -390,13 +417,13 @@ STORAGE_SYNC=true
|
||||||
let sql: string = this.createTableQuery.apply(this.queryGenerator, [tableName, attributes, options]);
|
let sql: string = this.createTableQuery.apply(this.queryGenerator, [tableName, attributes, options]);
|
||||||
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
|
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
|
||||||
const matches = re1.exec(sql);
|
const matches = re1.exec(sql);
|
||||||
if (matches) {
|
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(
|
sql = sql.replace(
|
||||||
re2,
|
re2,
|
||||||
(match: string, ...args: any[]): string => {
|
(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;
|
||||||
|
@ -406,15 +433,17 @@ STORAGE_SYNC=true
|
||||||
(match: string, ...args: any[]): string => {
|
(match: string, ...args: any[]): string => {
|
||||||
const fkcols = args[0];
|
const fkcols = args[0];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re4.exec(fkcols);
|
let matches2 = re4.exec(fkcols);
|
||||||
while (matches != null) {
|
while (matches2 !== null) {
|
||||||
fkname += '_' + matches[1];
|
fkname += `_${matches2[1]}`;
|
||||||
matches = re4.exec(fkcols);
|
matches2 = re4.exec(fkcols);
|
||||||
}
|
}
|
||||||
return 'CONSTRAINT [' + fkname + '_fk] FOREIGN KEY (' + fkcols + ')';
|
|
||||||
|
return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +459,7 @@ STORAGE_SYNC=true
|
||||||
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]);
|
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]);
|
||||||
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
|
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
|
||||||
const matches = re1.exec(sql);
|
const matches = re1.exec(sql);
|
||||||
if (matches) {
|
if (matches !== null) {
|
||||||
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;
|
||||||
|
@ -439,15 +468,17 @@ STORAGE_SYNC=true
|
||||||
(match: string, ...args: any[]): string => {
|
(match: string, ...args: any[]): string => {
|
||||||
const fkcols = args[2];
|
const fkcols = args[2];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re3.exec(fkcols);
|
let matches2 = re3.exec(fkcols);
|
||||||
while (matches != null) {
|
while (matches2 !== null) {
|
||||||
fkname += '_' + matches[1];
|
fkname += `_${matches2[1]}`;
|
||||||
matches = re3.exec(fkcols);
|
matches2 = re3.exec(fkcols);
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,9 +487,9 @@ STORAGE_SYNC=true
|
||||||
*
|
*
|
||||||
* @param azureDeployer Infrastructure Deployer instance.
|
* @param azureDeployer Infrastructure Deployer instance.
|
||||||
*/
|
*/
|
||||||
private async openStorageFrontier(deployer: AzureDeployerService) {
|
private async openStorageFrontier(installationDeployer: IGBInstallationDeployer) {
|
||||||
const group = GBConfigService.get('CLOUD_GROUP');
|
const group = GBConfigService.get('CLOUD_GROUP');
|
||||||
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
|
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
|
||||||
await deployer.openStorageFirewall(group, serverName);
|
await installationDeployer.openStorageFirewall(group, serverName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,24 +36,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
const WaitUntil = require('wait-until');
|
const WaitUntil = require('wait-until');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const graph = require('@microsoft/microsoft-graph-client');
|
const graph = require('@microsoft/microsoft-graph-client');
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
|
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import { GBError, IGBPackage } from 'botlib';
|
|
||||||
import { AzureSearch } from 'pragmatismo-io-framework';
|
import { AzureSearch } from 'pragmatismo-io-framework';
|
||||||
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
import { GuaribasPackage } from '../models/GBModel';
|
||||||
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel';
|
|
||||||
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
|
||||||
import { KBService } from './../../kb.gbapp/services/KBService';
|
import { KBService } from './../../kb.gbapp/services/KBService';
|
||||||
import { GBConfigService } from './GBConfigService';
|
import { GBConfigService } from './GBConfigService';
|
||||||
import { GBCoreService } from './GBCoreService';
|
|
||||||
import { GBImporter } from './GBImporterService';
|
import { GBImporter } from './GBImporterService';
|
||||||
import { GBVMService } from './GBVMService';
|
import { GBVMService } from './GBVMService';
|
||||||
|
|
||||||
|
@ -73,8 +69,8 @@ export class GBDeployer {
|
||||||
this.importer = importer;
|
this.importer = importer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getConnectionStringFromInstance(instance: GuaribasInstance) {
|
public static getConnectionStringFromInstance(instance: IGBInstance) {
|
||||||
return `Server=tcp:${instance.storageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
return `Server=tcp:torageServer}.database.windows.net,1433;Database=${instance.storageName};User ID=${
|
||||||
instance.storageUsername
|
instance.storageUsername
|
||||||
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
|
};Password=${instance.storagePassword};Trusted_Connection=False;Encrypt=True;Connection Timeout=30;`;
|
||||||
}
|
}
|
||||||
|
@ -92,12 +88,12 @@ export class GBDeployer {
|
||||||
let totalPackages = 0;
|
let totalPackages = 0;
|
||||||
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
|
||||||
let paths = [GBDeployer.deployFolder];
|
let paths = [GBDeployer.deployFolder];
|
||||||
if (additionalPath) {
|
if (additionalPath !== undefined && additionalPath !== '') {
|
||||||
paths = paths.concat(additionalPath.toLowerCase().split(';'));
|
paths = paths.concat(additionalPath.toLowerCase().split(';'));
|
||||||
}
|
}
|
||||||
const botPackages = new Array<string>();
|
const botPackages: string[] = [];
|
||||||
const gbappPackages = new Array<string>();
|
const gbappPackages: string[] = [];
|
||||||
let generalPackages = new Array<string>();
|
let generalPackages: string[] = [];
|
||||||
|
|
||||||
function doIt(path) {
|
function doIt(path) {
|
||||||
const isDirectory = source => Fs.lstatSync(source).isDirectory();
|
const isDirectory = source => Fs.lstatSync(source).isDirectory();
|
||||||
|
@ -109,7 +105,7 @@ export class GBDeployer {
|
||||||
const dirs = getDirectories(path);
|
const dirs = getDirectories(path);
|
||||||
dirs.forEach(element => {
|
dirs.forEach(element => {
|
||||||
if (element.startsWith('.')) {
|
if (element.startsWith('.')) {
|
||||||
logger.info(`Ignoring ${element}...`);
|
GBLog.info(`Ignoring ${element}...`);
|
||||||
} else {
|
} else {
|
||||||
if (element.endsWith('.gbot')) {
|
if (element.endsWith('.gbot')) {
|
||||||
botPackages.push(element);
|
botPackages.push(element);
|
||||||
|
@ -122,9 +118,9 @@ export class GBDeployer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`);
|
GBLog.info(`Starting looking for packages (.gbot, .gbtheme, .gbkb, .gbapp)...`);
|
||||||
paths.forEach(e => {
|
paths.forEach(e => {
|
||||||
logger.info(`Looking in: ${e}...`);
|
GBLog.info(`Looking in: ${e}...`);
|
||||||
doIt(e);
|
doIt(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -136,11 +132,11 @@ export class GBDeployer {
|
||||||
.interval(1000)
|
.interval(1000)
|
||||||
.times(10)
|
.times(10)
|
||||||
.condition(cb => {
|
.condition(cb => {
|
||||||
logger.info(`Waiting for app package deployment...`);
|
GBLog.info(`Waiting for app package deployment...`);
|
||||||
cb(appPackagesProcessed === gbappPackages.length);
|
cb(appPackagesProcessed === gbappPackages.length);
|
||||||
})
|
})
|
||||||
.done(async result => {
|
.done(async () => {
|
||||||
logger.info(`App Package deployment done.`);
|
GBLog.info(`App Package deployment done.`);
|
||||||
|
|
||||||
({ generalPackages, totalPackages } = await this.deployDataPackages(
|
({ generalPackages, totalPackages } = await this.deployDataPackages(
|
||||||
core,
|
core,
|
||||||
|
@ -162,11 +158,9 @@ export class GBDeployer {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public async deployBot(localPath: string): Promise<IGBInstance> {
|
public async deployBot(localPath: string): Promise<IGBInstance> {
|
||||||
const packageType = Path.extname(localPath);
|
|
||||||
const packageName = Path.basename(localPath);
|
const packageName = Path.basename(localPath);
|
||||||
const instance = await this.importer.importIfNotExistsBotPackage(null, packageName, localPath);
|
|
||||||
|
|
||||||
return instance;
|
return await this.importer.importIfNotExistsBotPackage(undefined, packageName, localPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
public async deployPackageToStorage(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
||||||
|
@ -176,7 +170,7 @@ export class GBDeployer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployFromSharePoint(instanceId: number, path: string) {
|
public async deployFromSharePoint(instanceId: number) {
|
||||||
const adminService = new GBAdminService(this.core);
|
const adminService = new GBAdminService(this.core);
|
||||||
const accessToken = adminService.acquireElevatedToken(instanceId);
|
const accessToken = adminService.acquireElevatedToken(instanceId);
|
||||||
|
|
||||||
|
@ -184,57 +178,30 @@ export class GBDeployer {
|
||||||
|
|
||||||
const client = graph.Client.init({
|
const client = graph.Client.init({
|
||||||
authProvider: done => {
|
authProvider: done => {
|
||||||
done(null, accessToken);
|
done(undefined, accessToken);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const events = await client
|
|
||||||
.api('/me/events')
|
|
||||||
.select('subject,organizer,start,end')
|
|
||||||
.orderby('createdDateTime DESC')
|
|
||||||
.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public deployScriptToStorage(instanceId: number, localPath: string) {}
|
public async deployPackage(min: GBMinInstance, localPath: string) {
|
||||||
|
|
||||||
public deployTheme(localPath: string) {
|
|
||||||
// DISABLED: Until completed, "/ui/public".
|
|
||||||
// FsExtra.copy(localPath, this.workDir + packageName)
|
|
||||||
// .then(() => {
|
|
||||||
// })
|
|
||||||
// .catch(err => {
|
|
||||||
// var gberr = GBError.create(
|
|
||||||
// `GuaribasBusinessError: Error copying package: ${localPath}.`
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
public async deployPackageFromSharePoint(min: GBMinInstance, path: string) {}
|
|
||||||
|
|
||||||
public async deployPackageFromLocalPath(min: GBMinInstance, localPath: string) {
|
|
||||||
const packageType = Path.extname(localPath);
|
const packageType = Path.extname(localPath);
|
||||||
|
|
||||||
switch (packageType) {
|
switch (packageType) {
|
||||||
case '.gbot':
|
case '.gbot':
|
||||||
return this.deployBot(localPath);
|
return this.deployBot(localPath);
|
||||||
|
|
||||||
case '.gbtheme':
|
|
||||||
return this.deployTheme(localPath);
|
|
||||||
|
|
||||||
// PACKAGE: Put in package logic.
|
|
||||||
case '.gbkb':
|
case '.gbkb':
|
||||||
const service = new KBService(this.core.sequelize);
|
const service = new KBService(this.core.sequelize);
|
||||||
return service.deployKb(this.core, this, localPath);
|
|
||||||
|
|
||||||
case '.gbui':
|
return service.deployKb(this.core, this, localPath);
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbdialog':
|
case '.gbdialog':
|
||||||
const vm = new GBVMService();
|
const vm = new GBVMService();
|
||||||
return vm.loadJS(localPath, min, this.core, this, localPath);
|
|
||||||
|
return vm.loadDialogPackage(localPath, min, this.core, this);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||||
Promise.reject(err);
|
Promise.reject(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -247,16 +214,9 @@ export class GBDeployer {
|
||||||
const p = await this.getPackageByName(instance.instanceId, packageName);
|
const p = await this.getPackageByName(instance.instanceId, packageName);
|
||||||
|
|
||||||
switch (packageType) {
|
switch (packageType) {
|
||||||
case '.gbot':
|
|
||||||
// TODO: this.undeployBot(packageName, localPath)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbtheme':
|
|
||||||
// TODO: this.undeployTheme(packageName, localPath)
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '.gbkb':
|
case '.gbkb':
|
||||||
const service = new KBService(this.core.sequelize);
|
const service = new KBService(this.core.sequelize);
|
||||||
|
|
||||||
return service.undeployKbFromStorage(instance, this, p.packageId);
|
return service.undeployKbFromStorage(instance, this, p.packageId);
|
||||||
|
|
||||||
case '.gbui':
|
case '.gbui':
|
||||||
|
@ -266,13 +226,13 @@ export class GBDeployer {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
const err = GBError.create(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
const err = GBError.create(`Unhandled package type: ${packageType}.`);
|
||||||
Promise.reject(err);
|
Promise.reject(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async rebuildIndex(instance: GuaribasInstance) {
|
public async rebuildIndex(instance: IGBInstance, searchSchema: any) {
|
||||||
const search = new AzureSearch(
|
const search = new AzureSearch(
|
||||||
instance.searchKey,
|
instance.searchKey,
|
||||||
instance.searchHost,
|
instance.searchHost,
|
||||||
|
@ -286,7 +246,7 @@ export class GBDeployer {
|
||||||
try {
|
try {
|
||||||
await search.deleteDataSource(dsName);
|
await search.deleteDataSource(dsName);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code != 404) {
|
if (err.code !== 404) {
|
||||||
// First time, nothing to delete.
|
// First time, nothing to delete.
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -297,25 +257,26 @@ export class GBDeployer {
|
||||||
try {
|
try {
|
||||||
await search.deleteIndex();
|
await search.deleteIndex();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code != 404) {
|
if (err.code !== 404) {
|
||||||
// First time, nothing to delete.
|
// First time, nothing to delete.
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await search.createIndex(AzureDeployerService.getKBSearchSchema(instance.searchIndex), dsName);
|
await search.createIndex(searchSchema, dsName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPackageByName(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
public async getPackageByName(instanceId: number, packageName: string): Promise<GuaribasPackage> {
|
||||||
const where = { packageName: packageName, instanceId: instanceId };
|
const where = { packageName: packageName, instanceId: instanceId };
|
||||||
|
|
||||||
return GuaribasPackage.findOne({
|
return GuaribasPackage.findOne({
|
||||||
where: where
|
where: where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public installDefaultGBUI() {
|
public runOnce() {
|
||||||
const root = 'packages/default.gbui';
|
const root = 'packages/default.gbui';
|
||||||
if (!Fs.existsSync(`${root}/build`)) {
|
if (!Fs.existsSync(`${root}/build`)) {
|
||||||
logger.info(`Preparing default.gbui (it may take some additional time for the first time)...`);
|
GBLog.info(`Preparing default.gbui (it may take some additional time for the first time)...`);
|
||||||
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
|
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
|
||||||
child_process.execSync('npm install', { cwd: root });
|
child_process.execSync('npm install', { cwd: root });
|
||||||
child_process.execSync('npm run build', { cwd: root });
|
child_process.execSync('npm run build', { cwd: root });
|
||||||
|
@ -323,7 +284,7 @@ export class GBDeployer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deployDataPackages(
|
private async deployDataPackages(
|
||||||
core: GBCoreService,
|
core: IGBCoreService,
|
||||||
botPackages: string[],
|
botPackages: string[],
|
||||||
_this: this,
|
_this: this,
|
||||||
generalPackages: string[],
|
generalPackages: string[],
|
||||||
|
@ -342,9 +303,9 @@ export class GBDeployer {
|
||||||
|
|
||||||
botPackages.forEach(e => {
|
botPackages.forEach(e => {
|
||||||
if (e !== 'packages\\boot.gbot') {
|
if (e !== 'packages\\boot.gbot') {
|
||||||
logger.info(`Deploying bot: ${e}...`);
|
GBLog.info(`Deploying bot: ${e}...`);
|
||||||
_this.deployBot(e);
|
_this.deployBot(e);
|
||||||
logger.info(`Bot: ${e} deployed...`);
|
GBLog.info(`Bot: ${e} deployed...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -353,18 +314,18 @@ export class GBDeployer {
|
||||||
generalPackages = generalPackages.filter(p => !p.endsWith('.git'));
|
generalPackages = generalPackages.filter(p => !p.endsWith('.git'));
|
||||||
generalPackages.forEach(filename => {
|
generalPackages.forEach(filename => {
|
||||||
const filenameOnly = Path.basename(filename);
|
const filenameOnly = Path.basename(filename);
|
||||||
logger.info(`Deploying package: ${filename}...`);
|
GBLog.info(`Deploying package: ${filename}...`);
|
||||||
|
|
||||||
// Handles apps for general bots - .gbapp must stay out of deploy folder.
|
// Handles apps for general bots - .gbapp must stay out of deploy folder.
|
||||||
|
|
||||||
if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') {
|
if (Path.extname(filename) === '.gbapp' || Path.extname(filename) === '.gblib') {
|
||||||
// Themes for bots.
|
// Themes for bots.
|
||||||
} else if (Path.extname(filename) === '.gbtheme') {
|
} else if (Path.extname(filename) === '.gbtheme') {
|
||||||
server.use('/themes/' + filenameOnly, express.static(filename));
|
server.use(`/themes/${filenameOnly}`, express.static(filename));
|
||||||
logger.info(`Theme (.gbtheme) assets accessible at: ${'/themes/' + filenameOnly}.`);
|
GBLog.info(`Theme (.gbtheme) assets accessible at: /themes/${filenameOnly}.`);
|
||||||
} else if (Path.extname(filename) === '.gbkb') {
|
} else if (Path.extname(filename) === '.gbkb') {
|
||||||
server.use('/kb/' + filenameOnly + '/subjects', express.static(UrlJoin(filename, 'subjects')));
|
server.use(`/kb/${filenameOnly}/subjects`, express.static(urlJoin(filename, 'subjects')));
|
||||||
logger.info(`KB (.gbkb) assets accessible at: ${'/kb/' + filenameOnly}.`);
|
GBLog.info(`KB (.gbkb) assets accessible at: /kb/${filenameOnly}.`);
|
||||||
} else if (Path.extname(filename) === '.gbui') {
|
} else if (Path.extname(filename) === '.gbui') {
|
||||||
// Already Handled
|
// Already Handled
|
||||||
} else if (Path.extname(filename) === '.gbdialog') {
|
} else if (Path.extname(filename) === '.gbdialog') {
|
||||||
|
@ -381,14 +342,14 @@ export class GBDeployer {
|
||||||
.interval(100)
|
.interval(100)
|
||||||
.times(5)
|
.times(5)
|
||||||
.condition(cb => {
|
.condition(cb => {
|
||||||
logger.info(`Waiting for package deployment...`);
|
GBLog.info(`Waiting for package deployment...`);
|
||||||
cb(totalPackages === generalPackages.length);
|
cb(totalPackages === generalPackages.length);
|
||||||
})
|
})
|
||||||
.done(result => {
|
.done(() => {
|
||||||
if (botPackages.length === 0) {
|
if (botPackages.length === 0) {
|
||||||
logger.info('Use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder (no external packages).');
|
GBLog.info('Use ADDITIONAL_DEPLOY_PATH to point to a .gbai package folder (no external packages).');
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Package deployment done.`);
|
GBLog.info(`Package deployment done.`);
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -401,17 +362,17 @@ export class GBDeployer {
|
||||||
gbappPackages.forEach(e => {
|
gbappPackages.forEach(e => {
|
||||||
// Skips .gbapp inside deploy folder.
|
// Skips .gbapp inside deploy folder.
|
||||||
if (!e.startsWith('packages')) {
|
if (!e.startsWith('packages')) {
|
||||||
logger.info(`Deploying app: ${e}...`);
|
GBLog.info(`Deploying app: ${e}...`);
|
||||||
|
|
||||||
let folder = Path.join(e, 'node_modules');
|
let folder = Path.join(e, 'node_modules');
|
||||||
if (!Fs.existsSync(folder)) {
|
if (!Fs.existsSync(folder)) {
|
||||||
logger.info(`Installing modules for ${e}...`);
|
GBLog.info(`Installing modules for ${e}...`);
|
||||||
child_process.execSync('npm install', { cwd: e });
|
child_process.execSync('npm install', { cwd: e });
|
||||||
}
|
}
|
||||||
|
|
||||||
folder = Path.join(e, 'dist');
|
folder = Path.join(e, 'dist');
|
||||||
if (!Fs.existsSync()) {
|
if (!Fs.existsSync()) {
|
||||||
logger.info(`Compiling ${e}...`);
|
GBLog.info(`Compiling ${e}...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
|
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
|
||||||
|
@ -420,15 +381,15 @@ export class GBDeployer {
|
||||||
const p = new m.Package();
|
const p = new m.Package();
|
||||||
p.loadPackage(core, core.sequelize);
|
p.loadPackage(core, core.sequelize);
|
||||||
appPackages.push(p);
|
appPackages.push(p);
|
||||||
logger.info(`App (.gbapp) deployed: ${e}.`);
|
GBLog.info(`App (.gbapp) deployed: ${e}.`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
GBLog.error(`Error deploying .gbapp package: ${e}\n${err}`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error compiling .gbapp package ${e}:\n${error.stdout.toString()}`);
|
GBLog.error(`Error compiling .gbapp package ${e}:\n${error.stdout.toString()}`);
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,6 +397,7 @@ export class GBDeployer {
|
||||||
appPackagesProcessed++;
|
appPackagesProcessed++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return appPackagesProcessed;
|
return appPackagesProcessed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,13 +36,14 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { IGBCoreService } from 'botlib';
|
||||||
import { IGBCoreService, IGBInstance } from 'botlib';
|
|
||||||
import fs = require('fs');
|
import fs = require('fs');
|
||||||
import path = require('path');
|
import urlJoin = require('url-join');
|
||||||
import { SecService } from '../../security.gblib/services/SecService';
|
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
import { GuaribasInstance } from '../models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the importing of packages.
|
||||||
|
*/
|
||||||
export class GBImporter {
|
export class GBImporter {
|
||||||
public core: IGBCoreService;
|
public core: IGBCoreService;
|
||||||
|
|
||||||
|
@ -51,25 +52,25 @@ export class GBImporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) {
|
public async importIfNotExistsBotPackage(botId: string, packageName: string, localPath: string) {
|
||||||
const packageJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8'));
|
const packageJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||||
if (!botId) {
|
if (botId === undefined) {
|
||||||
botId = packageJson.botId;
|
botId = packageJson.botId;
|
||||||
}
|
}
|
||||||
const instance = await this.core.loadInstance(botId);
|
const instance = await this.core.loadInstance(botId);
|
||||||
if (instance) {
|
if (instance !== null) {
|
||||||
return instance;
|
return instance;
|
||||||
} else {
|
} else {
|
||||||
return await this.createInstanceInternal(botId, packageName, localPath, packageJson);
|
return await this.createInstanceInternal(botId, localPath, packageJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createInstanceInternal(botId: string, packageName: string, localPath: string, packageJson: any) {
|
private async createInstanceInternal(botId: string, localPath: string, packageJson: any) {
|
||||||
const settings = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'settings.json'), 'utf8'));
|
const settings = JSON.parse(fs.readFileSync(urlJoin(localPath, 'settings.json'), 'utf8'));
|
||||||
const servicesJson = JSON.parse(fs.readFileSync(UrlJoin(localPath, 'services.json'), 'utf8'));
|
const servicesJson = JSON.parse(fs.readFileSync(urlJoin(localPath, 'services.json'), 'utf8'));
|
||||||
|
|
||||||
packageJson = { ...packageJson, ...settings, ...servicesJson };
|
packageJson = { ...packageJson, ...settings, ...servicesJson };
|
||||||
|
|
||||||
if (botId){
|
if (botId !== undefined) {
|
||||||
packageJson.botId = botId;
|
packageJson.botId = botId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,29 +37,40 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
|
const { DialogSet, TextPrompt } = require('botbuilder-dialogs');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const request = require('request-promise-native');
|
const request = require('request-promise-native');
|
||||||
const AuthenticationContext = require('adal-node').AuthenticationContext;
|
const AuthenticationContext = require('adal-node').AuthenticationContext;
|
||||||
|
|
||||||
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
||||||
|
|
||||||
import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib';
|
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
|
import {
|
||||||
|
GBDialogStep,
|
||||||
|
GBLog,
|
||||||
|
GBMinInstance,
|
||||||
|
IGBAdminService,
|
||||||
|
IGBConversationalService,
|
||||||
|
IGBCoreService,
|
||||||
|
IGBInstance,
|
||||||
|
IGBPackage
|
||||||
|
} from 'botlib';
|
||||||
|
|
||||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||||
import { GBCorePackage } from '../../core.gbapp';
|
import { GBCorePackage } from '../../core.gbapp';
|
||||||
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp';
|
||||||
import { GBKBPackage } from '../../kb.gbapp';
|
import { GBKBPackage } from '../../kb.gbapp';
|
||||||
|
import { AskDialogArgs } from '../../kb.gbapp/dialogs/AskDialog';
|
||||||
import { GBSecurityPackage } from '../../security.gblib';
|
import { GBSecurityPackage } from '../../security.gblib';
|
||||||
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
import { GBWhatsappPackage } from '../../whatsapp.gblib';
|
||||||
import { GuaribasInstance } from '../models/GBModel';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { GBAdminPackage } from './../../admin.gbapp/index';
|
import { GBAdminPackage } from './../../admin.gbapp/index';
|
||||||
import { GBDeployer } from './GBDeployer';
|
import { GBDeployer } from './GBDeployer';
|
||||||
import { ConfirmPrompt } from 'botbuilder-dialogs';
|
|
||||||
|
|
||||||
/** Minimal service layer for a bot. */
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal service layer for a bot.
|
||||||
|
*/
|
||||||
export class GBMinService {
|
export class GBMinService {
|
||||||
public core: IGBCoreService;
|
public core: IGBCoreService;
|
||||||
public conversationalService: IGBConversationalService;
|
public conversationalService: IGBConversationalService;
|
||||||
|
@ -94,21 +105,21 @@ export class GBMinService {
|
||||||
*
|
*
|
||||||
* @return Loaded minimal bot instance.
|
* @return Loaded minimal bot instance.
|
||||||
*
|
*
|
||||||
* */
|
*/
|
||||||
|
|
||||||
public async buildMin(
|
public async buildMin(
|
||||||
bootInstance: GuaribasInstance,
|
bootInstance: IGBInstance,
|
||||||
server: any,
|
server: any,
|
||||||
appPackages: IGBPackage[],
|
appPackages: IGBPackage[],
|
||||||
instances: GuaribasInstance[],
|
instances: IGBInstance[],
|
||||||
deployer: GBDeployer
|
deployer: GBDeployer
|
||||||
): Promise<GBMinInstance> {
|
) {
|
||||||
// Serves default UI on root address '/'.
|
// Serves default UI on root address '/'.
|
||||||
|
|
||||||
const uiPackage = 'default.gbui';
|
const uiPackage = 'default.gbui';
|
||||||
server.use('/', express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
server.use('/', express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||||
|
|
||||||
Promise.all(
|
await Promise.all(
|
||||||
instances.map(async instance => {
|
instances.map(async instance => {
|
||||||
// Gets the authorization key for each instance from Bot Service.
|
// Gets the authorization key for each instance from Bot Service.
|
||||||
|
|
||||||
|
@ -119,39 +130,7 @@ export class GBMinService {
|
||||||
|
|
||||||
server.get('/instances/:botId', (req, res) => {
|
server.get('/instances/:botId', (req, res) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
// Returns the instance object to clients requesting bot info.
|
await this.sendInstanceToClient(req, bootInstance, res, webchatToken);
|
||||||
|
|
||||||
let botId = req.params.botId;
|
|
||||||
if (botId === '[default]') {
|
|
||||||
botId = bootInstance.botId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = await this.core.loadInstance(botId);
|
|
||||||
|
|
||||||
if (instance) {
|
|
||||||
const speechToken = await this.getSTSToken(instance);
|
|
||||||
let theme = instance.theme;
|
|
||||||
if (!theme) {
|
|
||||||
theme = 'default.gbtheme';
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(
|
|
||||||
JSON.stringify({
|
|
||||||
instanceId: instance.instanceId,
|
|
||||||
botId: botId,
|
|
||||||
theme: theme,
|
|
||||||
secret: instance.webchatKey, // TODO: Use token.
|
|
||||||
speechToken: speechToken,
|
|
||||||
conversationId: webchatToken.conversationId,
|
|
||||||
authenticatorTenant: instance.authenticatorTenant,
|
|
||||||
authenticatorClientId: instance.authenticatorClientId
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const error = `Instance not found: ${botId}.`;
|
|
||||||
res.sendStatus(error);
|
|
||||||
logger.error(error);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -161,7 +140,7 @@ export class GBMinService {
|
||||||
|
|
||||||
// Install default VBA module.
|
// Install default VBA module.
|
||||||
|
|
||||||
deployer.deployPackageFromLocalPath(min, 'packages/default.gbdialog');
|
// DISABLED: deployer.deployPackage(min, 'packages/default.gbdialog');
|
||||||
|
|
||||||
// Call the loadBot context.activity for all packages.
|
// Call the loadBot context.activity for all packages.
|
||||||
|
|
||||||
|
@ -171,86 +150,122 @@ export class GBMinService {
|
||||||
|
|
||||||
const url = `/api/messages/${instance.botId}`;
|
const url = `/api/messages/${instance.botId}`;
|
||||||
server.post(url, async (req, res) => {
|
server.post(url, async (req, res) => {
|
||||||
return await this.receiver(adapter, req, res, conversationState, min, instance, appPackages);
|
await this.receiver(adapter, req, res, conversationState, min, instance, appPackages);
|
||||||
});
|
});
|
||||||
logger.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
GBLog.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
||||||
|
|
||||||
// Serves individual URL for each bot user interface.
|
// Serves individual URL for each bot user interface.
|
||||||
|
|
||||||
const uiUrl = `/${instance.botId}`;
|
const uiUrl = `/${instance.botId}`;
|
||||||
server.use(uiUrl, express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
server.use(uiUrl, express.static(urlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||||
|
|
||||||
logger.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
|
GBLog.info(`Bot UI ${uiPackage} accessible at: ${uiUrl}.`);
|
||||||
const state = `${instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
|
|
||||||
|
|
||||||
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
|
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
|
||||||
// There they will authenticate and give their consent to allow this app access to
|
// There they will authenticate and give their consent to allow this app access to
|
||||||
// some resource they own.
|
// some resource they own.
|
||||||
server.get(`/${min.instance.botId}/auth`, function(req, res) {
|
|
||||||
let authorizationUrl = UrlJoin(
|
|
||||||
min.instance.authenticatorAuthorityHostUrl,
|
|
||||||
min.instance.authenticatorTenant,
|
|
||||||
'/oauth2/authorize'
|
|
||||||
);
|
|
||||||
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
|
||||||
min.instance.authenticatorClientId
|
|
||||||
}&redirect_uri=${UrlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
|
|
||||||
|
|
||||||
res.redirect(authorizationUrl);
|
this.handleOAuthRequests(server, min);
|
||||||
});
|
|
||||||
|
|
||||||
// After consent is granted AAD redirects here. The ADAL library
|
// After consent is granted AAD redirects here. The ADAL library
|
||||||
// is invoked via the AuthenticationContext and retrieves an
|
// is invoked via the AuthenticationContext and retrieves an
|
||||||
// access token that can be used to access the user owned resource.
|
// access token that can be used to access the user owned resource.
|
||||||
|
|
||||||
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
this.handleOAuthTokenRequests(server, min, instance);
|
||||||
const state = await min.adminService.getValue(min.instance.instanceId, 'AntiCSRFAttackState');
|
|
||||||
|
|
||||||
if (req.query.state !== state) {
|
|
||||||
const msg = 'WARNING: state field was not provided as anti-CSRF token';
|
|
||||||
logger.error(msg);
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const authenticationContext = new AuthenticationContext(
|
|
||||||
UrlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
|
||||||
);
|
|
||||||
|
|
||||||
const resource = 'https://graph.microsoft.com';
|
|
||||||
|
|
||||||
authenticationContext.acquireTokenWithAuthorizationCode(
|
|
||||||
req.query.code,
|
|
||||||
UrlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
|
||||||
resource,
|
|
||||||
instance.authenticatorClientId,
|
|
||||||
instance.authenticatorClientSecret,
|
|
||||||
async (err, token) => {
|
|
||||||
if (err) {
|
|
||||||
const msg = `Error acquiring token: ${err}`;
|
|
||||||
logger.error(msg);
|
|
||||||
res.send(msg);
|
|
||||||
} else {
|
|
||||||
await this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
|
||||||
await this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
|
||||||
await this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
|
||||||
await this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', null);
|
|
||||||
|
|
||||||
res.redirect(min.instance.botEndpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) {
|
||||||
|
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
||||||
|
const state = await min.adminService.getValue(instance.instanceId, 'AntiCSRFAttackState');
|
||||||
|
if (req.query.state !== state) {
|
||||||
|
const msg = 'WARNING: state field was not provided as anti-CSRF token';
|
||||||
|
GBLog.error(msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
const authenticationContext = new AuthenticationContext(
|
||||||
|
urlJoin(min.instance.authenticatorAuthorityHostUrl, min.instance.authenticatorTenant)
|
||||||
|
);
|
||||||
|
const resource = 'https://graph.microsoft.com';
|
||||||
|
authenticationContext.acquireTokenWithAuthorizationCode(
|
||||||
|
req.query.code,
|
||||||
|
urlJoin(instance.botEndpoint, min.instance.botId, '/token'),
|
||||||
|
resource,
|
||||||
|
instance.authenticatorClientId,
|
||||||
|
instance.authenticatorClientSecret,
|
||||||
|
async (err, token) => {
|
||||||
|
if (err) {
|
||||||
|
const msg = `Error acquiring token: ${err}`;
|
||||||
|
GBLog.error(msg);
|
||||||
|
res.send(msg);
|
||||||
|
} else {
|
||||||
|
this.adminService.setValue(instance.instanceId, 'refreshToken', token.refreshToken);
|
||||||
|
this.adminService.setValue(instance.instanceId, 'accessToken', token.accessToken);
|
||||||
|
this.adminService.setValue(instance.instanceId, 'expiresOn', token.expiresOn.toString());
|
||||||
|
this.adminService.setValue(instance.instanceId, 'AntiCSRFAttackState', undefined);
|
||||||
|
res.redirect(min.instance.botEndpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleOAuthRequests(server: any, min: GBMinInstance) {
|
||||||
|
server.get(`/${min.instance.botId}/auth`, (req, res) => {
|
||||||
|
let authorizationUrl = urlJoin(
|
||||||
|
min.instance.authenticatorAuthorityHostUrl,
|
||||||
|
min.instance.authenticatorTenant,
|
||||||
|
'/oauth2/authorize'
|
||||||
|
);
|
||||||
|
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
|
||||||
|
min.instance.authenticatorClientId
|
||||||
|
}&redirect_uri=${urlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
|
||||||
|
res.redirect(authorizationUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instance object to clients requesting bot info.
|
||||||
|
*/
|
||||||
|
private async sendInstanceToClient(req, bootInstance: IGBInstance, res: any, webchatToken: any) {
|
||||||
|
let botId = req.params.botId;
|
||||||
|
if (botId === '[default]') {
|
||||||
|
botId = bootInstance.botId;
|
||||||
|
}
|
||||||
|
const instance = await this.core.loadInstance(botId);
|
||||||
|
if (instance !== undefined) {
|
||||||
|
const speechToken = await this.getSTSToken(instance);
|
||||||
|
let theme = instance.theme;
|
||||||
|
if (theme !== undefined) {
|
||||||
|
theme = 'default.gbtheme';
|
||||||
|
}
|
||||||
|
res.send(
|
||||||
|
JSON.stringify({
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
botId: botId,
|
||||||
|
theme: theme,
|
||||||
|
secret: instance.webchatKey,
|
||||||
|
speechToken: speechToken,
|
||||||
|
conversationId: webchatToken.conversationId,
|
||||||
|
authenticatorTenant: instance.authenticatorTenant,
|
||||||
|
authenticatorClientId: instance.authenticatorClientId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const error = `Instance not found: ${botId}.`;
|
||||||
|
res.sendStatus(error);
|
||||||
|
GBLog.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Webchat key from Bot Service.
|
* Get Webchat key from Bot Service.
|
||||||
*
|
*
|
||||||
* @param instance The Bot instance.
|
* @param instance The Bot instance.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public async getWebchatToken(instance: any) {
|
private async getWebchatToken(instance: any) {
|
||||||
const options = {
|
const options = {
|
||||||
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -261,11 +276,13 @@ export class GBMinService {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const json = await request(options);
|
const json = await request(options);
|
||||||
|
|
||||||
return Promise.resolve(JSON.parse(json));
|
return Promise.resolve(JSON.parse(json));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `[botId:${
|
const msg = `[botId:${
|
||||||
instance.botId
|
instance.botId
|
||||||
}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
|
}] Error calling Direct Line client, verify Bot endpoint on the cloud. Error is: ${error}.`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,8 +293,7 @@ export class GBMinService {
|
||||||
* @param instance The general bot instance.
|
* @param instance The general bot instance.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public async getSTSToken(instance: any) {
|
private async getSTSToken(instance: any) {
|
||||||
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
|
url: 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issueToken',
|
||||||
|
@ -291,6 +307,7 @@ export class GBMinService {
|
||||||
return await request(options);
|
return await request(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
|
const msg = `Error calling Speech to Text client. Error is: ${error}.`;
|
||||||
|
|
||||||
return Promise.reject(new Error(msg));
|
return Promise.reject(new Error(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +334,7 @@ export class GBMinService {
|
||||||
min.adminService = this.adminService;
|
min.adminService = this.adminService;
|
||||||
min.instance = await this.core.loadInstance(min.botId);
|
min.instance = await this.core.loadInstance(min.botId);
|
||||||
min.cbMap = {};
|
min.cbMap = {};
|
||||||
|
min.scriptMap = {};
|
||||||
min.userProfile = conversationState.createProperty('userProfile');
|
min.userProfile = conversationState.createProperty('userProfile');
|
||||||
const dialogState = conversationState.createProperty('dialogState');
|
const dialogState = conversationState.createProperty('dialogState');
|
||||||
|
|
||||||
|
@ -327,8 +345,8 @@ export class GBMinService {
|
||||||
return { min, adapter, conversationState };
|
return { min, adapter, conversationState };
|
||||||
}
|
}
|
||||||
|
|
||||||
private invokeLoadBot(appPackages: any[], min: any, server: any) {
|
private invokeLoadBot(appPackages: any[], min: GBMinInstance, server: any) {
|
||||||
const sysPackages = new Array<IGBPackage>();
|
const sysPackages : IGBPackage[] = [];
|
||||||
// NOTE: A semicolon is necessary before this line.
|
// NOTE: A semicolon is necessary before this line.
|
||||||
[
|
[
|
||||||
GBCorePackage,
|
GBCorePackage,
|
||||||
|
@ -336,8 +354,8 @@ export class GBMinService {
|
||||||
GBAdminPackage,
|
GBAdminPackage,
|
||||||
GBKBPackage,
|
GBKBPackage,
|
||||||
GBAnalyticsPackage,
|
GBAnalyticsPackage,
|
||||||
GBCustomerSatisfactionPackage,
|
GBCustomerSatisfactionPackage
|
||||||
GBWhatsappPackage
|
// DISABLED: GBWhatsappPackage
|
||||||
].forEach(sysPackage => {
|
].forEach(sysPackage => {
|
||||||
const p = Object.create(sysPackage.prototype) as IGBPackage;
|
const p = Object.create(sysPackage.prototype) as IGBPackage;
|
||||||
p.loadBot(min);
|
p.loadBot(min);
|
||||||
|
@ -345,15 +363,21 @@ export class GBMinService {
|
||||||
if (sysPackage.name === 'GBWhatsappPackage') {
|
if (sysPackage.name === 'GBWhatsappPackage') {
|
||||||
const url = '/instances/:botId/whatsapp';
|
const url = '/instances/:botId/whatsapp';
|
||||||
server.post(url, (req, res) => {
|
server.post(url, (req, res) => {
|
||||||
p.channel.received(req, res);
|
(p as any).channel.received(req, res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
appPackages.forEach(e => {
|
appPackages.forEach(p => {
|
||||||
e.sysPackages = sysPackages;
|
p.sysPackages = sysPackages;
|
||||||
e.loadBot(min);
|
p.loadBot(min);
|
||||||
}, this);
|
if (p.getDialogs !== undefined) {
|
||||||
|
const dialogs = p.getDialogs(min);
|
||||||
|
dialogs.forEach(dialog => {
|
||||||
|
min.dialogs.add(new WaterfallDialog(dialog.name, dialog.waterfall));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -364,15 +388,14 @@ export class GBMinService {
|
||||||
req: any,
|
req: any,
|
||||||
res: any,
|
res: any,
|
||||||
conversationState: ConversationState,
|
conversationState: ConversationState,
|
||||||
min: any,
|
min: GBMinInstance,
|
||||||
instance: any,
|
instance: any,
|
||||||
appPackages: any[]
|
appPackages: any[]
|
||||||
) {
|
) {
|
||||||
return await adapter.processActivity(req, res, async context => {
|
await adapter.processActivity(req, res, async context => {
|
||||||
// Get loaded user state
|
// Get loaded user state
|
||||||
const state = await conversationState.get(context);
|
const step = await min.dialogs.createContext(context);
|
||||||
const step = await min.dialogs.createContext(context, state);
|
step.context.activity.locale = 'en-US';
|
||||||
step.context.activity.locale = 'en-US'; // TODO: Make dynamic.
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await min.userProfile.get(context, {});
|
const user = await min.userProfile.get(context, {});
|
||||||
|
@ -386,11 +409,11 @@ export class GBMinService {
|
||||||
});
|
});
|
||||||
user.loaded = true;
|
user.loaded = true;
|
||||||
user.subjects = [];
|
user.subjects = [];
|
||||||
user.cb = null;
|
user.cb = undefined;
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
GBLog.info(
|
||||||
`User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${
|
`User>: ${context.activity.text} (${context.activity.type}, ${context.activity.name}, ${
|
||||||
context.activity.channelId
|
context.activity.channelId
|
||||||
}, {context.activity.value})`
|
}, {context.activity.value})`
|
||||||
|
@ -398,7 +421,7 @@ export class GBMinService {
|
||||||
if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) {
|
if (context.activity.type === 'conversationUpdate' && context.activity.membersAdded.length > 0) {
|
||||||
const member = context.activity.membersAdded[0];
|
const member = context.activity.membersAdded[0];
|
||||||
if (member.name === 'GeneralBots') {
|
if (member.name === 'GeneralBots') {
|
||||||
logger.info(`Bot added to conversation, starting chat...`);
|
GBLog.info(`Bot added to conversation, starting chat...`);
|
||||||
appPackages.forEach(e => {
|
appPackages.forEach(e => {
|
||||||
e.onNewSession(min, step);
|
e.onNewSession(min, step);
|
||||||
});
|
});
|
||||||
|
@ -406,79 +429,87 @@ export class GBMinService {
|
||||||
|
|
||||||
await step.beginDialog('/');
|
await step.beginDialog('/');
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Member added to conversation: ${member.name}`);
|
GBLog.info(`Member added to conversation: ${member.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes messages.
|
// Processes messages.
|
||||||
} else if (context.activity.type === 'message') {
|
} else if (context.activity.type === 'message') {
|
||||||
// Checks for /admin request.
|
// Checks for /admin request.
|
||||||
if (context.activity.text === 'alpha-vba') {
|
await this.processMessageActivity(context, min, step);
|
||||||
min.sandbox.context = context;
|
|
||||||
min.sandbox.step = step;
|
|
||||||
min.sandbox['bot'].bind(min.sandbox);
|
|
||||||
await min.sandbox['bot']();
|
|
||||||
} else if (context.activity.text === 'admin') {
|
|
||||||
await step.beginDialog('/admin');
|
|
||||||
|
|
||||||
// Checks for /menu JSON signature.
|
|
||||||
} else if (context.activity.text.startsWith('{"title"')) {
|
|
||||||
await step.beginDialog('/menu', {
|
|
||||||
data: JSON.parse(context.activity.text)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Otherwise, continue to the active dialog in the stack.
|
|
||||||
} else {
|
|
||||||
const user = await min.userProfile.get(context, {});
|
|
||||||
|
|
||||||
if (step.activeDialog) {
|
|
||||||
await step.continueDialog();
|
|
||||||
} else {
|
|
||||||
await step.beginDialog('/answer', {
|
|
||||||
query: context.activity.text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processes events.
|
// Processes events.
|
||||||
} else if (context.activity.type === 'event') {
|
} else if (context.activity.type === 'event') {
|
||||||
// Empties dialog stack before going to the target.
|
// Empties dialog stack before going to the target.
|
||||||
|
|
||||||
// TODO: Understand MSFT changes: await step.endAll();
|
await this.processEventActivity(context, step);
|
||||||
|
|
||||||
if (context.activity.name === 'whoAmI') {
|
|
||||||
await step.beginDialog('/whoAmI');
|
|
||||||
} else if (context.activity.name === 'showSubjects') {
|
|
||||||
await step.beginDialog('/menu');
|
|
||||||
} else if (context.activity.name === 'giveFeedback') {
|
|
||||||
await step.beginDialog('/feedback', {
|
|
||||||
fromMenu: true
|
|
||||||
});
|
|
||||||
} else if (context.activity.name === 'showFAQ') {
|
|
||||||
await step.beginDialog('/faq');
|
|
||||||
} else if (context.activity.name === 'answerEvent') {
|
|
||||||
await step.beginDialog('/answerEvent', {
|
|
||||||
questionId: (context.activity as any).data,
|
|
||||||
fromFaq: true
|
|
||||||
});
|
|
||||||
} else if (context.activity.name === 'quality') {
|
|
||||||
await step.beginDialog('/quality', {
|
|
||||||
score: (context.activity as any).data
|
|
||||||
});
|
|
||||||
} else if (context.activity.name === 'updateToken') {
|
|
||||||
const token = (context.activity as any).data;
|
|
||||||
await step.beginDialog('/adminUpdateToken', { token: token });
|
|
||||||
} else {
|
|
||||||
await step.continueDialog();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 : ''}`;
|
||||||
logger.error(msg);
|
GBLog.error(msg);
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[step.context.activity.locale].very_sorry_about_error);
|
await step.context.sendActivity(Messages[step.context.activity.locale].very_sorry_about_error);
|
||||||
await step.beginDialog('/ask', { isReturning: true });
|
await step.beginDialog('/ask', { isReturning: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async processEventActivity(context, step: GBDialogStep) {
|
||||||
|
if (context.activity.name === 'whoAmI') {
|
||||||
|
await step.beginDialog('/whoAmI');
|
||||||
|
} else if (context.activity.name === 'showSubjects') {
|
||||||
|
await step.beginDialog('/menu', undefined);
|
||||||
|
} else if (context.activity.name === 'giveFeedback') {
|
||||||
|
await step.beginDialog('/feedback', {
|
||||||
|
fromMenu: true
|
||||||
|
});
|
||||||
|
} else if (context.activity.name === 'showFAQ') {
|
||||||
|
await step.beginDialog('/faq');
|
||||||
|
} else if (context.activity.name === 'answerEvent') {
|
||||||
|
await step.beginDialog('/answerEvent', <AskDialogArgs>{
|
||||||
|
questionId: context.activity.data,
|
||||||
|
fromFaq: true
|
||||||
|
});
|
||||||
|
} else if (context.activity.name === 'quality') {
|
||||||
|
await step.beginDialog('/quality', {
|
||||||
|
score: context.activity.data
|
||||||
|
});
|
||||||
|
} else if (context.activity.name === 'updateToken') {
|
||||||
|
const token = context.activity.data;
|
||||||
|
await step.beginDialog('/adminUpdateToken', { token: token });
|
||||||
|
} else {
|
||||||
|
await step.continueDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) {
|
||||||
|
// Direct script invoking by itent name.
|
||||||
|
|
||||||
|
const isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
|
||||||
|
|
||||||
|
if (isVMCall) {
|
||||||
|
const mainMethod = context.activity.text;
|
||||||
|
|
||||||
|
min.sandBoxMap[mainMethod].context = context;
|
||||||
|
min.sandBoxMap[mainMethod].step = step;
|
||||||
|
min.sandBoxMap[mainMethod][mainMethod].bind(min.sandBoxMap[mainMethod]);
|
||||||
|
await min.sandBoxMap[mainMethod][mainMethod]();
|
||||||
|
} else if (context.activity.text === 'admin') {
|
||||||
|
await step.beginDialog('/admin');
|
||||||
|
|
||||||
|
// Checks for /menu JSON signature.
|
||||||
|
} else if (context.activity.text.startsWith('{"title"')) {
|
||||||
|
await step.beginDialog('/menu', JSON.parse(context.activity.text));
|
||||||
|
// Otherwise, continue to the active dialog in the stack.
|
||||||
|
} else {
|
||||||
|
const user = await min.userProfile.get(context, {});
|
||||||
|
if (step.activeDialog !== undefined) {
|
||||||
|
await step.continueDialog();
|
||||||
|
} else {
|
||||||
|
await step.beginDialog('/answer', {
|
||||||
|
query: context.activity.text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -33,63 +33,129 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance, IGBCoreService } from 'botlib';
|
import { GBLog, GBMinInstance, GBService, IGBCoreService } from 'botlib';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { DialogClass } from './GBAPIService';
|
|
||||||
import { GBDeployer } from './GBDeployer';
|
import { GBDeployer } from './GBDeployer';
|
||||||
import { TSCompiler } from './TSCompiler';
|
import { TSCompiler } from './TSCompiler';
|
||||||
const util = require('util');
|
|
||||||
const logger = require('../../../src/logger');
|
const walkPromise = require('walk-promise');
|
||||||
|
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
|
import { DialogClass } from './GBAPIService';
|
||||||
|
//tslint:disable-next-line:no-submodule-imports
|
||||||
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
||||||
|
const beautify = require('js-beautify').js;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Virtualization services for emulation of BASIC.
|
* @fileoverview Virtualization services for emulation of BASIC.
|
||||||
* This alpha version is using a converter to translate BASIC to TS
|
* This alpha version is using a hack in form of converter to
|
||||||
* and string replacements to emulate await code.
|
* translate BASIC to TSand string replacements to emulate await code.
|
||||||
|
* See http://jsfiddle.net/roderick/dym05hsy for more info on vb2ts, so
|
||||||
* http://stevehanov.ca/blog/index.php?id=92 should be used to run it without
|
* http://stevehanov.ca/blog/index.php?id=92 should be used to run it without
|
||||||
* translation and enhance classic BASIC experience.
|
* translation and enhance classic BASIC experience.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class GBVMService implements IGBCoreService {
|
/**
|
||||||
|
* Basic services for BASIC manipulation.
|
||||||
|
*/
|
||||||
|
export class GBVMService extends GBService {
|
||||||
private readonly script = new vm.Script();
|
private readonly script = new vm.Script();
|
||||||
|
|
||||||
public async loadJS(
|
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||||
filename: string,
|
const files = await walkPromise(folder);
|
||||||
min: GBMinInstance,
|
|
||||||
core: IGBCoreService,
|
|
||||||
deployer: GBDeployer,
|
|
||||||
localPath: string
|
|
||||||
): Promise<void> {
|
|
||||||
const path = 'packages/default.gbdialog';
|
|
||||||
const file = 'bot.vbs';
|
|
||||||
const source = UrlJoin(path, file);
|
|
||||||
|
|
||||||
// Example when handled through fs.watch() listener
|
|
||||||
fs.watchFile(source, async (curr, prev) => {
|
|
||||||
await this.run(source, path, min, deployer, filename);
|
|
||||||
});
|
|
||||||
await this.run(source, path, min, deployer, filename);
|
|
||||||
this.addHearDialog(min);
|
this.addHearDialog(min);
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
files.map(async file => {
|
||||||
|
if (
|
||||||
|
file.name.endsWith('.vbs') ||
|
||||||
|
file.name.endsWith('.vb') ||
|
||||||
|
file.name.endsWith('.basic') ||
|
||||||
|
file.name.endsWith('.bas')
|
||||||
|
) {
|
||||||
|
const mainName = file.name.replace(/\-|\./g, '');
|
||||||
|
min.scriptMap[file.name] = mainName;
|
||||||
|
|
||||||
|
const filename = urlJoin(folder, file.name);
|
||||||
|
fs.watchFile(filename, async () => {
|
||||||
|
await this.run(filename, min, deployer, mainName);
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.run(filename, min, deployer, mainName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(source: any, path: string, min: any, deployer: GBDeployer, filename: string) {
|
/**
|
||||||
// Converts VBS into TS.
|
* Converts General Bots BASIC
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param code General Bots BASIC
|
||||||
|
*/
|
||||||
|
public convertGBASICToVBS(code: string) {
|
||||||
|
// Start and End of VB2TS tags of processing.
|
||||||
|
|
||||||
vb2ts.convertFile(source);
|
code = `<%\n${code}`;
|
||||||
|
|
||||||
|
// Keywords from General Bots BASIC.
|
||||||
|
|
||||||
|
code = code.replace(/(hear)\s*(\w+)/g, ($0, $1, $2) => {
|
||||||
|
return `${$2} = hear()`;
|
||||||
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(wait)\s*(\d+)/g, ($0, $1, $2) => {
|
||||||
|
return `sys().wait(${$2})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(generate a password)/g, ($0, $1) => {
|
||||||
|
return 'let password = sys().generatePassword()';
|
||||||
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(get)(\s)(.*)/g, ($0, $1, $2) => {
|
||||||
|
return `sys().httpGet (${$2})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(create a bot farm using)(\s)(.*)/g, ($0, $1, $2, $3) => {
|
||||||
|
return `sys().createABotFarmUsing (${$3})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
code = code.replace(/(talk)(\s)(.*)/g, ($0, $1, $2, $3) => {
|
||||||
|
return `talk (${$3})\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
code = `${code}\n%>`;
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(filename: any, min: GBMinInstance, deployer: GBDeployer, mainName: string) {
|
||||||
|
// Converts General Bots BASIC into regular VBS
|
||||||
|
|
||||||
|
const basicCode: string = fs.readFileSync(filename, 'utf8');
|
||||||
|
const vbsCode = this.convertGBASICToVBS(basicCode);
|
||||||
|
const vbsFile = `${filename}.compiled`;
|
||||||
|
fs.writeFileSync(vbsFile, vbsCode, 'utf8');
|
||||||
|
|
||||||
|
// Converts VBS into TS.
|
||||||
|
vb2ts.convertFile(vbsFile);
|
||||||
|
|
||||||
// Convert TS into JS.
|
// Convert TS into JS.
|
||||||
const tsfile = `bot.ts`;
|
const tsfile: string = `${filename}.ts`;
|
||||||
|
let tsCode: string = fs.readFileSync(tsfile, 'utf8');
|
||||||
|
tsCode = tsCode.replace(/export.*\n/g, `export function ${mainName}() {`);
|
||||||
|
fs.writeFileSync(tsfile, tsCode);
|
||||||
|
|
||||||
const tsc = new TSCompiler();
|
const tsc = new TSCompiler();
|
||||||
tsc.compile([UrlJoin(path, tsfile)]);
|
tsc.compile([tsfile]);
|
||||||
|
|
||||||
// Run JS into the GB context.
|
// Run JS into the GB context.
|
||||||
const jsfile = `bot.js`;
|
const jsfile = `${tsfile}.js`.replace('.ts', '');
|
||||||
const localPath = UrlJoin(path, jsfile);
|
|
||||||
|
if (fs.existsSync(jsfile)) {
|
||||||
|
let code: string = fs.readFileSync(jsfile, 'utf8');
|
||||||
|
|
||||||
if (fs.existsSync(localPath)) {
|
|
||||||
let code: string = fs.readFileSync(localPath, 'utf8');
|
|
||||||
code = code.replace(/^.*exports.*$/gm, '');
|
code = code.replace(/^.*exports.*$/gm, '');
|
||||||
|
|
||||||
// Finds all hear calls.
|
// Finds all hear calls.
|
||||||
|
@ -97,14 +163,14 @@ export class GBVMService implements IGBCoreService {
|
||||||
let parsedCode = code;
|
let parsedCode = code;
|
||||||
const hearExp = /(\w+).*hear.*\(\)/;
|
const hearExp = /(\w+).*hear.*\(\)/;
|
||||||
|
|
||||||
let match1;
|
let match1 = hearExp.exec(code);
|
||||||
|
|
||||||
while ((match1 = hearExp.exec(code))) {
|
while (match1 !== undefined) {
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
// Writes async body.
|
// Writes async body.
|
||||||
|
|
||||||
const variable = match1[1]; // variable = hear();
|
const variable = match1[1]; // Construct variable = hear ().
|
||||||
|
|
||||||
parsedCode = code.substring(pos, pos + match1.index);
|
parsedCode = code.substring(pos, pos + match1.index);
|
||||||
parsedCode += `hear (async (${variable}) => {\n`;
|
parsedCode += `hear (async (${variable}) => {\n`;
|
||||||
|
@ -119,8 +185,9 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
let right = 0;
|
let right = 0;
|
||||||
let left = 1;
|
let left = 1;
|
||||||
let match2;
|
let match2 = /\{|\}/.exec(tempCode);
|
||||||
while ((match2 = /\{|\}/.exec(tempCode))) {
|
|
||||||
|
while (match2 !== undefined) {
|
||||||
const c = tempCode.substring(match2.index, match2.index + 1);
|
const c = tempCode.substring(match2.index, match2.index + 1);
|
||||||
|
|
||||||
if (c === '}') {
|
if (c === '}') {
|
||||||
|
@ -135,6 +202,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
if (left === right) {
|
if (left === right) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
match1 = hearExp.exec(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length);
|
parsedCode += code.substring(start + match1[0].length + 1, pos + match1[0].length);
|
||||||
|
@ -144,39 +212,49 @@ export class GBVMService implements IGBCoreService {
|
||||||
// A interaction will be made for each hear.
|
// A interaction will be made for each hear.
|
||||||
|
|
||||||
code = parsedCode;
|
code = parsedCode;
|
||||||
|
match2 = /\{|\}/.exec(tempCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
|
parsedCode = this.handleThisAndAwait(parsedCode);
|
||||||
return $1 == undefined ? 'this.talk' : $1;
|
|
||||||
});
|
|
||||||
|
|
||||||
parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
|
parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true });
|
||||||
return $1 == undefined ? 'this.hear' : $1;
|
fs.writeFileSync(jsfile, parsedCode);
|
||||||
});
|
|
||||||
|
|
||||||
parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
|
const sandbox: DialogClass = new DialogClass(min, deployer);
|
||||||
return $1 == undefined ? 'this.sendEmail' : $1;
|
|
||||||
});
|
|
||||||
|
|
||||||
parsedCode = parsedCode.replace(/this\./gm, 'await this.');
|
|
||||||
parsedCode = parsedCode.replace(/function/gm, 'async function');
|
|
||||||
|
|
||||||
fs.writeFileSync(localPath, parsedCode);
|
|
||||||
|
|
||||||
const sandbox: DialogClass = new DialogClass(min);
|
|
||||||
const context = vm.createContext(sandbox);
|
const context = vm.createContext(sandbox);
|
||||||
vm.runInContext(parsedCode, context);
|
vm.runInContext(parsedCode, context);
|
||||||
min.sandbox = sandbox;
|
min.sandBoxMap[mainName] = sandbox;
|
||||||
await deployer.deployScriptToStorage(min.instanceId, filename);
|
GBLog.info(`[GBVMService] Finished loading of ${filename}`);
|
||||||
logger.info(`[GBVMService] Finished loading of ${filename}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleThisAndAwait(code: string) {
|
||||||
|
// this insertion.
|
||||||
|
|
||||||
|
code = code.replace(/sys\(\)/g, 'this.sys()');
|
||||||
|
code = code.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
|
||||||
|
return $1 === undefined ? 'this.talk' : $1;
|
||||||
|
});
|
||||||
|
code = code.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
|
||||||
|
return $1 === undefined ? 'this.hear' : $1;
|
||||||
|
});
|
||||||
|
code = code.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
|
||||||
|
return $1 === undefined ? 'this.sendEmail' : $1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// await insertion.
|
||||||
|
|
||||||
|
code = code.replace(/this\./gm, 'await this.');
|
||||||
|
code = code.replace(/function/gm, 'async function');
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
private addHearDialog(min) {
|
private addHearDialog(min) {
|
||||||
min.dialogs.add(
|
min.dialogs.add(
|
||||||
new WaterfallDialog('/hear', [
|
new WaterfallDialog('/hear', [
|
||||||
async step => {
|
async step => {
|
||||||
step.activeDialog.state.cbId = step.options['id'];
|
step.activeDialog.state.cbId = (step.options as any).id;
|
||||||
|
|
||||||
return await step.prompt('textPrompt', {});
|
return await step.prompt('textPrompt', {});
|
||||||
},
|
},
|
||||||
|
@ -186,7 +264,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
const cbId = step.activeDialog.state.cbId;
|
const cbId = step.activeDialog.state.cbId;
|
||||||
const cb = min.cbMap[cbId];
|
const cb = min.cbMap[cbId];
|
||||||
cb.bind({ step: step, context: step.context }); // TODO: Necessary or min.sandbox?
|
cb.bind({ step: step, context: step.context });
|
||||||
|
|
||||||
await step.endDialog();
|
await step.endDialog();
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,12 +36,14 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import { GBLog } from 'botlib';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
|
||||||
|
@ -76,11 +78,11 @@ export class TSCompiler {
|
||||||
if (!TSCompiler.shouldIgnoreError(diagnostic)) {
|
if (!TSCompiler.shouldIgnoreError(diagnostic)) {
|
||||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||||
|
|
||||||
if (diagnostic.file) {
|
if (diagnostic.file !== undefined) {
|
||||||
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||||
logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
GBLog.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
||||||
} else {
|
} else {
|
||||||
logger.error(`${message}`);
|
GBLog.error(`${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import 'mocha';
|
import { GBImporter } from '../services/GBImporterService';
|
||||||
import {GBImporter} from '../services/GBImporterService';
|
|
||||||
|
|
||||||
describe('Hello function', () => {
|
describe('Hello function', () => {
|
||||||
|
|
||||||
it('should return empty test', () => {
|
it('should return empty test', () => {
|
||||||
const service = new GBImporter(null);
|
const service = new GBImporter(undefined);
|
||||||
//service.importIfNotExistsBotPackage(null, null);
|
|
||||||
const result = 0;
|
const result = 0;
|
||||||
expect(result).to.equal(0);
|
expect(result).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -42,9 +42,7 @@ import { GBVMService } from '../services/GBVMService';
|
||||||
describe('Load function', () => {
|
describe('Load function', () => {
|
||||||
it('should fail on invalid file', () => {
|
it('should fail on invalid file', () => {
|
||||||
try {
|
try {
|
||||||
// const service = new GBVMService();
|
const service = new GBVMService();
|
||||||
// TODO: service.loadJS('invalid.file', null, null, null, null);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error).to.equal(0);
|
expect(error).to.equal(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,12 +38,14 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { IGBDialog } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { CSService } from '../services/CSService';
|
import { CSService } from '../services/CSService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for feedback collecting.
|
||||||
|
*/
|
||||||
export class FeedbackDialog extends IGBDialog {
|
export class FeedbackDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -58,13 +60,7 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
new WaterfallDialog('/feedbackNumber', [
|
new WaterfallDialog('/feedbackNumber', [
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
// TODO: Migrate to 4.*+ await step.prompt("choicePrompt", Messages[locale].what_about_me, [
|
|
||||||
// "1",
|
|
||||||
// "2",
|
|
||||||
// "3",
|
|
||||||
// "4",
|
|
||||||
// "5"
|
|
||||||
// ]);
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
@ -73,6 +69,7 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
const user = await min.userProfile.get(context, {});
|
const user = await min.userProfile.get(context, {});
|
||||||
await service.updateConversationRate(user.conversation, rate);
|
await service.updateConversationRate(user.conversation, rate);
|
||||||
await step.context.sendActivity(Messages[locale].thanks);
|
await step.context.sendActivity(Messages[locale].thanks);
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
@ -84,29 +81,26 @@ export class FeedbackDialog extends IGBDialog {
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].about_suggestions);
|
await step.context.sendActivity(Messages[locale].about_suggestions);
|
||||||
step.activeDialog.state.cbId = step.options['id'];
|
step.activeDialog.state.cbId = (step.options as any).id;
|
||||||
|
|
||||||
return await step.prompt('textPrompt', Messages[locale].what_about_service);
|
return await step.prompt('textPrompt', Messages[locale].what_about_service);
|
||||||
},
|
},
|
||||||
async step => {
|
async step => {
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
const rate = await AzureText.getSentiment(
|
const rate = await AzureText.getSentiment(
|
||||||
min.instance.textAnalyticsKey,
|
min.instance.textAnalyticsKey,
|
||||||
min.instance.textAnalyticsEndpoint,
|
min.instance.textAnalyticsEndpoint,
|
||||||
min.conversationalService.getCurrentLanguage(step),
|
min.conversationalService.getCurrentLanguage(step),
|
||||||
step.result
|
step.result
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rate > 0.5) {
|
if (rate > 0.5) {
|
||||||
await step.context.sendActivity(Messages[locale].glad_you_liked);
|
await step.context.sendActivity(Messages[locale].glad_you_liked);
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(Messages[locale].we_will_improve);
|
await step.context.sendActivity(Messages[locale].we_will_improve);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Record.
|
|
||||||
}
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,15 +36,16 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { CSService } from '../services/CSService';
|
import { CSService } from '../services/CSService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog for collecting quality of answer.
|
||||||
|
*/
|
||||||
export class QualityDialog extends IGBDialog {
|
export class QualityDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -63,11 +64,11 @@ export class QualityDialog extends IGBDialog {
|
||||||
const score = step.result;
|
const score = step.result;
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => min.conversationalService.sendEvent(step, 'stop', null),
|
() => min.conversationalService.sendEvent(step, 'stop', undefined),
|
||||||
400
|
400
|
||||||
);
|
);
|
||||||
|
|
||||||
if (score == 0) {
|
if (score === 0) {
|
||||||
await step.context.sendActivity(Messages[locale].im_sorry_lets_try);
|
await step.context.sendActivity(Messages[locale].im_sorry_lets_try);
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(Messages[locale].great_thanks);
|
await step.context.sendActivity(Messages[locale].great_thanks);
|
||||||
|
@ -79,6 +80,7 @@ export class QualityDialog extends IGBDialog {
|
||||||
);
|
);
|
||||||
await step.replaceDialog('/ask', { isReturning: true });
|
await step.replaceDialog('/ask', { isReturning: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
]));
|
]));
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,32 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import urlJoin = require('url-join');
|
||||||
import { FeedbackDialog } from './dialogs/FeedbackDialog';
|
import { FeedbackDialog } from './dialogs/FeedbackDialog';
|
||||||
import { QualityDialog } from './dialogs/QualityDialog';
|
import { QualityDialog } from './dialogs/QualityDialog';
|
||||||
import { GuaribasQuestionAlternate } from './models/index';
|
import { GuaribasQuestionAlternate } from './models/index';
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for customer-satisfaction.gblib.
|
||||||
|
*/
|
||||||
export class GBCustomerSatisfactionPackage implements IGBPackage {
|
export class GBCustomerSatisfactionPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
core.sequelize.addModels([
|
GBLog.verbose(`getDialogs called.`);
|
||||||
GuaribasQuestionAlternate
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasQuestionAlternate]);
|
||||||
}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
FeedbackDialog.setup(min.bot, min);
|
FeedbackDialog.setup(min.bot, min);
|
||||||
QualityDialog.setup(min.bot, min);
|
QualityDialog.setup(min.bot, min);
|
||||||
}
|
}
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,33 +36,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
BelongsTo,
|
BelongsTo,
|
||||||
BelongsToMany,
|
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
|
||||||
DataType,
|
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
HasMany,
|
|
||||||
IsUUID,
|
|
||||||
Length,
|
|
||||||
Model,
|
Model,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Sequelize,
|
Table} from 'sequelize-typescript';
|
||||||
Table,
|
|
||||||
UpdatedAt
|
|
||||||
} from 'sequelize-typescript';
|
|
||||||
|
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of saved alternate questions.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
|
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -33,6 +33,9 @@
|
||||||
import { GuaribasConversation } from '../../analytics.gblib/models';
|
import { GuaribasConversation } from '../../analytics.gblib/models';
|
||||||
import { GuaribasQuestionAlternate } from '../models';
|
import { GuaribasQuestionAlternate } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Satisfaction Service Layer.
|
||||||
|
*/
|
||||||
export class CSService {
|
export class CSService {
|
||||||
|
|
||||||
public async resolveQuestionAlternate(
|
public async resolveQuestionAlternate(
|
||||||
|
@ -62,6 +65,7 @@ export class CSService {
|
||||||
rate: number
|
rate: number
|
||||||
): Promise<GuaribasConversation> {
|
): Promise<GuaribasConversation> {
|
||||||
conversation.rate = rate;
|
conversation.rate = rate;
|
||||||
|
|
||||||
return conversation.save();
|
return conversation.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +1,20 @@
|
||||||
<%
|
' General Bots Copyright (c) Pragmatismo.io. All rights reserved. Licensed under the AGPL-3.0.
|
||||||
'****************************************************************************
|
|
||||||
' ( )_ _
|
|
||||||
' _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _
|
|
||||||
' ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\
|
|
||||||
' | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) )
|
|
||||||
' | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/'
|
|
||||||
' | | ( )_) |
|
|
||||||
' (_) \___/'
|
|
||||||
'
|
|
||||||
' General Bots Copyright (c) Pragmatismo.io. All rights reserved.
|
|
||||||
' Licensed under the AGPL-3.0.
|
|
||||||
'
|
|
||||||
' This BASIC file is based on this JavaScript file by Rodrigo Ruotolo:
|
|
||||||
' -> http://jsfiddle.net/roderick/dym05hsy
|
|
||||||
'
|
|
||||||
' 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.
|
|
||||||
'
|
|
||||||
'****************************************************************************
|
|
||||||
|
|
||||||
|
talk "How many installments do you want to pay your Credit?"
|
||||||
|
hear installments
|
||||||
talk ("How many installments do you want to pay your Credit?")
|
|
||||||
installments = hear ()
|
|
||||||
|
|
||||||
if installments > 60 then
|
if installments > 60 then
|
||||||
talk ("The maximum number of payments is 60")
|
talk "The maximum number of payments is 60"
|
||||||
else
|
else
|
||||||
talk ("What is the amount requested?")
|
talk "What is the amount requested?"
|
||||||
ammount = hear ()
|
hear ammount
|
||||||
|
|
||||||
if ammount >100000 then
|
if ammount >100000 then
|
||||||
talk ("We are sorry, we can only accept proposals bellow 100k")
|
talk "We are sorry, we can only accept proposals bellow 100k"
|
||||||
else
|
else
|
||||||
|
|
||||||
talk ("What is the best due date?")
|
talk "What is the best due date?"
|
||||||
dueDate = hear ()
|
hear dueDate
|
||||||
|
|
||||||
interestRate = 0
|
interestRate = 0
|
||||||
adjustment = 0
|
adjustment = 0
|
||||||
|
@ -86,15 +50,15 @@ else
|
||||||
end if
|
end if
|
||||||
|
|
||||||
if installments > 60 then
|
if installments > 60 then
|
||||||
talk ("The maximum number of payments is 60")
|
talk "The maximum number of payments is 60"
|
||||||
end if
|
end if
|
||||||
|
|
||||||
|
|
||||||
' TODO: This must be reviewed in terms of financing logic.
|
' TODO: This must be reviewed in terms of financing logic.
|
||||||
|
|
||||||
nInstallments = parseInt(installments)
|
nInstallments = parseIntinstallments
|
||||||
vAmmount = parseFloat(ammount)
|
vAmmount = parseFloatammount
|
||||||
initialPayment = parseFloat(vAmmount) * 0.3 ' 30% of the value
|
initialPayment = parseFloatvAmmount * 0.3 ' 30% of the value
|
||||||
tac = 800
|
tac = 800
|
||||||
adjustment = 1.3
|
adjustment = 1.3
|
||||||
|
|
||||||
|
@ -102,13 +66,12 @@ else
|
||||||
paymentValue = totalValue * adjustment
|
paymentValue = totalValue * adjustment
|
||||||
finalValue = paymentValue * nInstallments + initialPayment
|
finalValue = paymentValue * nInstallments + initialPayment
|
||||||
|
|
||||||
talk("Congratulations! Your credit analysis is **done**:")
|
talk "Congratulations! Your credit analysis is **done**:"
|
||||||
talk("First payment: **" + initialPayment + "**")
|
talk "First payment: **" + initialPayment + "**"
|
||||||
talk("Payment value: **" + paymentValue + "**")
|
talk "Payment value: **" + paymentValue + "**"
|
||||||
talk("Interest Rate: **" + interestRate + "%**")
|
talk "Interest Rate: **" + interestRate + "%**"
|
||||||
talk("Total Value: **" + totalValue + "**")
|
talk "Total Value: **" + totalValue + "**"
|
||||||
talk("Final Value: **" + finalValue + "**")
|
talk "Final Value: **" + finalValue + "**"
|
||||||
|
|
||||||
end if
|
end if
|
||||||
end if
|
end if
|
||||||
%>
|
|
37
packages/default.gbdialog/sys-bot-farm-creation.vbs
Normal file
37
packages/default.gbdialog/sys-bot-farm-creation.vbs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
' General Bots Copyright (c) Pragmatismo.io. All rights reserved. Licensed under the AGPL-3.0.
|
||||||
|
|
||||||
|
talk "Please, tell me what is the Bot name?"
|
||||||
|
hear title
|
||||||
|
|
||||||
|
talk "If you tell me your username/password, I can show service subscription list to you."
|
||||||
|
talk "What is your Username (eg.: human@domain.bot)"
|
||||||
|
hear email
|
||||||
|
|
||||||
|
talk "What is your Password"
|
||||||
|
hear password
|
||||||
|
|
||||||
|
talk "Your password? (Will be discarded after sigining process)"
|
||||||
|
talk "Can you describe in a few words what the bot is about?"
|
||||||
|
hear description
|
||||||
|
|
||||||
|
talk "Please, paste the Subscription ID (Azure):"
|
||||||
|
hear subscriptionId
|
||||||
|
|
||||||
|
talk "Please, provide the cloud location just like 'westus'?"
|
||||||
|
hear location
|
||||||
|
|
||||||
|
talk "Please, provide the Authoring Key for NLP service (LUIS)?"
|
||||||
|
hear authoringKey
|
||||||
|
|
||||||
|
talk "Sorry, this part cannot be automated yet due to Microsoft schedule, please go to https://apps.dev.microsoft.com/portal/register-app to generate manually an App ID and App Secret."
|
||||||
|
wait 5
|
||||||
|
|
||||||
|
talk "Please, provide the App ID you just generated:"
|
||||||
|
hear appId
|
||||||
|
|
||||||
|
talk "Please, provide the Generated Password:"
|
||||||
|
hear appPassword
|
||||||
|
|
||||||
|
talk "Now, I am going to create a Bot farm... Wait 5 minutes or more..."
|
||||||
|
|
||||||
|
create a bot farm using title, email, password, location, authoringKey, appId, appPassword, subscriptionId
|
|
@ -81,7 +81,7 @@ html[dir="ltr"] .ms-Nav-compositeLink.is-selected a:after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background-image: url(../images/logo_General_BotS.png);
|
background-image: url(../images/bot-logo.png);
|
||||||
background-position: 2px 2px;
|
background-position: 2px 2px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 25 KiB |
13
packages/default.gbui/package-lock.json
generated
13
packages/default.gbui/package-lock.json
generated
|
@ -5719,7 +5719,8 @@
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -6084,7 +6085,8 @@
|
||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
@ -6132,6 +6134,7 @@
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
@ -6170,11 +6173,13 @@
|
||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,14 +38,22 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBLog, GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
import { AzureText } from 'pragmatismo-io-framework';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { KBService } from './../services/KBService';
|
import { KBService } from './../services/KBService';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
/**
|
||||||
|
* Dialog arguments.
|
||||||
|
*/
|
||||||
|
export class AskDialogArgs {
|
||||||
|
public questionId: number;
|
||||||
|
public fromFaq: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ask loop on knowledge base data or delegate to other services.
|
||||||
|
*/
|
||||||
export class AskDialog extends IGBDialog {
|
export class AskDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -55,154 +63,135 @@ export class AskDialog extends IGBDialog {
|
||||||
*/
|
*/
|
||||||
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('/answerEvent', AskDialog.getAnswerEventDialog(service, min)));
|
||||||
|
min.dialogs.add(new WaterfallDialog('/answer', AskDialog.getAnswerDialog(min, service)));
|
||||||
|
min.dialogs.add(new WaterfallDialog('/ask', AskDialog.getAskDialog(min)));
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAskDialog(min: GBMinInstance) {
|
||||||
new WaterfallDialog('/answerEvent', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
if (step.options && step.options['questionId']) {
|
const locale = step.context.activity.locale;
|
||||||
const question = await service.getQuestionById(min.instance.instanceId, step.options['questionId']);
|
const user = await min.userProfile.get(step.context, {});
|
||||||
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
user.isAsking = true;
|
||||||
|
if (!user.subjects) {
|
||||||
|
user.subjects = [];
|
||||||
|
}
|
||||||
|
let text;
|
||||||
|
// Three forms of asking.
|
||||||
|
if (step.options && step.options.firstTime) {
|
||||||
|
text = Messages[locale].ask_first_time;
|
||||||
|
} else if (step.options && step.options.isReturning) {
|
||||||
|
text = Messages[locale].anything_else;
|
||||||
|
} else if (user.subjects.length > 0) {
|
||||||
|
text = Messages[locale].which_question;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid use of /ask');
|
||||||
|
}
|
||||||
|
if (text.length > 0) {
|
||||||
|
return await step.prompt('textPrompt', text);
|
||||||
|
}
|
||||||
|
|
||||||
// Sends the answer to all outputs, including projector.
|
return await step.next();
|
||||||
|
},
|
||||||
await service.sendAnswer(min.conversationalService, step, answer);
|
async step => {
|
||||||
|
if (step.result) {
|
||||||
await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/answer', { query: step.result });
|
||||||
}
|
} else {
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
])
|
}
|
||||||
);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAnswerDialog(min: GBMinInstance, service: KBService) {
|
||||||
new WaterfallDialog('/answer', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
const user = await min.userProfile.get(step.context, {});
|
||||||
let text = step.options['query'];
|
let text = step.options.query;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
throw new Error(`/answer being called with no args query text.`);
|
throw new Error(`/answer being called with no args query text.`);
|
||||||
|
}
|
||||||
|
const locale = step.context.activity.locale;
|
||||||
|
// Stops any content on projector.
|
||||||
|
await min.conversationalService.sendEvent(step, 'stop', undefined);
|
||||||
|
// Handle extra text from FAQ.
|
||||||
|
if (step.options && step.options.query) {
|
||||||
|
text = step.options.query;
|
||||||
|
} else if (step.options && step.options.fromFaq) {
|
||||||
|
await step.context.sendActivity(Messages[locale].going_answer);
|
||||||
|
}
|
||||||
|
// Spells check the input text before sending Search or NLP.
|
||||||
|
if (min.instance.spellcheckerKey !== undefined) {
|
||||||
|
const data = await AzureText.getSpelledText(min.instance.spellcheckerKey, text);
|
||||||
|
if (data !== text) {
|
||||||
|
GBLog.info(`Spelling corrected: ${data}`);
|
||||||
|
text = data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const locale = step.context.activity.locale;
|
// Searches KB for the first time.
|
||||||
|
user.lastQuestion = text;
|
||||||
|
await min.userProfile.set(step.context, user);
|
||||||
|
const resultsA = await service.ask(min.instance, text, min.instance.searchScore, user.subjects);
|
||||||
|
|
||||||
// Stops any content on projector.
|
// If there is some result, answer immediately.
|
||||||
|
if (resultsA !== undefined && resultsA.answer !== undefined) {
|
||||||
await min.conversationalService.sendEvent(step, 'stop', null);
|
// Saves some context info.
|
||||||
|
user.isAsking = false;
|
||||||
// Handle extra text from FAQ.
|
user.lastQuestionId = resultsA.questionId;
|
||||||
|
|
||||||
if (step.options && step.options['query']) {
|
|
||||||
text = step.options['query'];
|
|
||||||
} else if (step.options && step.options['fromFaq']) {
|
|
||||||
await step.context.sendActivity(Messages[locale].going_answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spells check the input text before sending Search or NLP.
|
|
||||||
|
|
||||||
if (min.instance.spellcheckerKey) {
|
|
||||||
const data = await AzureText.getSpelledText(min.instance.spellcheckerKey, text);
|
|
||||||
|
|
||||||
if (data != text) {
|
|
||||||
logger.info(`Spelling corrected: ${data}`);
|
|
||||||
text = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searches KB for the first time.
|
|
||||||
|
|
||||||
user.lastQuestion = text;
|
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user);
|
||||||
const resultsA = await service.ask(min.instance, text, min.instance.searchScore, user.subjects);
|
|
||||||
|
|
||||||
|
// Sends the answer to all outputs, including projector.
|
||||||
|
await service.sendAnswer(min.conversationalService, step, resultsA.answer);
|
||||||
|
|
||||||
|
// Goes to ask loop, again.
|
||||||
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
} else {
|
||||||
|
// Second time running Search, now with no filter.
|
||||||
|
const resultsB = await service.ask(min.instance, text, min.instance.searchScore, undefined);
|
||||||
// If there is some result, answer immediately.
|
// If there is some result, answer immediately.
|
||||||
|
|
||||||
if (resultsA && resultsA.answer) {
|
if (resultsB !== undefined && resultsB.answer !== undefined) {
|
||||||
// Saves some context info.
|
// Saves some context info.
|
||||||
|
const user2 = await min.userProfile.get(step.context, {});
|
||||||
user.isAsking = false;
|
user2.isAsking = false;
|
||||||
user.lastQuestionId = resultsA.questionId;
|
user2.lastQuestionId = resultsB.questionId;
|
||||||
await min.userProfile.set(step.context, user);
|
await min.userProfile.set(step.context, user2);
|
||||||
|
// Informs user that a broader search will be used.
|
||||||
|
if (user2.subjects.length > 0) {
|
||||||
|
await step.context.sendActivity(Messages[locale].wider_answer);
|
||||||
|
}
|
||||||
// Sends the answer to all outputs, including projector.
|
// Sends the answer to all outputs, including projector.
|
||||||
|
await service.sendAnswer(min.conversationalService, step, resultsB.answer);
|
||||||
await service.sendAnswer(min.conversationalService, step, resultsA.answer);
|
|
||||||
|
|
||||||
// Goes to ask loop, again.
|
|
||||||
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
} else {
|
} else {
|
||||||
// Second time running Search, now with no filter.
|
if (!(await min.conversationalService.routeNLP(step, min, text))) {
|
||||||
|
await step.context.sendActivity(Messages[locale].did_not_find);
|
||||||
|
|
||||||
const resultsB = await service.ask(min.instance, text, min.instance.searchScore, null);
|
|
||||||
|
|
||||||
// If there is some result, answer immediately.
|
|
||||||
|
|
||||||
if (resultsB && resultsB.answer) {
|
|
||||||
// Saves some context info.
|
|
||||||
|
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
user.isAsking = false;
|
|
||||||
user.lastQuestionId = resultsB.questionId;
|
|
||||||
await min.userProfile.set(step.context, user);
|
|
||||||
|
|
||||||
// Informs user that a broader search will be used.
|
|
||||||
|
|
||||||
if (user.subjects.length > 0) {
|
|
||||||
const subjectText = `${KBService.getSubjectItemsSeparatedBySpaces(user.subjects)}`;
|
|
||||||
await step.context.sendActivity(Messages[locale].wider_answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends the answer to all outputs, including projector.
|
|
||||||
|
|
||||||
await service.sendAnswer(min.conversationalService, step, resultsB.answer);
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
return await step.replaceDialog('/ask', { isReturning: true });
|
||||||
} else {
|
|
||||||
if (!(await min.conversationalService.routeNLP(step, min, text))) {
|
|
||||||
await step.context.sendActivity(Messages[locale].did_not_find);
|
|
||||||
return await step.replaceDialog('/ask', { isReturning: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
}
|
||||||
);
|
];
|
||||||
|
}
|
||||||
|
|
||||||
min.dialogs.add(
|
private static getAnswerEventDialog(service: KBService, min: GBMinInstance) {
|
||||||
new WaterfallDialog('/ask', [
|
return [
|
||||||
async step => {
|
async step => {
|
||||||
const locale = step.context.activity.locale;
|
const data = step.options as AskDialogArgs;
|
||||||
const user = await min.userProfile.get(step.context, {});
|
if (data !== undefined && data.questionId !== undefined) {
|
||||||
user.isAsking = true;
|
const question = await service.getQuestionById(min.instance.instanceId, data.questionId);
|
||||||
if (!user.subjects) {
|
const answer = await service.getAnswerById(min.instance.instanceId, question.answerId);
|
||||||
user.subjects = [];
|
// Sends the answer to all outputs, including projector.
|
||||||
}
|
await service.sendAnswer(min.conversationalService, step, answer);
|
||||||
let text;
|
await step.replaceDialog('/ask', { isReturning: true });
|
||||||
|
|
||||||
// Three forms of asking.
|
|
||||||
|
|
||||||
if (step.options && step.options['firstTime']) {
|
|
||||||
text = Messages[locale].ask_first_time;
|
|
||||||
} else if (step.options && step.options['isReturning']) {
|
|
||||||
text = Messages[locale].anything_else;
|
|
||||||
} else if (user.subjects.length > 0) {
|
|
||||||
text = Messages[locale].which_question;
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid use of /ask');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.length > 0) {
|
|
||||||
return await step.prompt('textPrompt', text);
|
|
||||||
}
|
|
||||||
return await step.next();
|
|
||||||
},
|
|
||||||
async step => {
|
|
||||||
if (step.result) {
|
|
||||||
return await step.replaceDialog('/answer', { query: step.result });
|
|
||||||
} else {
|
|
||||||
return await step.next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
])
|
|
||||||
);
|
return await step.next();
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -38,11 +38,13 @@
|
||||||
|
|
||||||
import { BotAdapter } from 'botbuilder';
|
import { BotAdapter } from 'botbuilder';
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
import { KBService } from './../services/KBService';
|
import { KBService } from './../services/KBService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle display of FAQ allowing direct access to KB.
|
||||||
|
*/
|
||||||
export class FaqDialog extends IGBDialog {
|
export class FaqDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -56,15 +58,16 @@ export class FaqDialog extends IGBDialog {
|
||||||
|
|
||||||
min.dialogs.add(new WaterfallDialog('/faq', [
|
min.dialogs.add(new WaterfallDialog('/faq', [
|
||||||
async step => {
|
async step => {
|
||||||
const data = await service.getFaqBySubjectArray('faq', null);
|
const data = await service.getFaqBySubjectArray('faq', undefined);
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
if (data) {
|
if (data !== undefined) {
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'bullet',
|
playerType: 'bullet',
|
||||||
data: data.slice(0, 10)
|
data: data.slice(0, 10)
|
||||||
});
|
});
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].see_faq); // TODO: RND messages.
|
await step.context.sendActivity(Messages[locale].see_faq);
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
/*****************************************************************************\
|
/*****************************************************************************\
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,17 +36,26 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('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 { IGBDialog } from 'botlib';
|
import { GBMinInstance, IGBDialog } from 'botlib';
|
||||||
import { GBMinInstance } from 'botlib';
|
|
||||||
import { AzureText } from 'pragmatismo-io-framework';
|
|
||||||
import { GuaribasSubject } from '../models';
|
import { GuaribasSubject } from '../models';
|
||||||
import { KBService } from '../services/KBService';
|
import { KBService } from '../services/KBService';
|
||||||
import { Messages } from '../strings';
|
import { Messages } from '../strings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog arguments.
|
||||||
|
*/
|
||||||
|
export class MenuDialogArgs {
|
||||||
|
public to: string;
|
||||||
|
public subjectId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialogs for handling Menu control.
|
||||||
|
*/
|
||||||
export class MenuDialog extends IGBDialog {
|
export class MenuDialog extends IGBDialog {
|
||||||
/**
|
/**
|
||||||
* Setup dialogs flows and define services call.
|
* Setup dialogs flows and define services call.
|
||||||
|
@ -58,70 +66,56 @@ export class MenuDialog extends IGBDialog {
|
||||||
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('/menu', [
|
min.dialogs.add(new WaterfallDialog('/menu', MenuDialog.getMenuDialog(min, service)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getMenuDialog(min: GBMinInstance, service: KBService) {
|
||||||
|
return [
|
||||||
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 args: MenuDialogArgs = step.options;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
let rootSubjectId = null;
|
let rootSubjectId = null;
|
||||||
|
|
||||||
if (step.options && step.options['data']) {
|
if (Object.keys(args).length > 0) {
|
||||||
const subject = step.options['data'];
|
|
||||||
|
|
||||||
// If there is a shortcut specified as subject destination, go there.
|
// If there is a shortcut specified as subject destination, go there.
|
||||||
|
if (args.to !== null) {
|
||||||
|
const dialog = args.to.split(':')[1];
|
||||||
|
|
||||||
if (subject.to) {
|
return await step.replaceDialog(`/${dialog}`);
|
||||||
const dialog = subject.to.split(':')[1];
|
|
||||||
await step.replaceDialog('/' + dialog);
|
|
||||||
await step.endDialog();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds to bot a perception of a new subject.
|
user.subjects.push(args);
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
const user = await min.userProfile.get(step.context, {});
|
rootSubjectId = args.subjectId === undefined ? null : args.subjectId;
|
||||||
user.subjects.push(subject);
|
|
||||||
rootSubjectId = subject.subjectId;
|
|
||||||
|
|
||||||
// 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 data = await service.getFaqBySubjectArray(
|
const list = await service.getFaqBySubjectArray('menu', user.subjects);
|
||||||
'menu',
|
|
||||||
user.subjects
|
|
||||||
);
|
|
||||||
await min.conversationalService.sendEvent(step, 'play', {
|
await min.conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'bullet',
|
playerType: 'bullet',
|
||||||
data: data.slice(0, 10)
|
data: list.slice(0, 10)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
user.subjects = [];
|
user.subjects = [];
|
||||||
|
await step.context.sendActivity(Messages[locale].here_is_subjects);
|
||||||
await step.context.sendActivity(Messages[locale].here_is_subjects); // TODO: Handle rnd.
|
|
||||||
user.isAsking = false;
|
user.isAsking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = MessageFactory.text('');
|
const msg = MessageFactory.text('');
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
|
const data = await service.getSubjectItems(min.instance.instanceId, rootSubjectId);
|
||||||
const data = await service.getSubjectItems(
|
|
||||||
min.instance.instanceId,
|
|
||||||
rootSubjectId
|
|
||||||
);
|
|
||||||
|
|
||||||
msg.attachmentLayout = 'carousel';
|
msg.attachmentLayout = 'carousel';
|
||||||
|
data.forEach((item: GuaribasSubject) => {
|
||||||
data.forEach(function(item: GuaribasSubject) {
|
|
||||||
const subject = item;
|
const subject = item;
|
||||||
const card = CardFactory.heroCard(
|
const card = CardFactory.heroCard(
|
||||||
subject.title,
|
subject.title,
|
||||||
subject.description,
|
subject.description,
|
||||||
CardFactory.images([
|
CardFactory.images([urlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')]),
|
||||||
UrlJoin('/kb', min.instance.kb, 'subjects', 'subject.png')
|
|
||||||
]),
|
|
||||||
CardFactory.actions([
|
CardFactory.actions([
|
||||||
{
|
{
|
||||||
channelData: null,
|
|
||||||
type: 'postBack',
|
type: 'postBack',
|
||||||
title: Messages[locale].menu_select,
|
title: Messages[locale].menu_select,
|
||||||
value: JSON.stringify({
|
value: JSON.stringify({
|
||||||
|
@ -134,30 +128,23 @@ export class MenuDialog extends IGBDialog {
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments.push(card);
|
attachments.push(card);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (attachments.length === 0) {
|
if (attachments.length === 0) {
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
|
|
||||||
if (user.subjects && user.subjects.length > 0) {
|
if (user.subjects && user.subjects.length > 0) {
|
||||||
await step.context.sendActivity(
|
await step.context.sendActivity(
|
||||||
Messages[locale].lets_search(
|
Messages[locale].lets_search(KBService.getFormattedSubjectItems(user.subjects))
|
||||||
KBService.getFormattedSubjectItems(user.subjects)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
msg.attachments = attachments;
|
msg.attachments = attachments;
|
||||||
await step.context.sendActivity(msg);
|
await step.context.sendActivity(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await min.userProfile.get(step.context, {});
|
|
||||||
user.isAsking = true;
|
user.isAsking = true;
|
||||||
|
|
||||||
return await step.next();
|
return await step.next();
|
||||||
}
|
}
|
||||||
]));
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,43 +36,37 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { GBMinInstance, IGBPackage } from 'botlib';
|
|
||||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
|
||||||
|
|
||||||
import { IGBCoreService } from 'botlib';
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { AskDialog } from './dialogs/AskDialog';
|
import { AskDialog } from './dialogs/AskDialog';
|
||||||
import { FaqDialog } from './dialogs/FaqDialog';
|
import { FaqDialog } from './dialogs/FaqDialog';
|
||||||
import { MenuDialog } from './dialogs/MenuDialog';
|
import { MenuDialog } from './dialogs/MenuDialog';
|
||||||
|
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from './models/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for kb.gbapp.
|
||||||
|
*/
|
||||||
export class GBKBPackage implements IGBPackage {
|
export class GBKBPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
public sysPackages: IGBPackage[] = null;
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
|
||||||
core.sequelize.addModels([
|
|
||||||
GuaribasAnswer,
|
|
||||||
GuaribasQuestion,
|
|
||||||
GuaribasSubject
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
|
}
|
||||||
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasAnswer, GuaribasQuestion, GuaribasSubject]);
|
||||||
}
|
}
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
|
||||||
AskDialog.setup(min.bot, min);
|
AskDialog.setup(min.bot, min);
|
||||||
FaqDialog.setup(min.bot, min);
|
FaqDialog.setup(min.bot, min);
|
||||||
MenuDialog.setup(min.bot, min);
|
MenuDialog.setup(min.bot, min);
|
||||||
|
|
||||||
}
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -60,6 +60,9 @@ import {
|
||||||
} from '../../core.gbapp/models/GBModel';
|
} from '../../core.gbapp/models/GBModel';
|
||||||
import { GuaribasUser } from '../../security.gblib/models';
|
import { GuaribasUser } from '../../security.gblib/models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subjects to group the pair of questions and answers.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasSubject extends Model<GuaribasSubject> {
|
export class GuaribasSubject extends Model<GuaribasSubject> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -111,6 +114,9 @@ export class GuaribasSubject extends Model<GuaribasSubject> {
|
||||||
public package: GuaribasPackage;
|
public package: GuaribasPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A question and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -155,6 +161,7 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
@UpdatedAt
|
@UpdatedAt
|
||||||
public updatedAt: Date;
|
public updatedAt: Date;
|
||||||
|
|
||||||
|
//tslint:disable-next-line:no-use-before-declare
|
||||||
@ForeignKey(() => GuaribasAnswer)
|
@ForeignKey(() => GuaribasAnswer)
|
||||||
@Column
|
@Column
|
||||||
public answerId: number;
|
public answerId: number;
|
||||||
|
@ -174,6 +181,9 @@ export class GuaribasQuestion extends Model<GuaribasQuestion> {
|
||||||
public package: GuaribasPackage;
|
public package: GuaribasPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An answer and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasAnswer extends Model<GuaribasAnswer> {
|
export class GuaribasAnswer extends Model<GuaribasAnswer> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -34,33 +34,38 @@
|
||||||
* @fileoverview Knowledge base services and logic.
|
* @fileoverview Knowledge base services and logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
|
import urlJoin = require('url-join');
|
||||||
const parse = require('bluebird').promisify(require('csv-parse'));
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const marked = require('marked');
|
const marked = require('marked');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const asyncPromise = require('async-promises');
|
const asyncPromise = require('async-promises');
|
||||||
const walkPromise = require('walk-promise');
|
const walkPromise = require('walk-promise');
|
||||||
import { Messages } from '../strings';
|
// tslint:disable-next-line:newline-per-chained-call
|
||||||
|
const parse = require('bluebird').promisify(require('csv-parse'));
|
||||||
|
|
||||||
import { IGBConversationalService, IGBCoreService, IGBInstance } from 'botlib';
|
import { GBDialogStep, GBLog, IGBConversationalService, IGBCoreService, IGBInstance } from 'botlib';
|
||||||
import { AzureSearch } from 'pragmatismo-io-framework';
|
import { AzureSearch } from 'pragmatismo-io-framework';
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
|
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
|
||||||
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
|
||||||
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
|
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
|
||||||
|
import { Messages } from '../strings';
|
||||||
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
|
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result for quey on KB data.
|
||||||
|
*/
|
||||||
export class KBServiceSearchResults {
|
export class KBServiceSearchResults {
|
||||||
public answer: GuaribasAnswer;
|
public answer: GuaribasAnswer;
|
||||||
public questionId: number;
|
public questionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All services related to knowledge base management.
|
||||||
|
*/
|
||||||
export class KBService {
|
export class KBService {
|
||||||
|
|
||||||
public sequelize: Sequelize;
|
public sequelize: Sequelize;
|
||||||
|
|
||||||
constructor(sequelize: Sequelize) {
|
constructor(sequelize: Sequelize) {
|
||||||
|
@ -68,7 +73,9 @@ export class KBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
|
public static getFormattedSubjectItems(subjects: GuaribasSubject[]) {
|
||||||
if (!subjects) { return ''; }
|
if (subjects === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
const out = [];
|
const out = [];
|
||||||
subjects.forEach(subject => {
|
subjects.forEach(subject => {
|
||||||
out.push(subject.title);
|
out.push(subject.title);
|
||||||
|
@ -86,10 +93,7 @@ export class KBService {
|
||||||
return out.join(' ');
|
return out.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getQuestionById(
|
public async getQuestionById(instanceId: number, questionId: number): Promise<GuaribasQuestion> {
|
||||||
instanceId: number,
|
|
||||||
questionId: number
|
|
||||||
): Promise<GuaribasQuestion> {
|
|
||||||
return GuaribasQuestion.findOne({
|
return GuaribasQuestion.findOne({
|
||||||
where: {
|
where: {
|
||||||
instanceId: instanceId,
|
instanceId: instanceId,
|
||||||
|
@ -98,10 +102,7 @@ export class KBService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAnswerById(
|
public async getAnswerById(instanceId: number, answerId: number): Promise<GuaribasAnswer> {
|
||||||
instanceId: number,
|
|
||||||
answerId: number
|
|
||||||
): Promise<GuaribasAnswer> {
|
|
||||||
return GuaribasAnswer.findOne({
|
return GuaribasAnswer.findOne({
|
||||||
where: {
|
where: {
|
||||||
instanceId: instanceId,
|
instanceId: instanceId,
|
||||||
|
@ -110,11 +111,7 @@ export class KBService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAnswerByText(
|
public async getAnswerByText(instanceId: number, text: string): Promise<any> {
|
||||||
instanceId: number,
|
|
||||||
text: string
|
|
||||||
): Promise<any> {
|
|
||||||
|
|
||||||
const Op = Sequelize.Op;
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
const question = await GuaribasQuestion.findOne({
|
const question = await GuaribasQuestion.findOne({
|
||||||
|
@ -124,7 +121,7 @@ export class KBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (question) {
|
if (question !== null) {
|
||||||
const answer = await GuaribasAnswer.findOne({
|
const answer = await GuaribasAnswer.findOne({
|
||||||
where: {
|
where: {
|
||||||
instanceId: instanceId,
|
instanceId: instanceId,
|
||||||
|
@ -135,18 +132,19 @@ export class KBService {
|
||||||
return Promise.resolve({ question: question, answer: answer });
|
return Promise.resolve({ question: question, answer: answer });
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
|
public async addAnswer(obj: GuaribasAnswer): Promise<GuaribasAnswer> {
|
||||||
return new Promise<GuaribasAnswer>(
|
return new Promise<GuaribasAnswer>((resolve, reject) => {
|
||||||
(resolve, reject) => {
|
GuaribasAnswer.create(obj)
|
||||||
GuaribasAnswer.create(obj).then(item => {
|
.then(item => {
|
||||||
resolve(item);
|
resolve(item);
|
||||||
}).error((reason) => {
|
})
|
||||||
|
.error(reason => {
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ask(
|
public async ask(
|
||||||
|
@ -155,7 +153,6 @@ export class KBService {
|
||||||
searchScore: number,
|
searchScore: number,
|
||||||
subjects: GuaribasSubject[]
|
subjects: GuaribasSubject[]
|
||||||
): Promise<KBServiceSearchResults> {
|
): Promise<KBServiceSearchResults> {
|
||||||
|
|
||||||
// Builds search query.
|
// Builds search query.
|
||||||
|
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
|
@ -165,51 +162,43 @@ export class KBService {
|
||||||
query = query.replace('/', ' ');
|
query = query.replace('/', ' ');
|
||||||
query = query.replace('\\', ' ');
|
query = query.replace('\\', ' ');
|
||||||
|
|
||||||
if (subjects) {
|
if (subjects !== null) {
|
||||||
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
|
const text = KBService.getSubjectItemsSeparatedBySpaces(subjects);
|
||||||
if (text) {
|
if (text !== null) {
|
||||||
query = `${query} ${text}`;
|
query = `${query} ${text}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
|
query = `${query}&$filter=instanceId eq ${instance.instanceId}`;
|
||||||
try {
|
|
||||||
if (instance.searchKey && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
// tslint:disable:no-unsafe-any
|
||||||
const service = new AzureSearch(
|
if (instance.searchKey !== null && GBConfigService.get('STORAGE_DIALECT') === 'mssql') {
|
||||||
instance.searchKey,
|
const service = new AzureSearch(
|
||||||
instance.searchHost,
|
instance.searchKey,
|
||||||
instance.searchIndex,
|
instance.searchHost,
|
||||||
instance.searchIndexer
|
instance.searchIndex,
|
||||||
);
|
instance.searchIndexer
|
||||||
const results = await service.search(query);
|
);
|
||||||
if (results && results.length > 0 &&
|
const results = await service.search(query);
|
||||||
results[0]['@search.score'] >= searchScore) {
|
|
||||||
const value = await this.getAnswerById(
|
if (results && results.length > 0 && results[0]['@search.score'] >= searchScore) {
|
||||||
instance.instanceId,
|
const value = await this.getAnswerById(instance.instanceId, results[0].answerId);
|
||||||
results[0].answerId);
|
if (value !== null) {
|
||||||
if (value) {
|
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
||||||
return Promise.resolve({ answer: value, questionId: results[0].questionId });
|
|
||||||
} else {
|
|
||||||
return Promise.resolve({ answer: null, questionId: 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const data = await this.getAnswerByText(instance.instanceId, query);
|
|
||||||
if (data) {
|
|
||||||
return Promise.resolve(
|
|
||||||
{ answer: data.answer, questionId: data.question.questionId });
|
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ answer: null, questionId: 0 });
|
return Promise.resolve({ answer: undefined, questionId: 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (reason) {
|
} else {
|
||||||
return Promise.reject(new Error(reason));
|
const data = await this.getAnswerByText(instance.instanceId, query);
|
||||||
|
if (data) {
|
||||||
|
return Promise.resolve({ answer: data.answer, questionId: data.question.questionId });
|
||||||
|
} else {
|
||||||
|
return Promise.resolve({ answer: undefined, questionId: 0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSubjectItems(
|
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
||||||
instanceId: number,
|
|
||||||
parentId: number
|
|
||||||
): Promise<GuaribasSubject[]> {
|
|
||||||
const where = { parentSubjectId: parentId, instanceId: instanceId };
|
const where = { parentSubjectId: parentId, instanceId: instanceId };
|
||||||
|
|
||||||
return GuaribasSubject.findAll({
|
return GuaribasSubject.findAll({
|
||||||
|
@ -218,31 +207,43 @@ export class KBService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
|
public async getFaqBySubjectArray(from: string, subjects: any): Promise<GuaribasQuestion[]> {
|
||||||
const where = {
|
|
||||||
from: from, subject1: null, subject2: null, subject3: null, subject4: null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subjects) {
|
if (subjects) {
|
||||||
if (subjects[0]) {
|
const where = {
|
||||||
|
from: from,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
|
subject1: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
|
subject2: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
|
subject3: null,
|
||||||
|
// tslint:disable-next-line: no-null-keyword
|
||||||
|
subject4: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (subjects[0] && subjects[0].internalId) {
|
||||||
where.subject1 = subjects[0].internalId;
|
where.subject1 = subjects[0].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[1]) {
|
if (subjects[1] && subjects[1].internalId) {
|
||||||
where.subject2 = subjects[1].internalId;
|
where.subject2 = subjects[1].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[2]) {
|
if (subjects[2] && subjects[2].internalId) {
|
||||||
where.subject3 = subjects[2].internalId;
|
where.subject3 = subjects[2].internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subjects[3]) {
|
if (subjects[3] && subjects[3].internalId) {
|
||||||
where.subject4 = subjects[3].internalId;
|
where.subject4 = subjects[3].internalId;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return await GuaribasQuestion.findAll({
|
return await GuaribasQuestion.findAll({
|
||||||
where: where
|
where: where
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
return await GuaribasQuestion.findAll({
|
||||||
|
where: { from: from }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importKbTabularFile(
|
public async importKbTabularFile(
|
||||||
|
@ -250,19 +251,17 @@ export class KBService {
|
||||||
instanceId: number,
|
instanceId: number,
|
||||||
packageId: number
|
packageId: number
|
||||||
): Promise<GuaribasQuestion[]> {
|
): Promise<GuaribasQuestion[]> {
|
||||||
|
|
||||||
const file = Fs.readFileSync(filePath, 'UCS-2');
|
const file = Fs.readFileSync(filePath, 'UCS-2');
|
||||||
const opts = {
|
const opts = {
|
||||||
delimiter: '\t'
|
delimiter: '\t'
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastQuestion: GuaribasQuestion;
|
let lastQuestionId: number;
|
||||||
let lastAnswer: GuaribasAnswer;
|
let lastAnswer: GuaribasAnswer;
|
||||||
|
|
||||||
const data = await parse(file, opts);
|
const data = await parse(file, opts);
|
||||||
|
|
||||||
return asyncPromise.eachSeries(data, async line => {
|
return asyncPromise.eachSeries(data, async line => {
|
||||||
|
|
||||||
// Extracts values from columns in the current line.
|
// Extracts values from columns in the current line.
|
||||||
|
|
||||||
const subjectsText = line[0];
|
const subjectsText = line[0];
|
||||||
|
@ -279,12 +278,12 @@ export class KBService {
|
||||||
// Extracts answer from external media if any.
|
// Extracts answer from external media if any.
|
||||||
|
|
||||||
if (answer.indexOf('.md') > -1) {
|
if (answer.indexOf('.md') > -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)) {
|
||||||
answer = Fs.readFileSync(mediaFilename, 'utf8');
|
answer = Fs.readFileSync(mediaFilename, 'utf8');
|
||||||
format = '.md';
|
format = '.md';
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
GBLog.info(`[GBImporter] File not found: ${mediaFilename}.`);
|
||||||
answer = '';
|
answer = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,13 +298,13 @@ export class KBService {
|
||||||
let indexer = 0;
|
let indexer = 0;
|
||||||
|
|
||||||
subjectArray.forEach(element => {
|
subjectArray.forEach(element => {
|
||||||
if (indexer == 0) {
|
if (indexer === 0) {
|
||||||
subject1 = subjectArray[indexer].substring(0, 63);
|
subject1 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 1) {
|
} else if (indexer === 1) {
|
||||||
subject2 = subjectArray[indexer].substring(0, 63);
|
subject2 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 2) {
|
} else if (indexer === 2) {
|
||||||
subject3 = subjectArray[indexer].substring(0, 63);
|
subject3 = subjectArray[indexer].substring(0, 63);
|
||||||
} else if (indexer == 3) {
|
} else if (indexer === 3) {
|
||||||
subject4 = subjectArray[indexer].substring(0, 63);
|
subject4 = subjectArray[indexer].substring(0, 63);
|
||||||
}
|
}
|
||||||
indexer++;
|
indexer++;
|
||||||
|
@ -318,7 +317,7 @@ export class KBService {
|
||||||
content: answer,
|
content: answer,
|
||||||
format: format,
|
format: format,
|
||||||
packageId: packageId,
|
packageId: packageId,
|
||||||
prevId: lastQuestion ? lastQuestion.questionId : 0
|
prevId: lastQuestionId !== null ? lastQuestionId : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const question1 = await GuaribasQuestion.create({
|
const question1 = await GuaribasQuestion.create({
|
||||||
|
@ -334,36 +333,31 @@ export class KBService {
|
||||||
packageId: packageId
|
packageId: packageId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (lastAnswer && lastQuestion) {
|
if (lastAnswer !== undefined && lastQuestionId !== 0) {
|
||||||
await lastAnswer.updateAttributes({ nextId: lastQuestion.questionId });
|
await lastAnswer.update({ nextId: lastQuestionId });
|
||||||
}
|
}
|
||||||
lastAnswer = answer1;
|
lastAnswer = answer1;
|
||||||
lastQuestion = question1;
|
lastQuestionId = question1.questionId;
|
||||||
|
|
||||||
return Promise.resolve(lastQuestion);
|
|
||||||
|
|
||||||
|
return Promise.resolve(question1.questionId);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Skips the header.
|
// Skips the header.
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendAnswer(conversationalService: IGBConversationalService,
|
public async sendAnswer(conversationalService: IGBConversationalService, step: GBDialogStep, answer: GuaribasAnswer) {
|
||||||
step: any, answer: GuaribasAnswer) {
|
|
||||||
|
|
||||||
if (answer.content.endsWith('.mp4')) {
|
if (answer.content.endsWith('.mp4')) {
|
||||||
await conversationalService.sendEvent(step, 'play', {
|
await conversationalService.sendEvent(step, 'play', {
|
||||||
playerType: 'video',
|
playerType: 'video',
|
||||||
data: answer.content
|
data: answer.content
|
||||||
});
|
});
|
||||||
} else if (answer.content.length > 140 &&
|
} else if (answer.content.length > 140 && step.context.activity.channelId === 'webchat') {
|
||||||
step.context._activity.channelId === 'webchat') {
|
|
||||||
const locale = step.context.activity.locale;
|
const locale = step.context.activity.locale;
|
||||||
|
|
||||||
await step.context.sendActivity(Messages[locale].will_answer_projector); // TODO: Handle rnd.
|
await step.context.sendActivity(Messages[locale].will_answer_projector);
|
||||||
let html = answer.content;
|
let html = answer.content;
|
||||||
|
|
||||||
if (answer.format === '.md') {
|
if (answer.format === '.md') {
|
||||||
|
@ -380,16 +374,18 @@ export class KBService {
|
||||||
});
|
});
|
||||||
html = marked(answer.content);
|
html = marked(answer.content);
|
||||||
}
|
}
|
||||||
await conversationalService.sendEvent(step, 'play',
|
await conversationalService.sendEvent(step, 'play', {
|
||||||
{
|
playerType: 'markdown',
|
||||||
playerType: 'markdown', data: {
|
data: {
|
||||||
content: html, answer: answer,
|
content: html,
|
||||||
prevId: answer.prevId, nextId: answer.nextId
|
answer: answer,
|
||||||
}
|
prevId: answer.prevId,
|
||||||
});
|
nextId: answer.nextId
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await step.context.sendActivity(answer.content);
|
await step.context.sendActivity(answer.content);
|
||||||
await conversationalService.sendEvent(step, 'stop', null);
|
await conversationalService.sendEvent(step, 'stop', undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,53 +394,32 @@ export class KBService {
|
||||||
packageStorage: GuaribasPackage,
|
packageStorage: GuaribasPackage,
|
||||||
instance: IGBInstance
|
instance: IGBInstance
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
|
|
||||||
// Imports subjects tree into database and return it.
|
// Imports subjects tree into database and return it.
|
||||||
|
|
||||||
await this.importSubjectFile(
|
await this.importSubjectFile(packageStorage.packageId, urlJoin(localPath, 'subjects.json'), instance);
|
||||||
packageStorage.packageId,
|
|
||||||
UrlJoin(localPath, 'subjects.json'),
|
|
||||||
instance);
|
|
||||||
|
|
||||||
// Import all .tsv files in the tabular directory.
|
// Import all .tsv files in the tabular directory.
|
||||||
|
|
||||||
return this.importKbTabularDirectory(
|
return this.importKbTabularDirectory(localPath, instance, packageStorage.packageId);
|
||||||
localPath,
|
}
|
||||||
instance,
|
|
||||||
packageStorage.packageId
|
public async importKbTabularDirectory(localPath: string, instance: IGBInstance, packageId: number): Promise<any> {
|
||||||
|
const files = await walkPromise(urlJoin(localPath, 'tabular'));
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
files.map(async file => {
|
||||||
|
if (file.name.endsWith('.tsv')) {
|
||||||
|
return this.importKbTabularFile(urlJoin(file.root, file.name), instance.instanceId, packageId);
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async importKbTabularDirectory(
|
public async importSubjectFile(packageId: number, filename: string, instance: IGBInstance): Promise<any> {
|
||||||
localPath: string,
|
const subjectsLoaded = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
||||||
instance: IGBInstance,
|
|
||||||
packageId: number
|
|
||||||
): Promise<any> {
|
|
||||||
|
|
||||||
const files = await walkPromise(UrlJoin(localPath, 'tabular'));
|
|
||||||
|
|
||||||
return Promise.all(files.map(async file => {
|
|
||||||
if (file.name.endsWith('.tsv')) {
|
|
||||||
return this.importKbTabularFile(
|
|
||||||
UrlJoin(file.root, file.name),
|
|
||||||
instance.instanceId,
|
|
||||||
packageId);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async importSubjectFile(
|
|
||||||
packageId: number,
|
|
||||||
filename: string,
|
|
||||||
instance: IGBInstance
|
|
||||||
): Promise<any> {
|
|
||||||
const subjects = JSON.parse(Fs.readFileSync(filename, 'utf8'));
|
|
||||||
|
|
||||||
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
|
const doIt = async (subjects: GuaribasSubject[], parentSubjectId: number) => {
|
||||||
return asyncPromise.eachSeries(subjects, async item => {
|
return asyncPromise.eachSeries(subjects, async item => {
|
||||||
const mediaFilename = item.id + '.png';
|
|
||||||
|
|
||||||
const value = await GuaribasSubject.create({
|
const value = await GuaribasSubject.create({
|
||||||
internalId: item.id,
|
internalId: item.id,
|
||||||
parentSubjectId: parentSubjectId,
|
parentSubjectId: parentSubjectId,
|
||||||
|
@ -463,15 +438,11 @@ export class KBService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return doIt(subjects.children, null);
|
|
||||||
|
return doIt(subjectsLoaded.children, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async undeployKbFromStorage(
|
public async undeployKbFromStorage(instance: IGBInstance, deployer: GBDeployer, packageId: number) {
|
||||||
instance: IGBInstance,
|
|
||||||
deployer: GBDeployer,
|
|
||||||
packageId: number
|
|
||||||
) {
|
|
||||||
|
|
||||||
await GuaribasQuestion.destroy({
|
await GuaribasQuestion.destroy({
|
||||||
where: { instanceId: instance.instanceId, packageId: packageId }
|
where: { instanceId: instance.instanceId, packageId: packageId }
|
||||||
});
|
});
|
||||||
|
@ -485,30 +456,26 @@ export class KBService {
|
||||||
where: { instanceId: instance.instanceId, packageId: packageId }
|
where: { instanceId: instance.instanceId, packageId: packageId }
|
||||||
});
|
});
|
||||||
|
|
||||||
await deployer.rebuildIndex(instance);
|
await deployer.rebuildIndex(instance, new AzureDeployerService(deployer).getKBSearchSchema(instance.searchIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deploys a knowledge base to the storage using the .gbkb format.
|
* Deploys a knowledge base to the storage using the .gbkb format.
|
||||||
*
|
*
|
||||||
* @param localPath Path to the .gbkb folder.
|
* @param localPath Path to the .gbkb folder.
|
||||||
*/
|
*/
|
||||||
public async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
|
public async deployKb(core: IGBCoreService, deployer: GBDeployer, localPath: string) {
|
||||||
const packageType = Path.extname(localPath);
|
const packageType = Path.extname(localPath);
|
||||||
const packageName = Path.basename(localPath);
|
const packageName = Path.basename(localPath);
|
||||||
logger.info(`[GBDeployer] Opening package: ${localPath}`);
|
GBLog.info(`[GBDeployer] Opening package: ${localPath}`);
|
||||||
const packageObject = JSON.parse(
|
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
|
||||||
Fs.readFileSync(UrlJoin(localPath, 'package.json'), 'utf8')
|
|
||||||
);
|
|
||||||
|
|
||||||
const instance = await core.loadInstance(packageObject.botId);
|
const instance = await core.loadInstance(packageObject.botId);
|
||||||
logger.info(`[GBDeployer] Importing: ${localPath}`);
|
GBLog.info(`[GBDeployer] Importing: ${localPath}`);
|
||||||
const p = await deployer.deployPackageToStorage(
|
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
|
||||||
instance.instanceId,
|
|
||||||
packageName);
|
|
||||||
await this.importKbPackage(localPath, p, instance);
|
await this.importKbPackage(localPath, p, instance);
|
||||||
|
|
||||||
deployer.rebuildIndex(instance);
|
deployer.rebuildIndex(instance, new AzureDeployerService(deployer).getKBSearchSchema(instance.searchIndex));
|
||||||
logger.info(`[GBDeployer] Finished import of ${localPath}`);
|
GBLog.info(`[GBDeployer] Finished import of ${localPath}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,8 @@ export const Messages = {
|
||||||
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 =>
|
||||||
`Vamos pesquisar sobre ${query}... O que deseja saber?`,
|
`Lets search for ${query}... What do you want to know?`,
|
||||||
see_faq:
|
see_faq: 'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
||||||
'Please take a look at the FAQ I\'ve prepared for you. You can click on them to get the answer.',
|
|
||||||
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...',
|
||||||
ask_first_time: 'What are you looking for?'
|
ask_first_time: 'What are you looking for?'
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,37 +36,35 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import urlJoin = require('url-join');
|
||||||
|
|
||||||
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { Sequelize } from 'sequelize-typescript';
|
import { Sequelize } from 'sequelize-typescript';
|
||||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
|
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for the security module.
|
||||||
|
*/
|
||||||
export class GBSecurityPackage implements IGBPackage {
|
export class GBSecurityPackage implements IGBPackage {
|
||||||
public sysPackages: IGBPackage[] = null;
|
public sysPackages: IGBPackage[];
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
core.sequelize.addModels([
|
GBLog.verbose(`getDialogs called.`);
|
||||||
GuaribasGroup,
|
|
||||||
GuaribasUser,
|
|
||||||
GuaribasUserGroup
|
|
||||||
]);
|
|
||||||
|
|
||||||
core;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadBot(min: GBMinInstance): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`loadBot called.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unloadBot(min: GBMinInstance): void {
|
public unloadBot(min: GBMinInstance): void {
|
||||||
|
GBLog.verbose(`unloadBot called.`);
|
||||||
|
}
|
||||||
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
}
|
}
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
|
core.sequelize.addModels([GuaribasGroup, GuaribasUser, GuaribasUserGroup]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,33 +36,23 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {
|
|
||||||
DataTypeDate,
|
|
||||||
DataTypeDecimal,
|
|
||||||
DataTypes,
|
|
||||||
DataTypeUUIDv4
|
|
||||||
} from 'sequelize';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
BelongsTo,
|
BelongsTo,
|
||||||
BelongsToMany,
|
|
||||||
Column,
|
Column,
|
||||||
CreatedAt,
|
|
||||||
DataType,
|
DataType,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
HasMany,
|
|
||||||
IsUUID,
|
|
||||||
Length,
|
Length,
|
||||||
Model,
|
Model,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Sequelize,
|
Table} from 'sequelize-typescript';
|
||||||
Table,
|
|
||||||
UpdatedAt
|
|
||||||
} from 'sequelize-typescript';
|
|
||||||
|
|
||||||
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A user and its metadata.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasUser extends Model<GuaribasUser> {
|
export class GuaribasUser extends Model<GuaribasUser> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -90,6 +80,9 @@ export class GuaribasUser extends Model<GuaribasUser> {
|
||||||
public instance: GuaribasInstance;
|
public instance: GuaribasInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A group of users.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasGroup extends Model<GuaribasGroup> {
|
export class GuaribasGroup extends Model<GuaribasGroup> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -109,6 +102,9 @@ export class GuaribasGroup extends Model<GuaribasGroup> {
|
||||||
public instance: GuaribasInstance;
|
public instance: GuaribasInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relation of groups and users.
|
||||||
|
*/
|
||||||
@Table
|
@Table
|
||||||
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
|
export class GuaribasUserGroup extends Model<GuaribasUserGroup> {
|
||||||
@ForeignKey(() => GuaribasUser)
|
@ForeignKey(() => GuaribasUser)
|
||||||
|
|
|
@ -1,69 +1,31 @@
|
||||||
/*****************************************************************************\
|
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
const Fs = require('fs');
|
||||||
const _ = require('lodash');
|
import urlJoin = require('url-join');
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
|
|
||||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
import { GBService, IGBInstance } from 'botlib';
|
||||||
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
|
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security service layer.
|
||||||
|
*/
|
||||||
export class SecService extends GBService {
|
export class SecService extends GBService {
|
||||||
|
|
||||||
public async importSecurityFile(localPath: string, instance: IGBInstance) {
|
public async importSecurityFile(localPath: string, instance: IGBInstance) {
|
||||||
const security = JSON.parse(
|
const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8'));
|
||||||
Fs.readFileSync(UrlJoin(localPath, 'security.json'), 'utf8')
|
|
||||||
);
|
|
||||||
security.groups.forEach(group => {
|
security.groups.forEach(group => {
|
||||||
const groupDb = GuaribasGroup.build({
|
const groupDb = GuaribasGroup.build({
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
displayName: group.displayName
|
displayName: group.displayName
|
||||||
});
|
});
|
||||||
groupDb.save().then(groupDb => {
|
groupDb.save().then(g1 => {
|
||||||
group.users.forEach(user => {
|
group.users.forEach(user => {
|
||||||
const userDb = GuaribasUser.build({
|
const userDb = GuaribasUser.build({
|
||||||
instanceId: instance.instanceId,
|
instanceId: instance.instanceId,
|
||||||
groupId: groupDb.groupId,
|
groupId: g1.groupId,
|
||||||
userName: user.userName
|
userName: user.userName
|
||||||
});
|
});
|
||||||
userDb.save().then(userDb => {
|
userDb.save().then(user2 => {
|
||||||
const userGroup = GuaribasUserGroup.build();
|
const userGroup = GuaribasUserGroup.build();
|
||||||
userGroup.groupId = groupDb.groupId;
|
userGroup.groupId = g1.groupId;
|
||||||
userGroup.userId = userDb.userId;
|
userGroup.userId = user2.userId;
|
||||||
userGroup.save();
|
userGroup.save();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -79,16 +41,15 @@ export class SecService extends GBService {
|
||||||
channelName: string,
|
channelName: string,
|
||||||
displayName: string
|
displayName: string
|
||||||
): Promise<GuaribasUser> {
|
): Promise<GuaribasUser> {
|
||||||
return new Promise<GuaribasUser>(
|
return new Promise<GuaribasUser>((resolve, reject) => {
|
||||||
(resolve, reject) => {
|
GuaribasUser.findOne({
|
||||||
|
attributes: ['instanceId', 'internalAddress'],
|
||||||
GuaribasUser.findOne({
|
where: {
|
||||||
attributes: ['instanceId', 'internalAddress'],
|
instanceId: instanceId,
|
||||||
where: {
|
userSystemId: userSystemId
|
||||||
instanceId: instanceId,
|
}
|
||||||
userSystemId: userSystemId
|
})
|
||||||
}
|
.then(user => {
|
||||||
}).then(user => {
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = GuaribasUser.build();
|
user = GuaribasUser.build();
|
||||||
}
|
}
|
||||||
|
@ -100,7 +61,8 @@ export class SecService extends GBService {
|
||||||
user.defaultChannel = channelName;
|
user.defaultChannel = channelName;
|
||||||
user.save();
|
user.save();
|
||||||
resolve(user);
|
resolve(user);
|
||||||
}).error(reject);
|
})
|
||||||
});
|
.error(reject);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -36,39 +36,46 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const UrlJoin = require('url-join');
|
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
|
|
||||||
import { 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package for whatsapp.gblib
|
||||||
|
*/
|
||||||
export class GBWhatsappPackage implements IGBPackage {
|
export class GBWhatsappPackage implements IGBPackage {
|
||||||
|
public sysPackages: IGBPackage[];
|
||||||
|
|
||||||
public sysPackages: IGBPackage[] = null;
|
public channel: WhatsappDirectLine;
|
||||||
public channel: WhatsappDirectLine;
|
|
||||||
|
|
||||||
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
public loadBot(min: GBMinInstance): void {
|
||||||
|
// Only loads engine if it is defined on services.json.
|
||||||
|
|
||||||
|
if (min.instance.whatsappBotKey !== undefined) {
|
||||||
|
this.channel = new WhatsappDirectLine(
|
||||||
|
min.botId,
|
||||||
|
min.instance.whatsappBotKey,
|
||||||
|
min.instance.whatsappServiceKey,
|
||||||
|
min.instance.whatsappServiceNumber,
|
||||||
|
min.instance.whatsappServiceUrl,
|
||||||
|
min.instance.whatsappServiceWebhookUrl
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public unloadPackage(core: IGBCoreService): void {
|
public getDialogs(min: GBMinInstance) {
|
||||||
|
GBLog.verbose(`getDialogs called.`);
|
||||||
}
|
}
|
||||||
|
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {
|
||||||
public loadBot(min: GBMinInstance): void {
|
GBLog.verbose(`loadPackage called.`);
|
||||||
|
}
|
||||||
// Only loads engine if it is defined on services.json.
|
public unloadPackage(core: IGBCoreService): void {
|
||||||
|
GBLog.verbose(`unloadPackage called.`);
|
||||||
if (min.instance.whatsappBotKey) {
|
}
|
||||||
this.channel = new WhatsappDirectLine(min.botId, min.instance.whatsappBotKey, min.instance.whatsappServiceKey,
|
public unloadBot(min: GBMinInstance): void {
|
||||||
min.instance.whatsappServiceNumber, min.instance.whatsappServiceUrl, min.instance.whatsappServiceWebhookUrl);
|
GBLog.verbose(`unloadBot called.`);
|
||||||
}
|
}
|
||||||
}
|
public onNewSession(min: GBMinInstance, step: GBDialogStep): void {
|
||||||
|
GBLog.verbose(`onNewSession called.`);
|
||||||
public unloadBot(min: GBMinInstance): void {
|
}
|
||||||
|
|
||||||
}
|
|
||||||
public onNewSession(min: GBMinInstance, step: any): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,267 +1,218 @@
|
||||||
/*****************************************************************************\
|
import urlJoin = require('url-join');
|
||||||
| ( )_ _ |
|
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
|
||||||
| | | ( )_) | |
|
|
||||||
| (_) \___/' |
|
|
||||||
| |
|
|
||||||
| 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. |
|
|
||||||
| |
|
|
||||||
\*****************************************************************************/
|
|
||||||
|
|
||||||
const Path = require('path');
|
|
||||||
const Fs = require('fs');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const Parse = require('csv-parse');
|
|
||||||
const Async = require('async');
|
|
||||||
const UrlJoin = require('url-join');
|
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
|
||||||
const Swagger = require('swagger-client');
|
const Swagger = require('swagger-client');
|
||||||
const rp = require('request-promise');
|
const rp = require('request-promise');
|
||||||
|
import { GBLog, GBService } from 'botlib';
|
||||||
import * as request from 'request-promise-native';
|
import * as request from 'request-promise-native';
|
||||||
|
|
||||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
/**
|
||||||
|
* Support for Whatsapp.
|
||||||
|
*/
|
||||||
export class WhatsappDirectLine extends GBService {
|
export class WhatsappDirectLine extends GBService {
|
||||||
|
public pollInterval = 1000;
|
||||||
|
public directLineClientName = 'DirectLineClient';
|
||||||
|
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
||||||
|
|
||||||
public pollInterval = 1000;
|
public directLineClient: any;
|
||||||
public directLineClientName = 'DirectLineClient';
|
public whatsappServiceKey: string;
|
||||||
public directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
|
public whatsappServiceNumber: string;
|
||||||
|
public whatsappServiceUrl: string;
|
||||||
|
public whatsappServiceWebhookUrl: string;
|
||||||
|
public botId: string;
|
||||||
|
public watermark: string;
|
||||||
|
|
||||||
public directLineClient: any;
|
public conversationIds = {};
|
||||||
public whatsappServiceKey: string;
|
|
||||||
public whatsappServiceNumber: string;
|
|
||||||
public whatsappServiceUrl: string;
|
|
||||||
public whatsappServiceWebhookUrl: string;
|
|
||||||
public botId: string;
|
|
||||||
public watermark: string = null;
|
|
||||||
|
|
||||||
public conversationIds = {};
|
constructor(
|
||||||
|
botId,
|
||||||
|
directLineSecret,
|
||||||
|
whatsappServiceKey,
|
||||||
|
whatsappServiceNumber,
|
||||||
|
whatsappServiceUrl,
|
||||||
|
whatsappServiceWebhookUrl
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
constructor(botId, directLineSecret, whatsappServiceKey, whatsappServiceNumber, whatsappServiceUrl, whatsappServiceWebhookUrl) {
|
this.botId = botId;
|
||||||
|
this.whatsappServiceKey = whatsappServiceKey;
|
||||||
|
this.whatsappServiceNumber = whatsappServiceNumber;
|
||||||
|
this.whatsappServiceUrl = whatsappServiceUrl;
|
||||||
|
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl;
|
||||||
|
|
||||||
super();
|
this.directLineClient = rp(this.directLineSpecUrl)
|
||||||
|
.then(spec => {
|
||||||
this.botId = botId;
|
return new Swagger({
|
||||||
this.whatsappServiceKey = whatsappServiceKey;
|
spec: JSON.parse(spec.trim()),
|
||||||
this.whatsappServiceNumber = whatsappServiceNumber;
|
usePromise: true
|
||||||
this.whatsappServiceUrl = whatsappServiceUrl;
|
|
||||||
this.whatsappServiceWebhookUrl = whatsappServiceWebhookUrl;
|
|
||||||
|
|
||||||
// TODO: Migrate to Swagger 3.
|
|
||||||
this.directLineClient = rp(this.directLineSpecUrl)
|
|
||||||
.then((spec) => {
|
|
||||||
return new Swagger({
|
|
||||||
spec: JSON.parse(spec.trim()),
|
|
||||||
usePromise: true
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(async (client) => {
|
|
||||||
client.clientAuthorizations.add('AuthorizationBotConnector',
|
|
||||||
new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' +
|
|
||||||
directLineSecret, 'header'));
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
method: 'POST',
|
|
||||||
url: UrlJoin(this.whatsappServiceUrl, 'webhook'),
|
|
||||||
qs:
|
|
||||||
{
|
|
||||||
token: this.whatsappServiceKey,
|
|
||||||
webhookUrl: `${this.whatsappServiceWebhookUrl}/instances/${this.botId}/whatsapp`,
|
|
||||||
set: true
|
|
||||||
},
|
|
||||||
headers:
|
|
||||||
{
|
|
||||||
'cache-control': 'no-cache'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await request.post(options);
|
|
||||||
logger.info(result);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error initializing 3rd party Whatsapp provider.', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return client;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
logger.error('Error initializing DirectLine client', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public received(req, res) {
|
|
||||||
const text = req.body.messages[0].body;
|
|
||||||
const from = req.body.messages[0].author.split('@')[0];
|
|
||||||
const fromName = req.body.messages[0].senderName;
|
|
||||||
|
|
||||||
if (req.body.messages[0].fromMe) {
|
|
||||||
return; // Exit here.
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`);
|
|
||||||
|
|
||||||
const conversationId = this.conversationIds[from];
|
|
||||||
|
|
||||||
this.directLineClient.then((client) => {
|
|
||||||
|
|
||||||
if (this.conversationIds[from] == null) {
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Starting new conversation on Bot.`);
|
|
||||||
client.Conversations.Conversations_StartConversation()
|
|
||||||
.then((response) => {
|
|
||||||
return response.obj.conversationId;
|
|
||||||
})
|
|
||||||
.then((conversationId) => {
|
|
||||||
|
|
||||||
this.conversationIds[from] = conversationId;
|
|
||||||
this.inputMessage(client, conversationId, text,
|
|
||||||
from, fromName);
|
|
||||||
|
|
||||||
this.pollMessages(client, conversationId, from, fromName);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('Error starting conversation', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.inputMessage(client, conversationId, text,
|
|
||||||
from, fromName);
|
|
||||||
}
|
|
||||||
res.end();
|
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
|
.then(async client => {
|
||||||
|
client.clientAuthorizations.add(
|
||||||
|
'AuthorizationBotConnector',
|
||||||
|
new Swagger.ApiKeyAuthorization('Authorization', `Bearer ${directLineSecret}`, 'header')
|
||||||
|
);
|
||||||
|
|
||||||
public inputMessage(client, conversationId, text, from, fromName) {
|
|
||||||
|
|
||||||
client.Conversations.Conversations_PostActivity(
|
|
||||||
{
|
|
||||||
conversationId: conversationId,
|
|
||||||
activity: {
|
|
||||||
textFormat: 'plain',
|
|
||||||
text: text,
|
|
||||||
type: 'message',
|
|
||||||
from: {
|
|
||||||
id: from,
|
|
||||||
name: fromName
|
|
||||||
},
|
|
||||||
replyToId: from
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
logger.error(`GBWhatsapp: Error receiving message: ${err}.`);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public pollMessages(client, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
logger.info(`GBWhatsapp: Starting polling message for conversationId:
|
|
||||||
${conversationId}.`);
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
client.Conversations.Conversations_GetActivities({
|
|
||||||
conversationId:
|
|
||||||
conversationId, watermark: this.watermark
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
this.watermark = response.obj.watermark;
|
|
||||||
return response.obj.activities;
|
|
||||||
})
|
|
||||||
.then((activities) => {
|
|
||||||
this.printMessages(activities, conversationId, from, fromName);
|
|
||||||
});
|
|
||||||
}, this.pollInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessages(activities, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
if (activities && activities.length) {
|
|
||||||
|
|
||||||
// Ignore own messages.
|
|
||||||
|
|
||||||
activities = activities.filter((m) => (m.from.id === 'GeneralBots') && m.type === 'message');
|
|
||||||
|
|
||||||
if (activities.length) {
|
|
||||||
|
|
||||||
// Print other messages.
|
|
||||||
|
|
||||||
activities.forEach(activity => {
|
|
||||||
this.printMessage(activity, conversationId, from, fromName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public printMessage(activity, conversationId, from, fromName) {
|
|
||||||
|
|
||||||
let output = '';
|
|
||||||
|
|
||||||
if (activity.text) {
|
|
||||||
logger.info(`GBWhatsapp: MSG: ${activity.text}`);
|
|
||||||
output = activity.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activity.attachments) {
|
|
||||||
activity.attachments.forEach((attachment) => {
|
|
||||||
switch (attachment.contentType) {
|
|
||||||
case 'application/vnd.microsoft.card.hero':
|
|
||||||
output += `\n${this.renderHeroCard(attachment)}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'image/png':
|
|
||||||
logger.info('Opening the requested image ' + attachment.contentUrl);
|
|
||||||
output += `\n${attachment.contentUrl}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sendToDevice(conversationId, from, fromName, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderHeroCard(attachment) {
|
|
||||||
return `${attachment.content.title} - ${attachment.content.text}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendToDevice(conversationId, to, toName, msg) {
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: UrlJoin(this.whatsappServiceUrl, 'message'),
|
url: urlJoin(this.whatsappServiceUrl, 'webhook'),
|
||||||
qs:
|
qs: {
|
||||||
{
|
token: this.whatsappServiceKey,
|
||||||
token: this.whatsappServiceKey,
|
webhookUrl: `${this.whatsappServiceWebhookUrl}/instances/${this.botId}/whatsapp`,
|
||||||
phone: to,
|
set: true
|
||||||
body: msg
|
},
|
||||||
},
|
headers: {
|
||||||
headers:
|
'cache-control': 'no-cache'
|
||||||
{
|
}
|
||||||
'cache-control': 'no-cache'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await request.get(options);
|
try {
|
||||||
|
const result = request.post(options);
|
||||||
|
GBLog.info(result);
|
||||||
|
} catch (error) {
|
||||||
|
GBLog.error(`Error initializing 3rd party Whatsapp provider(1) ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error initializing 3rd party Whatsapp provider(2) ${err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public received(req, res) {
|
||||||
|
const text = req.body.messages[0].body;
|
||||||
|
const from = req.body.messages[0].author.split('@')[0];
|
||||||
|
const fromName = req.body.messages[0].senderName;
|
||||||
|
|
||||||
|
if (req.body.messages[0].fromMe) {
|
||||||
|
return; // Exit here.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBLog.info(`GBWhatsapp: Hook called. from: ${from}(${fromName}), text: ${text})`);
|
||||||
|
|
||||||
|
const conversationId = this.conversationIds[from];
|
||||||
|
|
||||||
|
this.directLineClient.then(client => {
|
||||||
|
if (this.conversationIds[from] === undefined) {
|
||||||
|
GBLog.info(`GBWhatsapp: Starting new conversation on Bot.`);
|
||||||
|
client.Conversations.Conversations_StartConversation()
|
||||||
|
.then(response => {
|
||||||
|
return response.obj.conversationId;
|
||||||
|
})
|
||||||
|
.then(generatedConversationId => {
|
||||||
|
this.conversationIds[from] = generatedConversationId;
|
||||||
|
this.inputMessage(client, generatedConversationId, text, from, fromName);
|
||||||
|
|
||||||
|
this.pollMessages(client, generatedConversationId, from, fromName);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
GBLog.error(`Error starting conversation ${err}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.inputMessage(client, conversationId, text, from, fromName);
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public inputMessage(client, conversationId, text, from, fromName) {
|
||||||
|
client.Conversations.Conversations_PostActivity({
|
||||||
|
conversationId: conversationId,
|
||||||
|
activity: {
|
||||||
|
textFormat: 'plain',
|
||||||
|
text: text,
|
||||||
|
type: 'message',
|
||||||
|
from: {
|
||||||
|
id: from,
|
||||||
|
name: fromName
|
||||||
|
},
|
||||||
|
replyToId: from
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
GBLog.error(`GBWhatsapp: Error receiving message: ${err}.`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public pollMessages(client, conversationId, from, fromName) {
|
||||||
|
GBLog.info(`GBWhatsapp: Starting polling message for conversationId:
|
||||||
|
${conversationId}.`);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
client.Conversations.Conversations_GetActivities({
|
||||||
|
conversationId: conversationId,
|
||||||
|
watermark: this.watermark
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.watermark = response.obj.watermark;
|
||||||
|
|
||||||
|
return response.obj.activities;
|
||||||
|
})
|
||||||
|
.then(activities => {
|
||||||
|
this.printMessages(activities, conversationId, from, fromName);
|
||||||
|
});
|
||||||
|
}, this.pollInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public printMessages(activities, conversationId, from, fromName) {
|
||||||
|
if (activities && activities.length) {
|
||||||
|
// Ignore own messages.
|
||||||
|
|
||||||
|
activities = activities.filter(m => m.from.id === 'GeneralBots' && m.type === 'message');
|
||||||
|
|
||||||
|
if (activities.length) {
|
||||||
|
// Print other messages.
|
||||||
|
|
||||||
|
activities.forEach(activity => {
|
||||||
|
this.printMessage(activity, conversationId, from, fromName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public printMessage(activity, conversationId, from, fromName) {
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
if (activity.text) {
|
||||||
|
GBLog.info(`GBWhatsapp: MSG: ${activity.text}`);
|
||||||
|
output = activity.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activity.attachments) {
|
||||||
|
activity.attachments.forEach(attachment => {
|
||||||
|
switch (attachment.contentType) {
|
||||||
|
case 'application/vnd.microsoft.card.hero':
|
||||||
|
output += `\n${this.renderHeroCard(attachment)}`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'image/png':
|
||||||
|
GBLog.info(`Opening the requested image ${attachment.contentUrl}`);
|
||||||
|
output += `\n${attachment.contentUrl}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GBLog.info(`Unknown content type: ${attachment.contentType}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendToDevice(from, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHeroCard(attachment) {
|
||||||
|
return `${attachment.content.title} - ${attachment.content.text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendToDevice(to, msg) {
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
url: urlJoin(this.whatsappServiceUrl, 'message'),
|
||||||
|
qs: {
|
||||||
|
token: this.whatsappServiceKey,
|
||||||
|
phone: to,
|
||||||
|
body: msg
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'cache-control': 'no-cache'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
60
src/app.ts
60
src/app.ts
|
@ -3,7 +3,7 @@
|
||||||
| ( )_ _ |
|
| ( )_ _ |
|
||||||
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
|
||||||
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
| | | ( )_) | |
|
| | | ( )_) | |
|
||||||
| (_) \___/' |
|
| (_) \___/' |
|
||||||
|
@ -37,22 +37,20 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const logger = require('./logger');
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
import { IGBInstance, IGBPackage } from 'botlib';
|
|
||||||
|
import { GBLog, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
|
||||||
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService';
|
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService';
|
||||||
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
|
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
import { GuaribasInstance } from '../packages/core.gbapp/models/GBModel';
|
|
||||||
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
|
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService';
|
||||||
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
|
import { GBConversationalService } from '../packages/core.gbapp/services/GBConversationalService';
|
||||||
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
|
import { GBCoreService } from '../packages/core.gbapp/services/GBCoreService';
|
||||||
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../packages/core.gbapp/services/GBDeployer';
|
||||||
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
|
import { GBImporter } from '../packages/core.gbapp/services/GBImporterService';
|
||||||
import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
|
import { GBMinService } from '../packages/core.gbapp/services/GBMinService';
|
||||||
import { GBVMService } from '../packages/core.gbapp/services/GBVMService';
|
|
||||||
|
|
||||||
const appPackages = new Array<IGBPackage>();
|
const appPackages: IGBPackage[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General Bots open-core entry point.
|
* General Bots open-core entry point.
|
||||||
|
@ -63,34 +61,29 @@ export class GBServer {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static run() {
|
public static run() {
|
||||||
logger.info(`The Bot Server is in STARTING mode...`);
|
GBLog.info(`The Bot Server is in STARTING mode...`);
|
||||||
|
|
||||||
// Creates a basic HTTP server that will serve several URL, one for each
|
// Creates a basic HTTP server that will serve several URL, one for each
|
||||||
// bot instance. This allows the same server to attend multiple Bot on
|
// bot instance.
|
||||||
// the Marketplace until GB get serverless.
|
|
||||||
|
|
||||||
const port = process.env.port || process.env.PORT || 4242;
|
const port = GBConfigService.getServerPort();
|
||||||
const server = express();
|
const server = express();
|
||||||
|
server.use(bodyParser.json());
|
||||||
server.use(bodyParser.json()); // to support JSON-encoded bodies
|
|
||||||
server.use(
|
server.use(
|
||||||
bodyParser.urlencoded({
|
bodyParser.urlencoded({
|
||||||
// to support URL-encoded bodies
|
|
||||||
extended: true
|
extended: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let bootInstance: IGBInstance;
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
logger.info(`Now accepting connections on ${port}...`);
|
GBLog.info(`Now accepting connections on ${port}...`);
|
||||||
|
|
||||||
// Reads basic configuration, initialize minimal services.
|
// Reads basic configuration, initialize minimal services.
|
||||||
|
|
||||||
GBConfigService.init();
|
GBConfigService.init();
|
||||||
const core = new GBCoreService();
|
const core: IGBCoreService = new GBCoreService();
|
||||||
core.ensureAdminIsSecured();
|
|
||||||
|
|
||||||
const importer: GBImporter = new GBImporter(core);
|
const importer: GBImporter = new GBImporter(core);
|
||||||
const deployer: GBDeployer = new GBDeployer(core, importer);
|
const deployer: GBDeployer = new GBDeployer(core, importer);
|
||||||
|
@ -100,40 +93,46 @@ export class GBServer {
|
||||||
|
|
||||||
// Ensure that local development proxy is setup.
|
// Ensure that local development proxy is setup.
|
||||||
|
|
||||||
logger.info(`Establishing a development local proxy (ngrok)...`);
|
GBLog.info(`Establishing a development local proxy (ngrok)...`);
|
||||||
const proxyAddress: string = await core.ensureProxy(port);
|
const proxyAddress: string = await core.ensureProxy(port);
|
||||||
|
|
||||||
// Creates a boot instance or load it frmo storage.
|
// Creates a boot instance or load it from storage.
|
||||||
|
|
||||||
let bootInstance: IGBInstance = null;
|
let bootInstance: IGBInstance;
|
||||||
try {
|
try {
|
||||||
await core.initStorage();
|
await core.initStorage();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
GBLog.verbose(`Error initializing storage: ${error}`);
|
||||||
bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
|
bootInstance = await core.createBootInstance(core, azureDeployer, proxyAddress);
|
||||||
await core.initStorage();
|
await core.initStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.ensureAdminIsSecured();
|
||||||
|
|
||||||
// Deploys system and user packages.
|
// Deploys system and user packages.
|
||||||
|
|
||||||
logger.info(`Deploying packages...`);
|
GBLog.info(`Deploying packages...`);
|
||||||
await core.loadSysPackages(core);
|
core.loadSysPackages(core);
|
||||||
await core.checkStorage(azureDeployer);
|
await core.checkStorage(azureDeployer);
|
||||||
await deployer.deployPackages(core, server, appPackages);
|
await deployer.deployPackages(core, server, appPackages);
|
||||||
|
|
||||||
|
|
||||||
// Loads all bot instances.
|
// Loads all bot instances.
|
||||||
|
|
||||||
logger.info(`Publishing instances...`);
|
GBLog.info(`Publishing instances...`);
|
||||||
const packageInstance = await importer.importIfNotExistsBotPackage(
|
const packageInstance = await importer.importIfNotExistsBotPackage(
|
||||||
GBConfigService.get('CLOUD_GROUP'),
|
GBConfigService.get('CLOUD_GROUP'),
|
||||||
'boot.gbot',
|
'boot.gbot',
|
||||||
'packages/boot.gbot'
|
'packages/boot.gbot'
|
||||||
);
|
);
|
||||||
|
if (bootInstance === undefined) {
|
||||||
|
bootInstance = packageInstance;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:prefer-object-spread
|
||||||
const fullInstance = Object.assign(packageInstance, bootInstance);
|
const fullInstance = Object.assign(packageInstance, bootInstance);
|
||||||
await core.saveInstance(fullInstance);
|
await core.saveInstance(fullInstance);
|
||||||
let instances: GuaribasInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress);
|
let instances: IGBInstance[] = await core.loadAllInstances(core, azureDeployer, proxyAddress);
|
||||||
instances = await core.ensureInstances(instances, bootInstance, core);
|
instances = await core.ensureInstances(instances, bootInstance, core);
|
||||||
if (!bootInstance) {
|
if (bootInstance !== undefined) {
|
||||||
bootInstance = instances[0];
|
bootInstance = instances[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,18 +143,15 @@ export class GBServer {
|
||||||
|
|
||||||
// Deployment of local applications for the first time.
|
// Deployment of local applications for the first time.
|
||||||
|
|
||||||
deployer.installDefaultGBUI();
|
deployer.runOnce();
|
||||||
|
|
||||||
logger.info(`The Bot Server is in RUNNING mode...`);
|
GBLog.info(`The Bot Server is in RUNNING mode...`);
|
||||||
|
|
||||||
// Opens Navigator.
|
// Opens Navigator.
|
||||||
|
|
||||||
core.openBrowserInDevelopment();
|
core.openBrowserInDevelopment();
|
||||||
|
|
||||||
return core;
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
GBLog.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"*": ["types/*"],
|
||||||
"botlib/*": ["node_modules/botlib/*"],
|
"botlib/*": ["node_modules/botlib/*"],
|
||||||
"pragmatismo-io-framework/*": ["node_modules/pragmatismo-io-framework/*"]
|
"pragmatismo-io-framework/*": ["node_modules/pragmatismo-io-framework/*"]
|
||||||
},
|
},
|
||||||
|
|
81
tslint.json
81
tslint.json
|
@ -1,42 +1,43 @@
|
||||||
{
|
{
|
||||||
"defaultSeverity": "warning",
|
"defaultSeverity": "warning",
|
||||||
"extends": [
|
"extends": ["tslint:recommended", "tslint-microsoft-contrib"],
|
||||||
"tslint:recommended",
|
"linterOptions": {
|
||||||
"tslint-microsoft-contrib"
|
"exclude": [
|
||||||
],
|
"libraries/botframework-connector/src/generated/**/*",
|
||||||
"linterOptions": {
|
"libraries/botframework-schema/**/*",
|
||||||
"exclude":[
|
"./packages/default.gbui/**/*",
|
||||||
"libraries/botframework-connector/src/generated/**/*",
|
"./packages/**/*.gbdialog"
|
||||||
"libraries/botframework-schema/**/*"
|
]
|
||||||
]
|
},
|
||||||
},
|
"rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
|
||||||
"rulesDirectory": [
|
"jsRules": {},
|
||||||
"node_modules/tslint-microsoft-contrib"
|
"rules": {
|
||||||
],
|
"newline-per-chained-call": false,
|
||||||
"jsRules": {},
|
"no-unsafe-any": false,
|
||||||
"rules": {
|
"no-floating-promises": false,
|
||||||
"no-var-requires":false,
|
"no-var-requires": false,
|
||||||
"typedef":false,
|
"typedef": false,
|
||||||
"variable-name": false,
|
"variable-name": false,
|
||||||
"no-parameter-properties": false,
|
"no-parameter-properties": false,
|
||||||
"no-reserved-keywords": false,
|
"max-line-length": [true, { "limit": 120, "ignore-pattern": "^\\s+\\*" }],
|
||||||
"no-unnecessary-class":false,
|
"await-promise": [true, "Bluebird"],
|
||||||
"no-require-imports": false,
|
"no-reserved-keywords": false,
|
||||||
"function-name": false,
|
"no-unnecessary-class": false,
|
||||||
"no-redundant-jsdoc": false,
|
"no-require-imports": false,
|
||||||
"no-return-await": false,
|
"function-name": false,
|
||||||
"prefer-type-cast": false,
|
"no-relative-imports": false,
|
||||||
"no-object-literal-type-assertion":false,
|
"no-redundant-jsdoc": false,
|
||||||
"no-increment-decrement":false,
|
"no-return-await": false,
|
||||||
"no-any":false,
|
"prefer-type-cast": false,
|
||||||
"interface-name":false,
|
"non-literal-fs-path": false,
|
||||||
"no-this-assignment":false,
|
"no-object-literal-type-assertion": false,
|
||||||
"switch-final-break":false,
|
"no-increment-decrement": false,
|
||||||
"no-parameter-reassignment":false,
|
"no-any": false,
|
||||||
"export-name":false,
|
"interface-name": false,
|
||||||
"no-relative-imports": false,
|
"no-this-assignment": false,
|
||||||
"no-backbone-get-set-outside-model": false,
|
"switch-final-break": false,
|
||||||
"max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}],
|
"no-parameter-reassignment": false,
|
||||||
"await-promise": [true, "Bluebird"]
|
"export-name": false,
|
||||||
}
|
"no-backbone-get-set-outside-model": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue