diff --git a/README.md b/README.md
index a9ecf6c5..52dfdfe0 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,10 @@
| Releases | [](https://www.npmjs.com/package/botserver/) [](https://www.npmjs.com/package/botlib/) [](https://github.com/semantic-release/semantic-release)|
| [Docker Image](https://github.com/lpicanco/docker-botserver) |     
*Provided by [@lpicanco](https://github.com/lpicanco/docker-botserver)* |
+
#### Watch a video about easeness authoring of bot packages, development environment and self-deployment
-* Now with General Bots you can press F5 on Visual Studio to get a bot factory on your environment* published on November 10th, 2018.
+* Now with the General Bots server you can press F5 on Visual Studio to get a bot factory on your environment* published on November 10th, 2018.
[](https://www.youtube.com/watch?v=AfKTwljoMOs)
@@ -20,7 +21,7 @@ Welcome to General Bot Community Edition

-General Bot is a package based chat bot server focused in convention over configuration and code-less approaches, which brings software packages and application server concepts to help parallel bot development.
+General Bot is a strongly typed package based chat bot server focused in convention over configuration and code-less approaches, which brings software packages and application server concepts to help parallel bot development.
## Sample Package #1: [default.gbdialog (VBA)](https://github.com/pragmatismo-io/BotServer/tree/master/packages/default.gbdialog)
diff --git a/packages/admin.gbapp/dialogs/AdminDialog.ts b/packages/admin.gbapp/dialogs/AdminDialog.ts
index 287b2502..4d3b4a03 100644
--- a/packages/admin.gbapp/dialogs/AdminDialog.ts
+++ b/packages/admin.gbapp/dialogs/AdminDialog.ts
@@ -38,7 +38,7 @@
const UrlJoin = require('url-join');
import { BotAdapter } from 'botbuilder';
-import { WaterfallDialog } from 'botbuilder-dialogs';
+import { WaterfallDialog, WaterfallStep, WaterfallStepContext } from 'botbuilder-dialogs';
import { GBMinInstance } from 'botlib';
import { IGBDialog } from 'botlib';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService';
@@ -60,28 +60,35 @@ export class AdminDialog extends IGBDialog {
await deployer.undeployPackageFromLocalPath(min.instance, UrlJoin('packages', packageName));
}
+ public static isSharePointPath(path: string) {
+ return path.indexOf('sharepoint.com') > 0;
+ }
+
public static async deployPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
const packageName = text.split(' ')[1];
- const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
- if (!additionalPath)
- {
- throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
+
+ if (AdminDialog.isSharePointPath(packageName)) {
+ await deployer.deployFromSharePoint(min.instance.instanceId, packageName);
+ } else {
+ const additionalPath = GBConfigService.get('ADDITIONAL_DEPLOY_PATH');
+ if (!additionalPath) {
+ throw new Error('ADDITIONAL_DEPLOY_PATH is not set and deployPackage was called.');
+ }
+ await deployer.deployPackageFromLocalPath(min, UrlJoin(additionalPath, packageName));
}
- await deployer.deployPackageFromLocalPath(min, UrlJoin(additionalPath, packageName));
}
public static async rebuildIndexPackageCommand(min: GBMinInstance, text: string, deployer: GBDeployer) {
await deployer.rebuildIndex(min.instance);
}
- public static async addConnectionCommand(min: GBMinInstance, text: any) {
+ public static async addConnectionCommand(min: GBMinInstance, text: any) {
const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core);
const admin = new GBAdminService(min.core);
// TODO: await admin.addConnection
}
-
/**
* Setup dialogs flows and define services call.
*
@@ -94,6 +101,8 @@ export class AdminDialog extends IGBDialog {
const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer);
+ AdminDialog.setupSecurityDialogs(min);
+
min.dialogs.add(
new WaterfallDialog('/admin', [
async step => {
@@ -151,7 +160,7 @@ export class AdminDialog extends IGBDialog {
return await step.replaceDialog('/admin', { firstRun: false });
} else if (cmdName === 'setupSecurity') {
- await AdminDialog.setupSecurity(min, step);
+ return await step.beginDialog('/setupSecurity');
} else {
unknownCommand = true;
}
@@ -159,7 +168,7 @@ export class AdminDialog extends IGBDialog {
if (unknownCommand) {
await step.context.sendActivity(Messages[locale].unknown_command);
} else {
- await step.context.sendActivity(Messages[locale].finshed_working(cmdName));
+ await step.context.sendActivity(Messages[locale].finished_working);
}
await step.endDialog();
@@ -169,16 +178,66 @@ export class AdminDialog extends IGBDialog {
);
}
- private static async setupSecurity(min: any, step: any) {
- const locale = step.activity.locale;
- const state = `${min.instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
- await min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
- const url = `https://login.microsoftonline.com/${min.instance.authenticatorTenant}/oauth2/authorize?client_id=${
- min.instance.authenticatorClientId
- }&response_type=code&redirect_uri=${min.instance.botEndpoint}/${
- min.instance.botId
- }/token&state=${state}&response_mode=query`;
+ private static setupSecurityDialogs(min: any) {
+ min.dialogs.add(
+ new WaterfallDialog('/setupSecurity', [
+ async step => {
+ const locale = step.context.activity.locale;
+ const prompt = Messages[locale].enter_authenticator_tenant;
- await step.sendActivity(Messages[locale].consent(url));
+ return await step.prompt('textPrompt', prompt);
+ },
+ async step => {
+ step.activeDialog.state.authenticatorTenant = step.result;
+ const locale = step.context.activity.locale;
+ const prompt = Messages[locale].enter_authenticator_authority_host_url;
+
+ return await step.prompt('textPrompt', prompt);
+ },
+ async step => {
+ step.activeDialog.state.authenticatorAuthorityHostUrl = step.result;
+ const locale = step.context.activity.locale;
+ const prompt = Messages[locale].enter_authenticator_client_id;
+
+ return await step.prompt('textPrompt', prompt);
+ },
+ async step => {
+ step.activeDialog.state.authenticatorClientId = step.result;
+ const locale = step.context.activity.locale;
+ const prompt = Messages[locale].enter_authenticator_client_secret;
+
+ return await step.prompt('textPrompt', prompt);
+ },
+ async step => {
+ step.activeDialog.state.authenticatorClientSecret = step.result;
+
+ await min.adminService.updateSecurityInfo(
+ min.instance.instanceId,
+ step.activeDialog.state.authenticatorTenant,
+ step.activeDialog.state.authenticatorAuthorityHostUrl,
+ step.activeDialog.state.authenticatorClientId,
+ step.activeDialog.state.authenticatorClientSecret
+ );
+
+ const locale = step.context.activity.locale;
+ const state = `${min.instance.instanceId}${Math.floor(Math.random() * 1000000000)}`;
+
+ await min.adminService.setValue(min.instance.instanceId, 'AntiCSRFAttackState', state);
+
+ const url = `https://login.microsoftonline.com/${min.instance.authenticatorTenant}/oauth2/authorize?client_id=${
+ min.instance.authenticatorClientId
+ }&response_type=code&redirect_uri=${UrlJoin(
+ min.instance.botEndpoint,
+ min.instance.botId,
+ '/token'
+ )}&state=${state}&response_mode=query`;
+
+ await step.context.sendActivity(Messages[locale].consent(url));
+
+ return await step.replaceDialog('/ask', {isReturning: true});
+ }
+ ])
+ );
}
+
}
diff --git a/packages/admin.gbapp/models/AdminModel.ts b/packages/admin.gbapp/models/AdminModel.ts
index e7d26acb..a747f704 100644
--- a/packages/admin.gbapp/models/AdminModel.ts
+++ b/packages/admin.gbapp/models/AdminModel.ts
@@ -41,7 +41,8 @@ import {
CreatedAt,
Model,
Table,
- UpdatedAt
+ UpdatedAt,
+ DataType
} from 'sequelize-typescript';
@Table
@@ -53,7 +54,7 @@ export class GuaribasAdmin extends Model {
@Column
public key: string;
- @Column
+ @Column(DataType.STRING(1024))
public value: string;
@Column
diff --git a/packages/admin.gbapp/services/GBAdminService.ts b/packages/admin.gbapp/services/GBAdminService.ts
index aee36868..80c2d1f9 100644
--- a/packages/admin.gbapp/services/GBAdminService.ts
+++ b/packages/admin.gbapp/services/GBAdminService.ts
@@ -38,19 +38,20 @@
import { AuthenticationContext, TokenResponse } from 'adal-node';
import { IGBCoreService } from 'botlib';
+import { GuaribasInstance } from '../../core.gbapp/models/GBModel';
import { GuaribasAdmin } from '../models/AdminModel';
const UrlJoin = require('url-join');
const msRestAzure = require('ms-rest-azure');
const PasswordGenerator = require('strict-password-generator').default;
+/**
+ * Services for server administration.
+ */
export class GBAdminService {
-
public static GB_PROMPT: string = 'GeneralBots: ';
public static masterBotInstanceId = 0;
- public static StrongRegex = new RegExp(
- '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+_-])(?=.{8,})'
- );
+ public static StrongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+_-])(?=.{8,})');
public core: IGBCoreService;
@@ -62,26 +63,16 @@ export class GBAdminService {
return msRestAzure.generateUuid();
}
- public static async getADALTokenFromUsername(
- username: string,
- password: string
- ) {
- const credentials = await GBAdminService.getADALCredentialsFromUsername(
- username,
- password
- );
+ public static async getADALTokenFromUsername(username: string, password: string) {
+ const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
const accessToken = credentials.tokenCache._entries[0].accessToken;
+
return accessToken;
}
- public static async getADALCredentialsFromUsername(
- username: string,
- password: string
- ) {
- const credentials = await msRestAzure.loginWithUsernamePassword(
- username,
- password
- );
+ public static async getADALCredentialsFromUsername(username: string, password: string) {
+ const credentials = await msRestAzure.loginWithUsernamePassword(username, password);
+
return credentials;
}
@@ -114,31 +105,47 @@ export class GBAdminService {
return name;
}
- public async setValue(
- instanceId: number,
- key: string,
- value: string
- ): Promise {
+ public async setValue(instanceId: number, key: string, value: string): Promise {
const options = { where: {} };
options.where = { key: key };
let admin = await GuaribasAdmin.findOne(options);
- if (admin == null) {
+ if (admin === null) {
admin = new GuaribasAdmin();
admin.key = key;
}
admin.value = value;
admin.instanceId = instanceId;
+
return admin.save();
}
+ public async updateSecurityInfo(
+ instanceId: number,
+ authenticatorTenant: string,
+ authenticatorAuthorityHostUrl: string,
+ authenticatorClientId: string,
+ authenticatorClientSecret: string
+ ): Promise {
+ const options = { where: {} };
+ options.where = { instanceId: instanceId };
+ const item = await GuaribasInstance.findOne(options);
+ item.authenticatorTenant = authenticatorTenant;
+ item.authenticatorAuthorityHostUrl = authenticatorAuthorityHostUrl;
+ item.authenticatorClientId = authenticatorClientId;
+ item.authenticatorClientSecret = authenticatorClientSecret;
+
+ return item.save();
+ }
+
public async getValue(instanceId: number, key: string) {
const options = { where: {} };
options.where = { key: key, instanceId: instanceId };
const obj = await GuaribasAdmin.findOne(options);
+
return Promise.resolve(obj.value);
}
- public async acquireElevatedToken(instanceId): Promise {
+ public async acquireElevatedToken(instanceId: number): Promise {
return new Promise(async (resolve, reject) => {
const instance = await this.core.loadInstanceById(instanceId);
@@ -166,21 +173,9 @@ export class GBAdminService {
reject(err);
} else {
const token = res as TokenResponse;
- await this.setValue(
- instanceId,
- 'accessToken',
- token.accessToken
- );
- await this.setValue(
- instanceId,
- 'refreshToken',
- token.refreshToken
- );
- await this.setValue(
- instanceId,
- 'expiresOn',
- token.expiresOn.toString()
- );
+ await this.setValue(instanceId, 'accessToken', token.accessToken);
+ await this.setValue(instanceId, 'refreshToken', token.refreshToken);
+ await this.setValue(instanceId, 'expiresOn', token.expiresOn.toString());
resolve(token.accessToken);
}
}
@@ -188,5 +183,4 @@ export class GBAdminService {
}
});
}
-
}
diff --git a/packages/admin.gbapp/strings.ts b/packages/admin.gbapp/strings.ts
index db158f9d..0775e9ed 100644
--- a/packages/admin.gbapp/strings.ts
+++ b/packages/admin.gbapp/strings.ts
@@ -4,7 +4,7 @@ export const Messages = {
welcome: 'Welcome to Pragmatismo.io GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: (command) => `I'm working on ${command}...`,
- finshed_working: 'Done.',
+ finished_working: 'Done.',
unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
hi: text => `Hello, ${text}.`,
@@ -13,7 +13,11 @@ export const Messages = {
redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `Package ${text} undeployed...`,
consent: (url) => `Please, consent access to this app at: [Microsoft Online](${url}).`,
- wrong_password: 'Sorry, wrong password. Please, try again.'
+ wrong_password: 'Sorry, wrong password. Please, try again.',
+ enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
+ enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
+ enter_authenticator_client_id: 'Enter the Client Id [Application Id](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview) GUID:',
+ enter_authenticator_client_secret: 'Enter the Client Secret:'
},
'pt-BR': {
show_video: 'Vou te mostrar um vĂdeo. Por favor, aguarde...',
diff --git a/packages/azuredeployer.gbapp/index.ts b/packages/azuredeployer.gbapp/index.ts
index 93f60ac9..ff4ac32e 100644
--- a/packages/azuredeployer.gbapp/index.ts
+++ b/packages/azuredeployer.gbapp/index.ts
@@ -36,11 +36,11 @@
'use strict';
-const UrlJoin = require('url-join');
import { GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript';
-export class GBWhatsappPackage implements IGBPackage {
+export class GBAzureDeployerPackage implements IGBPackage {
+
public sysPackages: IGBPackage[] = null;
public loadPackage(core: IGBCoreService, sequelize: Sequelize): void {}
diff --git a/packages/azuredeployer.gbapp/services/AzureDeployerService.ts b/packages/azuredeployer.gbapp/services/AzureDeployerService.ts
index b271e795..8d47ca97 100644
--- a/packages/azuredeployer.gbapp/services/AzureDeployerService.ts
+++ b/packages/azuredeployer.gbapp/services/AzureDeployerService.ts
@@ -224,7 +224,7 @@ export class AzureDeployerService extends GBService {
logger.info(`Deploying Bot Storage...`);
const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
const administratorPassword = GBAdminService.getRndPassword();
- const storageServer = `${name}-storage-server`;
+ const storageServer = `${name.toLowerCase()}-storage-server`;
const storageName = `${name}-storage`;
await this.createStorageServer(
name,
@@ -311,6 +311,11 @@ export class AzureDeployerService extends GBService {
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,
diff --git a/packages/core.gbapp/services/GBCoreService.ts b/packages/core.gbapp/services/GBCoreService.ts
index 9d376dee..cdb036a4 100644
--- a/packages/core.gbapp/services/GBCoreService.ts
+++ b/packages/core.gbapp/services/GBCoreService.ts
@@ -177,12 +177,11 @@ export class GBCoreService implements IGBCoreService {
public async syncDatabaseStructure() {
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
- const force = GBConfigService.get('STORAGE_SYNC_FORCE') === 'true';
logger.info('Syncing database...');
return this.sequelize.sync({
alter: alter,
- force: force
+ force: false // Keep it false this due to data loss danger.
});
} else {
const msg = `Database synchronization is disabled.`;
diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts
index 87cc8c91..cbed900d 100644
--- a/packages/core.gbapp/services/GBDeployer.ts
+++ b/packages/core.gbapp/services/GBDeployer.ts
@@ -43,19 +43,25 @@ const Fs = require('fs');
const WaitUntil = require('wait-until');
const express = require('express');
const child_process = require('child_process');
+const graph = require('@microsoft/microsoft-graph-client');
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
import { GBError, IGBPackage } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework';
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel';
+import { GBAdminService } from './../../admin.gbapp/services/GBAdminService';
import { KBService } from './../../kb.gbapp/services/KBService';
import { GBConfigService } from './GBConfigService';
import { GBCoreService } from './GBCoreService';
import { GBImporter } from './GBImporterService';
import { GBVMService } from './GBVMService';
-/** Deployer service for bots, themes, ai and more. */
+/**
+ *
+ * Deployer service for bots, themes, ai and more.
+ */
+
export class GBDeployer {
public static deployFolder = 'packages';
public core: IGBCoreService;
@@ -170,6 +176,25 @@ export class GBDeployer {
});
}
+ public async deployFromSharePoint(instanceId: number, path: string) {
+ const adminService = new GBAdminService(this.core);
+ const accessToken = adminService.acquireElevatedToken(instanceId);
+
+ // Initialize Graph client.
+
+ const client = graph.Client.init({
+ authProvider: done => {
+ done(null, accessToken);
+ }
+ });
+
+ const events = await client
+ .api('/me/events')
+ .select('subject,organizer,start,end')
+ .orderby('createdDateTime DESC')
+ .get();
+ }
+
public deployScriptToStorage(instanceId: number, localPath: string) {}
public deployTheme(localPath: string) {
@@ -184,6 +209,8 @@ export class GBDeployer {
// })
}
+ public async deployPackageFromSharePoint(min: GBMinInstance, path: string) {}
+
public async deployPackageFromLocalPath(min: GBMinInstance, localPath: string) {
const packageType = Path.extname(localPath);
@@ -389,24 +416,22 @@ export class GBDeployer {
try {
child_process.execSync(Path.join(e, 'node_modules/.bin/tsc'), { cwd: e });
import(e)
- .then(m => {
- const p = new m.Package();
- p.loadPackage(core, core.sequelize);
- appPackages.push(p);
- logger.info(`App (.gbapp) deployed: ${e}.`);
- appPackagesProcessed++;
- })
- .catch(err => {
- logger.error(`Error deploying .gbapp package: ${e}\n${err}`);
- appPackagesProcessed++;
- });
-
+ .then(m => {
+ const p = new m.Package();
+ p.loadPackage(core, core.sequelize);
+ appPackages.push(p);
+ logger.info(`App (.gbapp) deployed: ${e}.`);
+ appPackagesProcessed++;
+ })
+ .catch(err => {
+ logger.error(`Error deploying .gbapp package: ${e}\n${err}`);
+ appPackagesProcessed++;
+ });
} catch (error) {
logger.error(`Error compiling .gbapp package ${e}:\n${error.stdout.toString()}`);
appPackagesProcessed++;
}
}
-
} else {
appPackagesProcessed++;
}
diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts
index e5684a5a..f0ad8c7e 100644
--- a/packages/core.gbapp/services/GBMinService.ts
+++ b/packages/core.gbapp/services/GBMinService.ts
@@ -56,6 +56,7 @@ import { GuaribasInstance } from '../models/GBModel';
import { Messages } from '../strings';
import { GBAdminPackage } from './../../admin.gbapp/index';
import { GBDeployer } from './GBDeployer';
+import { ConfirmPrompt } from 'botbuilder-dialogs';
/** Minimal service layer for a bot. */
@@ -193,7 +194,7 @@ export class GBMinService {
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.authenticatorClientId
- }&redirect_uri=${min.instance.botEndpoint}/${min.instance.botId}/token`;
+ }&redirect_uri=${UrlJoin(min.instance.botEndpoint, min.instance.botId, 'token')}`;
res.redirect(authorizationUrl);
});
@@ -321,6 +322,7 @@ export class GBMinService {
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
+ min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
return { min, adapter, conversationState };
}
diff --git a/packages/core.gbapp/services/GBVMService.ts b/packages/core.gbapp/services/GBVMService.ts
index 30cd2c8d..f7d56861 100644
--- a/packages/core.gbapp/services/GBVMService.ts
+++ b/packages/core.gbapp/services/GBVMService.ts
@@ -32,12 +32,12 @@
'use strict';
+import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBCoreService } from 'botlib';
import * as fs from 'fs';
import { DialogClass } from './GBAPIService';
import { GBDeployer } from './GBDeployer';
import { TSCompiler } from './TSCompiler';
-import { WaterfallDialog } from 'botbuilder-dialogs';
const util = require('util');
const logger = require('../../../src/logger');
const vm = require('vm');
@@ -46,10 +46,14 @@ const vb2ts = require('vbscript-to-typescript/dist/converter');
/**
* @fileoverview Virtualization services for emulation of BASIC.
+ * This alpha version is using a converter to translate BASIC to TS
+ * and string replacements to emulate await code.
+ * http://stevehanov.ca/blog/index.php?id=92 should be used to run it without
+ * translation and enhance classic BASIC experience.
*/
export class GBVMService implements IGBCoreService {
- private script = new vm.Script();
+ private readonly script = new vm.Script();
public async loadJS(
filename: string,
@@ -82,7 +86,7 @@ export class GBVMService implements IGBCoreService {
// Run JS into the GB context.
const jsfile = `bot.js`;
- let localPath = UrlJoin(path, jsfile);
+ const localPath = UrlJoin(path, jsfile);
if (fs.existsSync(localPath)) {
let code: string = fs.readFileSync(localPath, 'utf8');
@@ -91,12 +95,11 @@ export class GBVMService implements IGBCoreService {
// Finds all hear calls.
let parsedCode = code;
- let hearExp = /(\w+).*hear.*\(\)/;
+ const hearExp = /(\w+).*hear.*\(\)/;
let match1;
while ((match1 = hearExp.exec(code))) {
-
let pos = 0;
// Writes async body.
@@ -110,7 +113,7 @@ export class GBVMService implements IGBCoreService {
pos = pos + match1.index;
let tempCode = code.substring(pos + match1[0].length + 1);
- let start = pos;
+ const start = pos;
// Balances code blocks and checks for exits.
@@ -143,15 +146,15 @@ export class GBVMService implements IGBCoreService {
code = parsedCode;
}
- parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\btalk\b/g, function($0, $1) {
+ parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
return $1 == undefined ? 'this.talk' : $1;
});
- parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bhear\b/g, function($0, $1) {
+ parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
return $1 == undefined ? 'this.hear' : $1;
});
- parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, function($0, $1) {
+ parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bsendEmail\b/g, ($0, $1) => {
return $1 == undefined ? 'this.sendEmail' : $1;
});
@@ -183,8 +186,8 @@ export class GBVMService implements IGBCoreService {
const cbId = step.activeDialog.state.cbId;
const cb = min.cbMap[cbId];
- cb.bind({ step: step, context: step.context }); // TODO: Necessary or min.sandbox
-
+ cb.bind({ step: step, context: step.context }); // TODO: Necessary or min.sandbox?
+
await step.endDialog();
return await cb(step.result);
diff --git a/tslint.json b/tslint.json
index 1b26d002..b23d6bb2 100644
--- a/tslint.json
+++ b/tslint.json
@@ -36,6 +36,7 @@
"export-name":false,
"no-relative-imports": false,
"no-backbone-get-set-outside-model": false,
- "max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}]
+ "max-line-length": [true,{"limit":120,"ignore-pattern":"^\\s+\\*"}],
+ "await-promise": [true, "Bluebird"]
}
}