feat(basic): General Bots BASIC 2.0 with new keywords and parenthesis only when needed.
This commit is contained in:
parent
d7c0e5c3be
commit
3cc92ecec7
13 changed files with 1518 additions and 1597 deletions
2516
package-lock.json
generated
2516
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",
|
||||
"@semantic-release/exec": "^3.3.2",
|
||||
"adal-node": "0.1.28",
|
||||
"async": "2.6.1",
|
||||
"async": "2.6.2",
|
||||
"async-promises": "0.2.1",
|
||||
"azure-arm-cognitiveservices": "2.4.1",
|
||||
"azure-arm-resource": "7.3.0",
|
||||
|
@ -60,39 +60,39 @@
|
|||
"azure-arm-website": "5.7.0",
|
||||
"bluebird": "^3.5.3",
|
||||
"body-parser": "1.18.3",
|
||||
"botbuilder": "^4.1.7",
|
||||
"botbuilder-ai": "^4.2.0",
|
||||
"botbuilder-azure": "^4.2.0",
|
||||
"botbuilder": "^4.2.1",
|
||||
"botbuilder-ai": "^4.2.1",
|
||||
"botbuilder-azure": "^4.2.1",
|
||||
"botbuilder-choices": "^4.0.0-preview1.2",
|
||||
"botbuilder-dialogs": "^4.2.0",
|
||||
"botbuilder-dialogs": "^4.2.1",
|
||||
"botbuilder-prompts": "^4.0.0-preview1.2",
|
||||
"botlib": "^0.1.10",
|
||||
"botlib": "^0.1.12",
|
||||
"chai": "4.2.0",
|
||||
"child_process": "^1.0.2",
|
||||
"chokidar": "2.0.4",
|
||||
"chokidar": "2.1.2",
|
||||
"cli-spinner": "^0.2.8",
|
||||
"csv-parse": "4.3.1",
|
||||
"csv-parse": "4.3.3",
|
||||
"dotenv-extended": "2.3.0",
|
||||
"express": "4.16.4",
|
||||
"express-promise-router": "3.0.3",
|
||||
"fs-extra": "7.0.1",
|
||||
"fs-walk": "0.0.2",
|
||||
"ip": "^1.1.5",
|
||||
"js-beautify": "^1.8.9",
|
||||
"localize": "0.4.7",
|
||||
"marked": "0.6.0",
|
||||
"mocha": "5.2.0",
|
||||
"marked": "0.6.1",
|
||||
"mocha": "6.0.1",
|
||||
"mocha-typescript": "1.1.17",
|
||||
"ms": "2.1.1",
|
||||
"ms-rest-azure": "2.6.0",
|
||||
"nexmo": "2.4.1",
|
||||
"ngrok": "^3.1.0",
|
||||
"nyc": "^13.1.0",
|
||||
"ngrok": "^3.1.1",
|
||||
"nyc": "^13.3.0",
|
||||
"opn": "^5.4.0",
|
||||
"pragmatismo-io-framework": "1.0.19",
|
||||
"process-exists": "^3.1.0",
|
||||
"public-ip": "^3.0.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"request-promise-native": "1.0.5",
|
||||
"request-promise-native": "1.0.7",
|
||||
"scanf": "^1.0.2",
|
||||
"sequelize": "4.42.0",
|
||||
"sequelize-typescript": "0.6.7",
|
||||
|
@ -100,13 +100,13 @@
|
|||
"simple-git": "^1.107.0",
|
||||
"sqlite3": "4.0.6",
|
||||
"strict-password-generator": "^1.1.2",
|
||||
"swagger-client": "3.8.22",
|
||||
"tedious": "4.1.3",
|
||||
"swagger-client": "3.8.24",
|
||||
"tedious": "5.0.3",
|
||||
"ts-node": "8.0.2",
|
||||
"typedoc": "0.14.2",
|
||||
"typedoc-plugin-external-module-name": "^2.0.0",
|
||||
"typedoc-plugin-markdown": "^1.1.25",
|
||||
"typescript": "3.3.1",
|
||||
"typedoc-plugin-markdown": "^1.1.26",
|
||||
"typescript": "3.3.3333",
|
||||
"url-join": "4.0.0",
|
||||
"vbscript-to-typescript": "^1.0.8",
|
||||
"wait-until": "0.0.2",
|
||||
|
@ -121,13 +121,13 @@
|
|||
"@semantic-release/npm": "^5.1.4",
|
||||
"@semantic-release/release-notes-generator": "^7.1.4",
|
||||
"@types/chai": "4.1.7",
|
||||
"@types/mocha": "5.2.5",
|
||||
"@types/sequelize": "4.27.34",
|
||||
"@types/mocha": "5.2.6",
|
||||
"@types/sequelize": "4.27.37",
|
||||
"@types/url-join": "4.0.0",
|
||||
"@types/winston": "2.4.4",
|
||||
"ban-sensitive-files": "1.9.2",
|
||||
"commitizen": "^3.0.5",
|
||||
"coveralls": "^3.0.2",
|
||||
"commitizen": "^3.0.7",
|
||||
"coveralls": "^3.0.3",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"dependency-check": "3.3.0",
|
||||
"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(
|
||||
new WaterfallDialog('/setupSecurity', [
|
||||
async step => {
|
||||
|
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
|||
const Parse = require('csv-parse');
|
||||
const Async = require('async');
|
||||
const UrlJoin = require('url-join');
|
||||
const Walk = require('fs-walk');
|
||||
const logger = require('../../../src/logger');
|
||||
const Swagger = require('swagger-client');
|
||||
const rp = require('request-promise');
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
import { TurnContext } from 'botbuilder';
|
||||
import { WaterfallStepContext } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance } from 'botlib';
|
||||
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
|
||||
const WaitUntil = require('wait-until');
|
||||
|
||||
class SysClass {
|
||||
|
@ -44,7 +45,11 @@ class SysClass {
|
|||
this.min = min;
|
||||
}
|
||||
|
||||
public async deployBot(
|
||||
public generatePassword(){
|
||||
return GBAdminService.getRndPassword();
|
||||
}
|
||||
|
||||
public async createABotFarmUsing(
|
||||
botId,
|
||||
description,
|
||||
location,
|
||||
|
|
|
@ -231,7 +231,7 @@ export class GBDeployer {
|
|||
|
||||
case '.gbdialog':
|
||||
const vm = new GBVMService();
|
||||
return vm.loadJS(localPath, min, this.core, this, localPath);
|
||||
return vm.loadDialogPackage(localPath, min, this.core, this);
|
||||
|
||||
default:
|
||||
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 { ConfirmPrompt } from 'botbuilder-dialogs';
|
||||
import { GBMinInstance, IGBAdminService, IGBConversationalService, IGBCoreService, IGBPackage } from 'botlib';
|
||||
import { GBAnalyticsPackage } from '../../analytics.gblib';
|
||||
import { GBCorePackage } from '../../core.gbapp';
|
||||
|
@ -56,7 +57,6 @@ 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. */
|
||||
|
||||
|
@ -108,7 +108,7 @@ export class GBMinService {
|
|||
const uiPackage = 'default.gbui';
|
||||
server.use('/', express.static(UrlJoin(GBDeployer.deployFolder, uiPackage, 'build')));
|
||||
|
||||
Promise.all(
|
||||
await Promise.all(
|
||||
instances.map(async instance => {
|
||||
// Gets the authorization key for each instance from Bot Service.
|
||||
|
||||
|
@ -119,39 +119,7 @@ export class GBMinService {
|
|||
|
||||
server.get('/instances/:botId', (req, res) => {
|
||||
(async () => {
|
||||
// Returns the instance object to clients requesting bot info.
|
||||
|
||||
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);
|
||||
}
|
||||
await this.sendInstanceToClient(req, bootInstance, res, webchatToken);
|
||||
})();
|
||||
});
|
||||
|
||||
|
@ -171,7 +139,7 @@ export class GBMinService {
|
|||
|
||||
const url = `/api/messages/${instance.botId}`;
|
||||
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}.`);
|
||||
|
||||
|
@ -186,45 +154,32 @@ export class GBMinService {
|
|||
// 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
|
||||
// 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
|
||||
// is invoked via the AuthenticationContext and retrieves an
|
||||
// access token that can be used to access the user owned resource.
|
||||
|
||||
this.handleOAuthTokenRequests(server, min, instance);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
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) => {
|
||||
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);
|
||||
|
@ -234,14 +189,50 @@ export class GBMinService {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,7 +241,7 @@ export class GBMinService {
|
|||
* @param instance The Bot instance.
|
||||
*
|
||||
*/
|
||||
public async getWebchatToken(instance: any) {
|
||||
private async getWebchatToken(instance: any) {
|
||||
const options = {
|
||||
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||
method: 'POST',
|
||||
|
@ -276,7 +267,7 @@ export class GBMinService {
|
|||
* @param instance The general bot instance.
|
||||
*
|
||||
*/
|
||||
public async getSTSToken(instance: any) {
|
||||
private async getSTSToken(instance: any) {
|
||||
// TODO: Make dynamic: https://CHANGE.api.cognitive.microsoft.com/sts/v1.0
|
||||
|
||||
const options = {
|
||||
|
@ -327,7 +318,7 @@ export class GBMinService {
|
|||
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>();
|
||||
// NOTE: A semicolon is necessary before this line.
|
||||
[
|
||||
|
@ -364,11 +355,13 @@ export class GBMinService {
|
|||
req: any,
|
||||
res: any,
|
||||
conversationState: ConversationState,
|
||||
min: any,
|
||||
min: GBMinInstance,
|
||||
instance: any,
|
||||
appPackages: any[]
|
||||
) {
|
||||
return await adapter.processActivity(req, res, async context => {
|
||||
|
||||
await adapter.processActivity(req, res, async context => {
|
||||
|
||||
// Get loaded user state
|
||||
const state = await conversationState.get(context);
|
||||
const step = await min.dialogs.createContext(context, state);
|
||||
|
@ -412,32 +405,7 @@ export class GBMinService {
|
|||
// Processes messages.
|
||||
} else if (context.activity.type === 'message') {
|
||||
// Checks for /admin request.
|
||||
if (context.activity.text === 'alpha-vba') {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
await this.processMessageActivity(context, min, step);
|
||||
|
||||
// Processes events.
|
||||
} else if (context.activity.type === 'event') {
|
||||
|
@ -445,6 +413,20 @@ export class GBMinService {
|
|||
|
||||
// TODO: Understand MSFT changes: await step.endAll();
|
||||
|
||||
await this.processEventActivity(context, step);
|
||||
}
|
||||
await conversationState.saveChanges(context, true);
|
||||
} catch (error) {
|
||||
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
|
||||
logger.error(msg);
|
||||
|
||||
await step.context.sendActivity(Messages[step.context.activity.locale].very_sorry_about_error);
|
||||
await step.beginDialog('/ask', { isReturning: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async processEventActivity(context, step: any) {
|
||||
if (context.activity.name === 'whoAmI') {
|
||||
await step.beginDialog('/whoAmI');
|
||||
} else if (context.activity.name === 'showSubjects') {
|
||||
|
@ -457,28 +439,55 @@ export class GBMinService {
|
|||
await step.beginDialog('/faq');
|
||||
} else if (context.activity.name === 'answerEvent') {
|
||||
await step.beginDialog('/answerEvent', {
|
||||
questionId: (context.activity as any).data,
|
||||
questionId: (context.activity).data,
|
||||
fromFaq: true
|
||||
});
|
||||
} else if (context.activity.name === 'quality') {
|
||||
await step.beginDialog('/quality', {
|
||||
score: (context.activity as any).data
|
||||
score: (context.activity).data
|
||||
});
|
||||
} else if (context.activity.name === 'updateToken') {
|
||||
const token = (context.activity as any).data;
|
||||
const token = (context.activity).data;
|
||||
await step.beginDialog('/adminUpdateToken', { token: token });
|
||||
} else {
|
||||
await step.continueDialog();
|
||||
}
|
||||
}
|
||||
await conversationState.saveChanges(context, true);
|
||||
} catch (error) {
|
||||
const msg = `ERROR: ${error.message} ${error.stack ? error.stack : ''}`;
|
||||
logger.error(msg);
|
||||
|
||||
await step.context.sendActivity(Messages[step.context.activity.locale].very_sorry_about_error);
|
||||
await step.beginDialog('/ask', { isReturning: true });
|
||||
}
|
||||
private async processMessageActivity(context, min: GBMinInstance, step: any) {
|
||||
|
||||
// Direct script invoking by itent name.
|
||||
|
||||
let mainMethod = min.scriptMap[context.activity.text];
|
||||
if (mainMethod != undefined) {
|
||||
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 { GBMinInstance, IGBCoreService } from 'botlib';
|
||||
import * as fs from 'fs';
|
||||
import DialogClass from './GBAPIService';
|
||||
import { GBDeployer } from './GBDeployer';
|
||||
import { TSCompiler } from './TSCompiler';
|
||||
const util = require('util');
|
||||
import GBAPIService from './GBAPIService';
|
||||
import DialogClass from './GBAPIService';
|
||||
const walkPromise = require('walk-promise');
|
||||
const logger = require('../../../src/logger');
|
||||
const vm = require('vm');
|
||||
const UrlJoin = require('url-join');
|
||||
const vb2ts = require('vbscript-to-typescript/dist/converter');
|
||||
var beautify = require('js-beautify').js;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* This alpha version is using a hack in form of converter to
|
||||
* 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
|
||||
* translation and enhance classic BASIC experience.
|
||||
*/
|
||||
|
@ -55,41 +58,95 @@ const vb2ts = require('vbscript-to-typescript/dist/converter');
|
|||
export class GBVMService implements IGBCoreService {
|
||||
private readonly script = new vm.Script();
|
||||
|
||||
public async loadJS(
|
||||
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);
|
||||
public async loadDialogPackage(folder: string, min: GBMinInstance, core: IGBCoreService, deployer: GBDeployer) {
|
||||
const files = await walkPromise(folder);
|
||||
|
||||
// Example when handled through fs.watch() listener
|
||||
fs.watchFile(source, async (curr, prev) => {
|
||||
await this.run(source, path, min, deployer, filename);
|
||||
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] = ;
|
||||
|
||||
const filename = UrlJoin(folder, file.name);
|
||||
fs.watchFile(filename, async () => {
|
||||
await this.run(filename, min, deployer, mainName);
|
||||
});
|
||||
await this.run(source, path, min, deployer, filename);
|
||||
await this.run(filename, min, deployer, mainName);
|
||||
this.addHearDialog(min);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts General Bots BASIC
|
||||
*
|
||||
*
|
||||
* @param code General Bots BASIC
|
||||
*/
|
||||
public convertGBASICToVBS(code: string) {
|
||||
// Start and End of VB2TS tags of processing.
|
||||
|
||||
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');
|
||||
|
||||
public async run(source: any, path: string, min: any, deployer: GBDeployer, filename: string) {
|
||||
// Converts VBS into TS.
|
||||
|
||||
vb2ts.convertFile(source);
|
||||
vb2ts.convertFile(vbsFile);
|
||||
|
||||
// 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();
|
||||
tsc.compile([UrlJoin(path, tsfile)]);
|
||||
tsc.compile([tsfile]);
|
||||
|
||||
// Run JS into the GB context.
|
||||
const jsfile = `bot.js`;
|
||||
const localPath = UrlJoin(path, jsfile);
|
||||
const jsfile = `${tsfile}.js`.replace('.ts', '');
|
||||
|
||||
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, '');
|
||||
|
||||
// Finds all hear calls.
|
||||
|
@ -104,7 +161,7 @@ export class GBVMService implements IGBCoreService {
|
|||
|
||||
// Writes async body.
|
||||
|
||||
const variable = match1[1]; // variable = hear();
|
||||
const variable = match1[1]; // Construct variable = hear ().
|
||||
|
||||
parsedCode = code.substring(pos, pos + match1.index);
|
||||
parsedCode += `hear (async (${variable}) => {\n`;
|
||||
|
@ -146,22 +203,10 @@ export class GBVMService implements IGBCoreService {
|
|||
code = parsedCode;
|
||||
}
|
||||
|
||||
parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\btalk\b/g, ($0, $1) => {
|
||||
return $1 == undefined ? 'this.talk' : $1;
|
||||
});
|
||||
parsedCode = this.handleThisAndAwait(parsedCode);
|
||||
|
||||
parsedCode = parsedCode.replace(/("[^"]*"|'[^']*')|\bhear\b/g, ($0, $1) => {
|
||||
return $1 == undefined ? 'this.hear' : $1;
|
||||
});
|
||||
|
||||
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);
|
||||
parsedCode = beautify(parsedCode, { indent_size: 2, space_in_empty_paren: true })
|
||||
fs.writeFileSync(jsfile, parsedCode);
|
||||
|
||||
const sandbox: DialogClass = new DialogClass(min);
|
||||
const context = vm.createContext(sandbox);
|
||||
|
@ -172,6 +217,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) {
|
||||
min.dialogs.add(
|
||||
new WaterfallDialog('/hear', [
|
||||
|
|
|
@ -1,54 +1,20 @@
|
|||
<%
|
||||
'****************************************************************************
|
||||
' ( )_ _
|
||||
' _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _
|
||||
' ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' _ `\ /'_`\
|
||||
' | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| ( ) |( (_) )
|
||||
' | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/'
|
||||
' | | ( )_) |
|
||||
' (_) \___/'
|
||||
'
|
||||
' 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.
|
||||
'
|
||||
'****************************************************************************
|
||||
' General Bots Copyright (c) Pragmatismo.io. All rights reserved. Licensed under the AGPL-3.0.
|
||||
|
||||
talk ("How many installments do you want to pay your Credit?")
|
||||
installments = hear ()
|
||||
talk "How many installments do you want to pay your Credit?"
|
||||
hear installments
|
||||
|
||||
if installments > 60 then
|
||||
talk ("The maximum number of payments is 60")
|
||||
talk "The maximum number of payments is 60"
|
||||
else
|
||||
talk ("What is the amount requested?")
|
||||
ammount = hear ()
|
||||
talk "What is the amount requested?"
|
||||
hear ammount
|
||||
|
||||
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
|
||||
|
||||
talk ("What is the best due date?")
|
||||
dueDate = hear ()
|
||||
talk "What is the best due date?"
|
||||
hear dueDate
|
||||
|
||||
interestRate = 0
|
||||
adjustment = 0
|
||||
|
@ -84,15 +50,15 @@ else
|
|||
end if
|
||||
|
||||
if installments > 60 then
|
||||
talk ("The maximum number of payments is 60")
|
||||
talk "The maximum number of payments is 60"
|
||||
end if
|
||||
|
||||
|
||||
' TODO: This must be reviewed in terms of financing logic.
|
||||
|
||||
nInstallments = parseInt(installments)
|
||||
vAmmount = parseFloat(ammount)
|
||||
initialPayment = parseFloat(vAmmount) * 0.3 ' 30% of the value
|
||||
nInstallments = parseIntinstallments
|
||||
vAmmount = parseFloatammount
|
||||
initialPayment = parseFloatvAmmount * 0.3 ' 30% of the value
|
||||
tac = 800
|
||||
adjustment = 1.3
|
||||
|
||||
|
@ -100,13 +66,12 @@ else
|
|||
paymentValue = totalValue * adjustment
|
||||
finalValue = paymentValue * nInstallments + initialPayment
|
||||
|
||||
talk("Congratulations! Your credit analysis is **done**:")
|
||||
talk("First payment: **" + initialPayment + "**")
|
||||
talk("Payment value: **" + paymentValue + "**")
|
||||
talk("Interest Rate: **" + interestRate + "%**")
|
||||
talk("Total Value: **" + totalValue + "**")
|
||||
talk("Final Value: **" + finalValue + "**")
|
||||
talk "Congratulations! Your credit analysis is **done**:"
|
||||
talk "First payment: **" + initialPayment + "**"
|
||||
talk "Payment value: **" + paymentValue + "**"
|
||||
talk "Interest Rate: **" + interestRate + "%**"
|
||||
talk "Total Value: **" + totalValue + "**"
|
||||
talk "Final Value: **" + finalValue + "**"
|
||||
|
||||
end if
|
||||
end if
|
||||
%>
|
|
@ -1,8 +1,7 @@
|
|||
' 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.
|
||||
|
||||
talk "Please, tell me what is the Bot name?"
|
||||
hear 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)"
|
||||
|
@ -16,7 +15,8 @@ talk "Can you describe in a few words what the bot is about?"
|
|||
hear description
|
||||
|
||||
talk "Please, choose what subscription would you like to connect to:"
|
||||
hear one of subscriptions (email, password) into subscriptionId
|
||||
|
||||
subscriptionId = ""
|
||||
|
||||
talk "Please, provide the cloud location just like 'westus'?"
|
||||
hear cloudLocation
|
||||
|
@ -26,6 +26,7 @@ hear nlpKey
|
|||
|
||||
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 1
|
||||
|
||||
talk "Please, provide the App ID you just generated:"
|
||||
hear appId
|
||||
|
||||
|
@ -33,4 +34,5 @@ talk "Please, provide the Generated Password:"
|
|||
hear appPassword
|
||||
|
||||
talk "Now, I am going to create a Bot farm... Wait 5 minutes or more..."
|
||||
create bot farm (name, username, password, description, cloudLocation, nlpKey, appId, appPassword, subscriptionId)
|
||||
|
||||
create a bot farm using title, username, password, description, cloudLocation, nlpKey, appId, appPassword, subscriptionId
|
||||
|
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
|||
const Parse = require('csv-parse');
|
||||
const Async = require('async');
|
||||
const UrlJoin = require('url-join');
|
||||
const Walk = require('fs-walk');
|
||||
const logger = require('../../../src/logger');
|
||||
|
||||
import { GBService, GBServiceCallback, IGBInstance } from 'botlib';
|
||||
|
|
|
@ -36,7 +36,6 @@ const _ = require('lodash');
|
|||
const Parse = require('csv-parse');
|
||||
const Async = require('async');
|
||||
const UrlJoin = require('url-join');
|
||||
const Walk = require('fs-walk');
|
||||
const logger = require('../../../src/logger');
|
||||
const Swagger = require('swagger-client');
|
||||
const 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 { GBMinService } from '../packages/core.gbapp/services/GBMinService';
|
||||
import { GBVMService } from '../packages/core.gbapp/services/GBVMService';
|
||||
import { load } from 'dotenv';
|
||||
|
||||
const appPackages = new Array<IGBPackage>();
|
||||
|
||||
|
@ -163,6 +164,9 @@ export class GBServer {
|
|||
}
|
||||
}
|
||||
|
||||
let service:GBVMService = new GBVMService ();
|
||||
service.loadDialogPackage('C:\\Sources\\opensource\\BotServer\\packages\\default.gbdialog',null,null,null);
|
||||
|
||||
// First line to run.
|
||||
|
||||
GBServer.run();
|
||||
// GBServer.run();
|
||||
|
|
Loading…
Add table
Reference in a new issue