Merge pull request #87 from rodrigorodriguez/master
Alpha BASIC 2 support
This commit is contained in:
commit
11aa599c3d
19 changed files with 2098 additions and 2106 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
|
||||||
|
|
2552
package-lock.json
generated
2552
package-lock.json
generated
File diff suppressed because it is too large
Load diff
44
package.json
44
package.json
|
@ -51,7 +51,7 @@
|
||||||
"@microsoft/microsoft-graph-client": "1.4.0",
|
"@microsoft/microsoft-graph-client": "1.4.0",
|
||||||
"@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",
|
||||||
|
@ -60,39 +60,39 @@
|
||||||
"azure-arm-website": "5.7.0",
|
"azure-arm-website": "5.7.0",
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.3",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"botbuilder": "^4.1.7",
|
"botbuilder": "^4.2.1",
|
||||||
"botbuilder-ai": "^4.2.0",
|
"botbuilder-ai": "^4.2.1",
|
||||||
"botbuilder-azure": "^4.2.0",
|
"botbuilder-azure": "^4.2.1",
|
||||||
"botbuilder-choices": "^4.0.0-preview1.2",
|
"botbuilder-choices": "^4.0.0-preview1.2",
|
||||||
"botbuilder-dialogs": "^4.2.0",
|
"botbuilder-dialogs": "^4.2.1",
|
||||||
"botbuilder-prompts": "^4.0.0-preview1.2",
|
"botbuilder-prompts": "^4.0.0-preview1.2",
|
||||||
"botlib": "^0.1.10",
|
"botlib": "^0.1.12",
|
||||||
"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.2",
|
||||||
"cli-spinner": "^0.2.8",
|
"cli-spinner": "^0.2.8",
|
||||||
"csv-parse": "4.3.1",
|
"csv-parse": "4.3.3",
|
||||||
"dotenv-extended": "2.3.0",
|
"dotenv-extended": "2.3.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.8.9",
|
||||||
"localize": "0.4.7",
|
"localize": "0.4.7",
|
||||||
"marked": "0.6.0",
|
"marked": "0.6.1",
|
||||||
"mocha": "5.2.0",
|
"mocha": "6.0.1",
|
||||||
"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",
|
||||||
"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": "^5.4.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-native": "1.0.7",
|
||||||
"scanf": "^1.0.2",
|
"scanf": "^1.0.2",
|
||||||
"sequelize": "4.42.0",
|
"sequelize": "4.42.0",
|
||||||
"sequelize-typescript": "0.6.7",
|
"sequelize-typescript": "0.6.7",
|
||||||
|
@ -100,13 +100,13 @@
|
||||||
"simple-git": "^1.107.0",
|
"simple-git": "^1.107.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.24",
|
||||||
"tedious": "4.1.3",
|
"tedious": "5.0.3",
|
||||||
"ts-node": "8.0.2",
|
"ts-node": "8.0.2",
|
||||||
"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.26",
|
||||||
"typescript": "3.3.1",
|
"typescript": "3.3.3333",
|
||||||
"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 +121,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.37",
|
||||||
"@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",
|
||||||
|
|
|
@ -178,7 +178,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 => {
|
||||||
|
|
229
packages/azuredeployer.gbapp/dialogs/StartDialog.ts
Normal file
229
packages/azuredeployer.gbapp/dialogs/StartDialog.ts
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*****************************************************************************\
|
||||||
|
| ( )_ _ |
|
||||||
|
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
|
||||||
|
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\ |
|
||||||
|
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) ) |
|
||||||
|
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
|
||||||
|
| | | ( )_) | |
|
||||||
|
| (_) \___/' |
|
||||||
|
| |
|
||||||
|
| 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 { IGBInstance } from 'botlib';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
|
||||||
|
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
|
||||||
|
import { AzureDeployerService } from '../services/AzureDeployerService';
|
||||||
|
import { GuaribasInstance } from '../../../packages/core.gbapp/models/GBModel';
|
||||||
|
const scanf = require('scanf');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles command-line dialog for getting info for Boot Bot.
|
||||||
|
*/
|
||||||
|
export class StartDialog {
|
||||||
|
public static async createBaseInstance() {
|
||||||
|
// 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 AzureDeployerService.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
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:`);
|
||||||
|
|
||||||
|
// TODO: Update this regexp to match description of it.
|
||||||
|
|
||||||
|
botId = scanf('%s').replace(/(\n|\r)+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return botId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static retrieveAuthoringKey() {
|
||||||
|
let authoringKey = GBConfigService.get('NLP_AUTHORING_KEY');
|
||||||
|
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, 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');
|
||||||
|
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`
|
||||||
|
);
|
||||||
|
if (!appId) {
|
||||||
|
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) {
|
||||||
|
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 => {
|
||||||
|
console.log(`${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) {
|
||||||
|
process.stdout.write("CLOUD_LOCATION (eg. 'westus'):");
|
||||||
|
location = scanf('%s');
|
||||||
|
}
|
||||||
|
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,40 +38,38 @@
|
||||||
|
|
||||||
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
import { CognitiveServicesManagementClient } from 'azure-arm-cognitiveservices';
|
||||||
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
import { CognitiveServicesAccount } from 'azure-arm-cognitiveservices/lib/models';
|
||||||
import {
|
import { ResourceManagementClient, SubscriptionClient } from 'azure-arm-resource';
|
||||||
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';
|
||||||
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
import { AppServicePlan } from 'azure-arm-website/lib/models';
|
||||||
import { GBService, IGBInstance } from 'botlib';
|
import { GBService, IGBInstance } from 'botlib';
|
||||||
import { HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
import { HttpMethods, ServiceClient, WebResource } from 'ms-rest-js';
|
||||||
import { GBDeployer } from 'packages/core.gbapp/services/GBDeployer';
|
import { GBDeployer } from '../../../packages/core.gbapp/services/GBDeployer';
|
||||||
import * as simplegit from 'simple-git/promise';
|
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 { GuaribasInstance } from '../../../packages/core.gbapp/models/GBModel';
|
||||||
|
|
||||||
const Spinner = require('cli-spinner').Spinner;
|
const Spinner = require('cli-spinner').Spinner;
|
||||||
const scanf = require('scanf');
|
const scanf = require('scanf');
|
||||||
const git = simplegit();
|
const git = simplegit();
|
||||||
const logger = require('../../../src/logger');
|
const logger = require('../../../src/logger');
|
||||||
const UrlJoin = require('url-join');
|
const 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 {
|
export class AzureDeployerService extends GBService {
|
||||||
public static apiVersion = '2017-12-01';
|
public static apiVersion = '2017-12-01';
|
||||||
|
public static defaultEndPoint = 'http://localhost:4242';
|
||||||
public instance: IGBInstance;
|
public instance: IGBInstance;
|
||||||
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
public resourceClient: ResourceManagementClient.ResourceManagementClient;
|
||||||
public webSiteClient: WebSiteManagementClient;
|
public webSiteClient: WebSiteManagementClient;
|
||||||
public storageClient: SqlManagementClient;
|
public storageClient: SqlManagementClient;
|
||||||
public cognitiveClient: CognitiveServicesManagementClient;
|
public cognitiveClient: CognitiveServicesManagementClient;
|
||||||
public searchClient: SearchManagementClient;
|
public searchClient: SearchManagementClient;
|
||||||
public provider = 'Microsoft.BotService';
|
public static provider = 'Microsoft.BotService';
|
||||||
public subscriptionClient: SubscriptionClient.SubscriptionClient;
|
public subscriptionClient: SubscriptionClient.SubscriptionClient;
|
||||||
public accessToken: string;
|
public accessToken: string;
|
||||||
public location: string;
|
public location: string;
|
||||||
|
@ -189,228 +187,7 @@ export class AzureDeployerService extends GBService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployFarm(proxyAddress: string): Promise<IGBInstance> {
|
public static async updateBotProxy(botId, group, endpoint) {
|
||||||
const culture = 'en-us';
|
|
||||||
|
|
||||||
// Tries do get information from .env file otherwise asks in command-line.
|
|
||||||
|
|
||||||
let instance: IGBInstance = {};
|
|
||||||
instance = await this.ensureConfiguration(instance);
|
|
||||||
instance.marketplacePassword = GBAdminService.getRndPassword();
|
|
||||||
|
|
||||||
const spinner = new Spinner('%s');
|
|
||||||
spinner.start();
|
|
||||||
spinner.setSpinnerString('|/-\\');
|
|
||||||
|
|
||||||
let keys: any;
|
|
||||||
const name = instance.botId;
|
|
||||||
|
|
||||||
logger.info(`Deploying Deploy Group (It may take a few minutes)...`);
|
|
||||||
await this.createDeployGroup(name, instance.cloudLocation);
|
|
||||||
|
|
||||||
logger.info(`Deploying Bot Server...`);
|
|
||||||
const serverFarm = await this.createHostingPlan(
|
|
||||||
name,
|
|
||||||
`${name}-server-plan`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
await this.createServer(
|
|
||||||
serverFarm.id,
|
|
||||||
name,
|
|
||||||
`${name}-server`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info(`Deploying Bot Storage...`);
|
|
||||||
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
|
||||||
const administratorPassword = GBAdminService.getRndPassword();
|
|
||||||
const storageServer = `${name.toLowerCase()}-storage-server`;
|
|
||||||
const storageName = `${name}-storage`;
|
|
||||||
await this.createStorageServer(
|
|
||||||
name,
|
|
||||||
storageServer,
|
|
||||||
administratorLogin,
|
|
||||||
administratorPassword,
|
|
||||||
storageServer,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
await this.createStorage(
|
|
||||||
name,
|
|
||||||
storageServer,
|
|
||||||
storageName,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
instance.storageUsername = administratorLogin;
|
|
||||||
instance.storagePassword = administratorPassword;
|
|
||||||
instance.storageName = storageName;
|
|
||||||
instance.storageDialect = 'mssql';
|
|
||||||
instance.storageServer = storageServer;
|
|
||||||
|
|
||||||
logger.info(`Deploying Search...`);
|
|
||||||
const searchName = `${name}-search`.toLowerCase();
|
|
||||||
await this.createSearch(name, searchName, instance.cloudLocation);
|
|
||||||
const searchKeys = await this.searchClient.adminKeys.get(
|
|
||||||
name,
|
|
||||||
searchName
|
|
||||||
);
|
|
||||||
instance.searchHost = `${searchName}.search.windows.net`;
|
|
||||||
instance.searchIndex = 'azuresql-index';
|
|
||||||
instance.searchIndexer = 'azuresql-indexer';
|
|
||||||
instance.searchKey = searchKeys.primaryKey;
|
|
||||||
this.deployer.rebuildIndex(instance);
|
|
||||||
|
|
||||||
logger.info(`Deploying Speech...`);
|
|
||||||
const speech = await this.createSpeech(
|
|
||||||
name,
|
|
||||||
`${name}-speech`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
|
||||||
instance.speechKeyEndpoint = speech.endpoint;
|
|
||||||
instance.speechKey = keys.key1;
|
|
||||||
|
|
||||||
logger.info(`Deploying SpellChecker...`);
|
|
||||||
const spellChecker = await this.createSpellChecker(
|
|
||||||
name,
|
|
||||||
`${name}-spellchecker`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(
|
|
||||||
name,
|
|
||||||
spellChecker.name
|
|
||||||
);
|
|
||||||
instance.spellCheckerKey = keys.key1;
|
|
||||||
instance.spellCheckerEndpoint = spellChecker.endpoint;
|
|
||||||
|
|
||||||
logger.info(`Deploying Text Analytics...`);
|
|
||||||
const textAnalytics = await this.createTextAnalytics(
|
|
||||||
name,
|
|
||||||
`${name}-textanalytics`,
|
|
||||||
instance.cloudLocation
|
|
||||||
);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(
|
|
||||||
name,
|
|
||||||
textAnalytics.name
|
|
||||||
);
|
|
||||||
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
|
|
||||||
instance.textAnalyticsKey = keys.key1;
|
|
||||||
|
|
||||||
logger.info(`Deploying NLP...`);
|
|
||||||
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
|
|
||||||
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
|
||||||
const nlpAppId = await this.createLUISApp(
|
|
||||||
name,
|
|
||||||
name,
|
|
||||||
instance.cloudLocation,
|
|
||||||
culture,
|
|
||||||
instance.nlpAuthoringKey
|
|
||||||
);
|
|
||||||
|
|
||||||
instance.nlpEndpoint = nlp.endpoint;
|
|
||||||
instance.nlpKey = keys.key1;
|
|
||||||
instance.nlpAppId = nlpAppId;
|
|
||||||
|
|
||||||
logger.info(`Deploying Bot...`);
|
|
||||||
|
|
||||||
// TODO: Default endpoint, will be updated when it runs in production.
|
|
||||||
|
|
||||||
instance.botEndpoint = 'http://localhost:4242';
|
|
||||||
|
|
||||||
instance = await this.deployBootBot(
|
|
||||||
instance,
|
|
||||||
name,
|
|
||||||
`${proxyAddress}/api/messages/${name}`,
|
|
||||||
instance.nlpAppId,
|
|
||||||
instance.nlpKey,
|
|
||||||
instance.cloudSubscriptionId
|
|
||||||
);
|
|
||||||
|
|
||||||
spinner.stop();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 deployBootBot(
|
|
||||||
instance,
|
|
||||||
botId,
|
|
||||||
endpoint,
|
|
||||||
nlpAppId,
|
|
||||||
nlpKey,
|
|
||||||
subscriptionId
|
|
||||||
) {
|
|
||||||
let appId = GBConfigService.get('MARKETPLACE_ID');
|
|
||||||
let appPassword = GBConfigService.get('MARKETPLACE_SECRET');
|
|
||||||
|
|
||||||
if (!appId || !appPassword) {
|
|
||||||
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 baseUrl = `https://management.azure.com/`;
|
||||||
const username = GBConfigService.get('CLOUD_USERNAME');
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
const password = GBConfigService.get('CLOUD_PASSWORD');
|
const password = GBConfigService.get('CLOUD_PASSWORD');
|
||||||
|
@ -445,144 +222,193 @@ export class AzureDeployerService extends GBService {
|
||||||
logger.info(`Bot proxy updated at: ${endpoint}.`);
|
logger.info(`Bot proxy updated at: ${endpoint}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deployGeneralBotsToAzure() {
|
public static async openStorageFirewall(groupName, serverName) {
|
||||||
const status = await git.status();
|
const username = GBConfigService.get('CLOUD_USERNAME');
|
||||||
// TODO: Copy github to webapp.
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ensureConfiguration(instance: IGBInstance) {
|
public static createRequestObject(url: string, accessToken: string, verb: HttpMethods, body: string) {
|
||||||
let username = GBConfigService.get('CLOUD_USERNAME');
|
const req = new WebResource();
|
||||||
let password = GBConfigService.get('CLOUD_PASSWORD');
|
req.method = verb;
|
||||||
let subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
|
req.url = url;
|
||||||
let location = GBConfigService.get('CLOUD_LOCATION');
|
req.headers = {};
|
||||||
let botId = GBConfigService.get('BOT_ID');
|
req.headers['Content-Type'] = 'application/json';
|
||||||
|
req.headers['accept-language'] = '*';
|
||||||
|
req.headers.Authorization = 'Bearer ' + accessToken;
|
||||||
|
req.body = body;
|
||||||
|
|
||||||
// No .env so asks for cloud credentials to start a new farm.
|
return req;
|
||||||
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:`); // TODO: INCLUDE URL
|
|
||||||
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.
|
public async deployFarm(
|
||||||
|
proxyAddress: string,
|
||||||
|
instance: IGBInstance,
|
||||||
|
credentials,
|
||||||
|
subscriptionId: string
|
||||||
|
): Promise<IGBInstance> {
|
||||||
|
const culture = 'en-us';
|
||||||
|
|
||||||
const credentials = await GBAdminService.getADALCredentialsFromUsername(
|
this.initServices(credentials, subscriptionId);
|
||||||
username,
|
|
||||||
password
|
const spinner = new Spinner('%s');
|
||||||
|
spinner.start();
|
||||||
|
spinner.setSpinnerString('|/-\\');
|
||||||
|
|
||||||
|
let keys: any;
|
||||||
|
const name = instance.botId;
|
||||||
|
|
||||||
|
logger.info(`Deploying Deploy Group (It may take a few minutes)...`);
|
||||||
|
await this.createDeployGroup(name, instance.cloudLocation);
|
||||||
|
|
||||||
|
logger.info(`Deploying Bot Server...`);
|
||||||
|
const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, instance.cloudLocation);
|
||||||
|
await this.createServer(serverFarm.id, name, `${name}-server`, instance.cloudLocation);
|
||||||
|
|
||||||
|
logger.info(`Deploying Bot Storage...`);
|
||||||
|
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
|
||||||
|
const administratorPassword = GBAdminService.getRndPassword();
|
||||||
|
const storageServer = `${name.toLowerCase()}-storage-server`;
|
||||||
|
const storageName = `${name}-storage`;
|
||||||
|
await this.createStorageServer(
|
||||||
|
name,
|
||||||
|
storageServer,
|
||||||
|
administratorLogin,
|
||||||
|
administratorPassword,
|
||||||
|
storageServer,
|
||||||
|
instance.cloudLocation
|
||||||
);
|
);
|
||||||
if (!subscriptionId) {
|
await this.createStorage(name, storageServer, storageName, instance.cloudLocation);
|
||||||
const map = {};
|
instance.storageUsername = administratorLogin;
|
||||||
let index = 1;
|
instance.storagePassword = administratorPassword;
|
||||||
const list = await AzureDeployerService.getSubscriptions(credentials);
|
instance.storageName = storageName;
|
||||||
list.forEach(element => {
|
instance.storageDialect = 'mssql';
|
||||||
console.log(
|
instance.storageServer = storageServer;
|
||||||
`${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.
|
logger.info(`Deploying Search...`);
|
||||||
|
const searchName = `${name}-search`.toLowerCase();
|
||||||
|
await this.createSearch(name, searchName, instance.cloudLocation);
|
||||||
|
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
|
||||||
|
instance.searchHost = `${searchName}.search.windows.net`;
|
||||||
|
instance.searchIndex = 'azuresql-index';
|
||||||
|
instance.searchIndexer = 'azuresql-indexer';
|
||||||
|
instance.searchKey = searchKeys.primaryKey;
|
||||||
|
this.deployer.rebuildIndex(instance);
|
||||||
|
|
||||||
instance.botId = botId;
|
logger.info(`Deploying Speech...`);
|
||||||
instance.cloudUsername = username;
|
const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation);
|
||||||
instance.cloudPassword = password;
|
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
|
||||||
instance.cloudSubscriptionId = subscriptionId;
|
instance.speechKeyEndpoint = speech.endpoint;
|
||||||
instance.cloudLocation = location;
|
instance.speechKey = keys.key1;
|
||||||
instance.nlpAuthoringKey = authoringKey;
|
|
||||||
instance.adminPass = GBAdminService.getRndPassword();
|
|
||||||
|
|
||||||
this.resourceClient = new ResourceManagementClient.default(
|
logger.info(`Deploying SpellChecker...`);
|
||||||
credentials,
|
const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`, instance.cloudLocation);
|
||||||
subscriptionId
|
keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
|
||||||
);
|
instance.spellCheckerKey = keys.key1;
|
||||||
this.webSiteClient = new WebSiteManagementClient(
|
instance.spellCheckerEndpoint = spellChecker.endpoint;
|
||||||
credentials,
|
|
||||||
subscriptionId
|
logger.info(`Deploying Text Analytics...`);
|
||||||
);
|
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
|
||||||
this.storageClient = new SqlManagementClient(credentials, subscriptionId);
|
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
|
||||||
this.cognitiveClient = new CognitiveServicesManagementClient(
|
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
|
||||||
credentials,
|
instance.textAnalyticsKey = keys.key1;
|
||||||
subscriptionId
|
|
||||||
|
logger.info(`Deploying NLP...`);
|
||||||
|
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
|
||||||
|
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
|
||||||
|
const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
|
||||||
|
|
||||||
|
instance.nlpEndpoint = nlp.endpoint;
|
||||||
|
instance.nlpKey = keys.key1;
|
||||||
|
instance.nlpAppId = nlpAppId;
|
||||||
|
|
||||||
|
logger.info(`Deploying Bot...`);
|
||||||
|
instance.botEndpoint = AzureDeployerService.defaultEndPoint;
|
||||||
|
|
||||||
|
instance = await this.internalDeployBot(
|
||||||
|
instance,
|
||||||
|
this.accessToken,
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
'General BootBot',
|
||||||
|
`${proxyAddress}/api/messages/${name}`,
|
||||||
|
'global',
|
||||||
|
instance.nlpAppId,
|
||||||
|
instance.nlpKey,
|
||||||
|
instance.appId,
|
||||||
|
instance.appPassword,
|
||||||
|
instance.cloudSubscriptionId
|
||||||
);
|
);
|
||||||
|
|
||||||
this.searchClient = new SearchManagementClient(credentials, subscriptionId);
|
spinner.stop();
|
||||||
this.accessToken = credentials.tokenCache._entries[0].accessToken;
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createStorageServer(
|
public async deployToCloud(
|
||||||
group,
|
title,
|
||||||
name,
|
username,
|
||||||
administratorLogin,
|
password,
|
||||||
administratorPassword,
|
cloudLocation,
|
||||||
serverName,
|
authoringKey,
|
||||||
location
|
appId,
|
||||||
|
appPassword,
|
||||||
|
subscriptionId
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
const instance: IGBInstance = {};
|
||||||
|
|
||||||
|
instance.botId = title;
|
||||||
|
instance.cloudUsername = username;
|
||||||
|
instance.cloudPassword = password;
|
||||||
|
instance.cloudSubscriptionId = subscriptionId;
|
||||||
|
instance.cloudLocation = cloudLocation;
|
||||||
|
instance.nlpAuthoringKey = authoringKey;
|
||||||
|
instance.marketplaceId = appId;
|
||||||
|
instance.marketplacePassword = appPassword;
|
||||||
|
instance.adminPass = GBAdminService.getRndPassword();
|
||||||
|
|
||||||
|
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
|
||||||
|
this.deployFarm(`http://${instance.botId}.azurewebsites.net`, instance, credentials, subscriptionId);
|
||||||
|
|
||||||
|
// TODO: Copy github to webapp.
|
||||||
|
//const status = await git.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initServices(credentials: any, subscriptionId: string) {
|
||||||
|
this.resourceClient = new ResourceManagementClient.default(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createStorageServer(group, name, administratorLogin, administratorPassword, serverName, location) {
|
||||||
const params = {
|
const params = {
|
||||||
location: location,
|
location: location,
|
||||||
administratorLogin: administratorLogin,
|
administratorLogin: administratorLogin,
|
||||||
|
@ -595,7 +421,7 @@ 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
|
AzureDeployerService.provider
|
||||||
}/register?api-version=2018-02-01`;
|
}/register?api-version=2018-02-01`;
|
||||||
const requestUrl = UrlJoin(baseUrl, query);
|
const requestUrl = UrlJoin(baseUrl, query);
|
||||||
|
|
||||||
|
@ -611,6 +437,7 @@ export class AzureDeployerService extends GBService {
|
||||||
const res = await httpClient.sendRequest(req);
|
const res = await httpClient.sendRequest(req);
|
||||||
// TODO: Check res for error.
|
// 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
|
||||||
*/
|
*/
|
||||||
|
@ -660,15 +487,10 @@ 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
|
AzureDeployerService.provider
|
||||||
}/botServices/${botId}?api-version=${AzureDeployerService.apiVersion}`;
|
}/botServices/${botId}?api-version=${AzureDeployerService.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 (!(res.bodyAsJson as any).id) {
|
||||||
reject(res.bodyAsText);
|
reject(res.bodyAsText);
|
||||||
|
@ -681,42 +503,19 @@ export class AzureDeployerService extends GBService {
|
||||||
AzureDeployerService.apiVersion
|
AzureDeployerService.apiVersion
|
||||||
}`;
|
}`;
|
||||||
url = UrlJoin(baseUrl, query);
|
url = UrlJoin(baseUrl, query);
|
||||||
req = this.createRequestObject(
|
req = AzureDeployerService.createRequestObject(url, accessToken, 'GET', 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 = (resChannel.bodyAsJson as any).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 +529,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, null, 'GET', 'apps');
|
||||||
location,
|
|
||||||
authoringKey,
|
|
||||||
null,
|
|
||||||
'GET',
|
|
||||||
'apps'
|
|
||||||
);
|
|
||||||
const app = (apps.bodyAsJson as any).filter(x => x.name == name)[0];
|
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;
|
||||||
|
@ -771,8 +558,8 @@ export class AzureDeployerService extends GBService {
|
||||||
req.headers['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 +578,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,51 +593,20 @@ 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, location): 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) {
|
||||||
|
@ -868,11 +614,7 @@ export class AzureDeployerService extends GBService {
|
||||||
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 +625,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) {
|
||||||
|
@ -898,20 +636,4 @@ export class AzureDeployerService extends GBService {
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
||||||
const Parse = require('csv-parse');
|
const Parse = require('csv-parse');
|
||||||
const Async = require('async');
|
const Async = require('async');
|
||||||
const UrlJoin = require('url-join');
|
const UrlJoin = require('url-join');
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
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');
|
||||||
|
|
|
@ -35,25 +35,76 @@
|
||||||
import { TurnContext } from 'botbuilder';
|
import { TurnContext } from 'botbuilder';
|
||||||
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance } from 'botlib';
|
import { GBMinInstance } from 'botlib';
|
||||||
const WaitUntil = require('wait-until');
|
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||||
|
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview General Bots server core.
|
* BASIC system class for extra manipulation of bot behaviour.
|
||||||
*/
|
*/
|
||||||
|
class SysClass {
|
||||||
export class DialogClass {
|
|
||||||
public min: GBMinInstance;
|
public min: GBMinInstance;
|
||||||
public context: TurnContext;
|
|
||||||
public step: WaterfallStepContext;
|
|
||||||
|
|
||||||
constructor(min: GBMinInstance) {
|
constructor(min: GBMinInstance) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async wait(seconds: number) {
|
||||||
|
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
await timeout(seconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public generatePassword() {
|
||||||
|
return GBAdminService.getRndPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createABotFarmUsing(
|
||||||
|
botId,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
location,
|
||||||
|
nlpAuthoringKey,
|
||||||
|
appId,
|
||||||
|
appPassword,
|
||||||
|
subscriptionId
|
||||||
|
) {
|
||||||
|
const service = new AzureDeployerService(this.min.deployer);
|
||||||
|
await service.deployToCloud(
|
||||||
|
botId,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
location,
|
||||||
|
nlpAuthoringKey,
|
||||||
|
appId,
|
||||||
|
appPassword,
|
||||||
|
subscriptionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @fileoverview General Bots server core.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class DialogClass {
|
||||||
|
|
||||||
|
public min: GBMinInstance;
|
||||||
|
public context: TurnContext;
|
||||||
|
public step: WaterfallStepContext;
|
||||||
|
public internalSys: SysClass;
|
||||||
|
|
||||||
|
constructor(min: GBMinInstance) {
|
||||||
|
this.min = min;
|
||||||
|
this.internalSys = new SysClass(min);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sys(): SysClass {
|
||||||
|
return this.internalSys;
|
||||||
|
}
|
||||||
|
|
||||||
public async hear(cb) {
|
public async hear(cb) {
|
||||||
let idCallback = Math.floor(Math.random() * 1000000000000);
|
const idCallback = Math.floor(Math.random() * 1000000000000);
|
||||||
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) {
|
||||||
|
|
|
@ -74,6 +74,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;
|
||||||
|
|
|
@ -50,7 +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';
|
import { StartDialog } from '../../azuredeployer.gbapp/dialogs/StartDialog';
|
||||||
|
|
||||||
const logger = require('../../../src/logger');
|
const logger = require('../../../src/logger');
|
||||||
const opn = require('opn');
|
const opn = require('opn');
|
||||||
|
@ -274,7 +274,7 @@ STORAGE_SYNC=true
|
||||||
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)...`);
|
logger.info(`Updating bot endpoint to local reverse proxy (ngrok)...`);
|
||||||
await azureDeployer.updateBotProxy(
|
await AzureDeployerService.updateBotProxy(
|
||||||
instance.botId,
|
instance.botId,
|
||||||
instance.botId,
|
instance.botId,
|
||||||
`${proxyAddress}/api/messages/${instance.botId}`
|
`${proxyAddress}/api/messages/${instance.botId}`
|
||||||
|
@ -309,8 +309,8 @@ STORAGE_SYNC=true
|
||||||
*/
|
*/
|
||||||
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
|
public async ensureInstances(instances: GuaribasInstance[], bootInstance: any, core: GBCoreService) {
|
||||||
if (!instances) {
|
if (!instances) {
|
||||||
const saveInstance = new GuaribasInstance(bootInstance);
|
const instance: IGBInstance = {};
|
||||||
await saveInstance.save();
|
await instance.save();
|
||||||
instances = await core.loadInstances();
|
instances = await core.loadInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,10 +347,15 @@ STORAGE_SYNC=true
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
public async createBootInstance(core: GBCoreService, azureDeployer: AzureDeployerService, proxyAddress: string) {
|
||||||
let instance: IGBInstance;
|
|
||||||
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
logger.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
|
||||||
try {
|
try {
|
||||||
instance = await azureDeployer.deployFarm(proxyAddress);
|
let { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance();
|
||||||
|
instance = await azureDeployer.deployFarm(proxyAddress, instance, credentials, subscriptionId);
|
||||||
|
core.writeEnv(instance);
|
||||||
|
logger.info(`File .env written, starting General Bots...`);
|
||||||
|
GBConfigService.init();
|
||||||
|
|
||||||
|
return instance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`In case of error, please cleanup any infrastructure objects
|
`In case of error, please cleanup any infrastructure objects
|
||||||
|
@ -358,11 +363,6 @@ STORAGE_SYNC=true
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
core.writeEnv(instance);
|
|
||||||
logger.info(`File .env written, starting General Bots...`);
|
|
||||||
GBConfigService.init();
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openBrowserInDevelopment() {
|
public openBrowserInDevelopment() {
|
||||||
|
@ -407,7 +407,7 @@ STORAGE_SYNC=true
|
||||||
const fkcols = args[0];
|
const fkcols = args[0];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re4.exec(fkcols);
|
let matches = re4.exec(fkcols);
|
||||||
while (matches != null) {
|
while (matches != undefined) {
|
||||||
fkname += '_' + matches[1];
|
fkname += '_' + matches[1];
|
||||||
matches = re4.exec(fkcols);
|
matches = re4.exec(fkcols);
|
||||||
}
|
}
|
||||||
|
@ -440,7 +440,7 @@ STORAGE_SYNC=true
|
||||||
const fkcols = args[2];
|
const fkcols = args[2];
|
||||||
let fkname = table;
|
let fkname = table;
|
||||||
let matches = re3.exec(fkcols);
|
let matches = re3.exec(fkcols);
|
||||||
while (matches != null) {
|
while (matches != undefined) {
|
||||||
fkname += '_' + matches[1];
|
fkname += '_' + matches[1];
|
||||||
matches = re3.exec(fkcols);
|
matches = re3.exec(fkcols);
|
||||||
}
|
}
|
||||||
|
@ -459,6 +459,6 @@ STORAGE_SYNC=true
|
||||||
private async openStorageFrontier(deployer: AzureDeployerService) {
|
private async openStorageFrontier(deployer: AzureDeployerService) {
|
||||||
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 AzureDeployerService.openStorageFirewall(group, serverName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ export class GBDeployer {
|
||||||
|
|
||||||
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(`GuaribasBusinessError: Unknown package type: ${packageType}.`);
|
||||||
|
|
|
@ -45,6 +45,7 @@ const AuthenticationContext = require('adal-node').AuthenticationContext;
|
||||||
|
|
||||||
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
|
||||||
|
|
||||||
|
import { ConfirmPrompt } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib';
|
import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib';
|
||||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||||
import { GBCorePackage } from '../../core.gbapp';
|
import { GBCorePackage } from '../../core.gbapp';
|
||||||
|
@ -56,7 +57,6 @@ 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. */
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ export class GBMinService {
|
||||||
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 +119,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);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -171,7 +139,7 @@ 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}.`);
|
logger.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
|
||||||
|
|
||||||
|
@ -186,71 +154,108 @@ export class GBMinService {
|
||||||
// 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: GuaribasInstance) {
|
||||||
|
server.get(`/${min.instance.botId}/token`, async (req, res) => {
|
||||||
|
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 handleOAuthRequests(server: any, min: GBMinInstance) {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instance object to clients requesting bot info.
|
||||||
|
*/
|
||||||
|
private async sendInstanceToClient(req, bootInstance: GuaribasInstance, res: any, webchatToken: any) {
|
||||||
|
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,
|
||||||
|
speechToken: speechToken,
|
||||||
|
conversationId: webchatToken.conversationId,
|
||||||
|
authenticatorTenant: instance.authenticatorTenant,
|
||||||
|
authenticatorClientId: instance.authenticatorClientId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const error = `Instance not found: ${botId}.`;
|
||||||
|
res.sendStatus(error);
|
||||||
|
logger.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',
|
||||||
|
@ -276,7 +281,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
|
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -317,6 +322,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,7 +333,7 @@ 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 = new Array<IGBPackage>();
|
||||||
// NOTE: A semicolon is necessary before this line.
|
// NOTE: A semicolon is necessary before this line.
|
||||||
[
|
[
|
||||||
|
@ -364,11 +370,11 @@ 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 state = await conversationState.get(context);
|
||||||
const step = await min.dialogs.createContext(context, state);
|
const step = await min.dialogs.createContext(context, state);
|
||||||
|
@ -412,32 +418,7 @@ export class GBMinService {
|
||||||
// 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') {
|
||||||
|
@ -445,31 +426,7 @@ export class GBMinService {
|
||||||
|
|
||||||
// TODO: Understand MSFT changes: await step.endAll();
|
// TODO: Understand MSFT changes: await step.endAll();
|
||||||
|
|
||||||
if (context.activity.name === 'whoAmI') {
|
await this.processEventActivity(context, step);
|
||||||
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) {
|
||||||
|
@ -481,4 +438,66 @@ export class GBMinService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async processEventActivity(context, step: any) {
|
||||||
|
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.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: any) {
|
||||||
|
// Direct script invoking by itent name.
|
||||||
|
|
||||||
|
let isVMCall = Object.keys(min.scriptMap).find(key => min.scriptMap[key] === context.activity.text) !== undefined;
|
||||||
|
|
||||||
|
if (isVMCall) {
|
||||||
|
|
||||||
|
let mainMethod = context.activity.text;
|
||||||
|
|
||||||
|
min.sandbox.context = context;
|
||||||
|
min.sandbox.step = step;
|
||||||
|
min.sandbox[mainMethod].bind(min.sandbox);
|
||||||
|
await min.sandbox[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', {
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,19 +35,22 @@
|
||||||
import { WaterfallDialog } from 'botbuilder-dialogs';
|
import { WaterfallDialog } from 'botbuilder-dialogs';
|
||||||
import { GBMinInstance, IGBCoreService } from 'botlib';
|
import { GBMinInstance, 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');
|
import DialogClass from './GBAPIService';
|
||||||
|
|
||||||
|
const walkPromise = require('walk-promise');
|
||||||
const logger = require('../../../src/logger');
|
const logger = require('../../../src/logger');
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const UrlJoin = require('url-join');
|
const UrlJoin = require('url-join');
|
||||||
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
||||||
|
var 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.
|
||||||
*/
|
*/
|
||||||
|
@ -55,41 +58,97 @@ const vb2ts = require('vbscript-to-typescript/dist/converter');
|
||||||
export class GBVMService implements IGBCoreService {
|
export class GBVMService implements IGBCoreService {
|
||||||
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,
|
|
||||||
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
|
const files = await walkPromise(folder);
|
||||||
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(/(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 = await 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.
|
||||||
|
@ -104,7 +163,7 @@ export class GBVMService implements IGBCoreService {
|
||||||
|
|
||||||
// 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`;
|
||||||
|
@ -146,22 +205,10 @@ export class GBVMService implements IGBCoreService {
|
||||||
code = parsedCode;
|
code = parsedCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) => {
|
|
||||||
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 sandbox: DialogClass = new DialogClass(min);
|
||||||
const context = vm.createContext(sandbox);
|
const context = vm.createContext(sandbox);
|
||||||
|
@ -172,6 +219,28 @@ export class GBVMService implements IGBCoreService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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', [
|
||||||
|
|
|
@ -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
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
||||||
const Parse = require('csv-parse');
|
const Parse = require('csv-parse');
|
||||||
const Async = require('async');
|
const Async = require('async');
|
||||||
const UrlJoin = require('url-join');
|
const UrlJoin = require('url-join');
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
const logger = require('../../../src/logger');
|
||||||
|
|
||||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
||||||
|
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
||||||
const Parse = require('csv-parse');
|
const Parse = require('csv-parse');
|
||||||
const Async = require('async');
|
const Async = require('async');
|
||||||
const UrlJoin = require('url-join');
|
const UrlJoin = require('url-join');
|
||||||
const Walk = require('fs-walk');
|
|
||||||
const logger = require('../../../src/logger');
|
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');
|
||||||
|
|
|
@ -51,6 +51,7 @@ 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';
|
import { GBVMService } from '../packages/core.gbapp/services/GBVMService';
|
||||||
|
import { load } from 'dotenv';
|
||||||
|
|
||||||
const appPackages = new Array<IGBPackage>();
|
const appPackages = new Array<IGBPackage>();
|
||||||
|
|
||||||
|
@ -90,7 +91,6 @@ export class GBServer {
|
||||||
|
|
||||||
GBConfigService.init();
|
GBConfigService.init();
|
||||||
const core = new GBCoreService();
|
const core = 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);
|
||||||
|
@ -113,14 +113,15 @@ export class GBServer {
|
||||||
await core.initStorage();
|
await core.initStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.ensureAdminIsSecured();
|
||||||
|
|
||||||
// Deploys system and user packages.
|
// Deploys system and user packages.
|
||||||
|
|
||||||
logger.info(`Deploying packages...`);
|
logger.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...`);
|
logger.info(`Publishing instances...`);
|
||||||
|
@ -152,8 +153,6 @@ export class GBServer {
|
||||||
|
|
||||||
core.openBrowserInDevelopment();
|
core.openBrowserInDevelopment();
|
||||||
|
|
||||||
return core;
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
logger.error(`STOP: ${err} ${err.stack ? err.stack : ''}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
],
|
],
|
||||||
"jsRules": {},
|
"jsRules": {},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"no-floating-promises": false,
|
||||||
"no-var-requires":false,
|
"no-var-requires":false,
|
||||||
"typedef":false,
|
"typedef":false,
|
||||||
"variable-name": false,
|
"variable-name": false,
|
||||||
|
|
Loading…
Add table
Reference in a new issue