fix(basic.gblib): COPY and CONVERT is now generating good JS.

This commit is contained in:
Rodrigo Rodriguez 2021-01-20 18:23:42 -03:00
parent 8614ff4a8e
commit 3f13609d59
13 changed files with 262 additions and 63 deletions

7
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "botserver",
"version": "2.0.79",
"version": "2.0.91",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -20344,6 +20344,11 @@
"spdx-ranges": "^2.0.0"
}
},
"speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
},
"split": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",

View file

@ -100,6 +100,7 @@
"sequelize": "5.21.5",
"sequelize-typescript": "1.1.0",
"simple-git": "2.23.0",
"speakingurl": "^14.0.1",
"sppull": "2.6.7",
"strict-password-generator": "1.1.2",
"swagger-client": "2.1.18",

View file

@ -49,6 +49,7 @@ import { GuaribasAdmin } from '../models/AdminModel';
const Path = require('path');
const msRestAzure = require('ms-rest-azure');
const PasswordGenerator = require('strict-password-generator').default;
const crypto = require("crypto");
/**
* Services for server administration.
@ -145,6 +146,21 @@ export class GBAdminService implements IGBAdminService {
return passwordGenerator.generatePassword(options);
}
/**
* @see https://stackoverflow.com/a/52171480
*/
public static getHash(str, seed = 0) {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}
public static async undeployPackageCommand(text: any, min: GBMinInstance) {
const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core);
@ -176,7 +192,7 @@ export class GBAdminService implements IGBAdminService {
// .gbot packages are handled using storage API, so no download
// of local resources is required.
if (!localFolder.endsWith('.gbot')) {
GBLog.warn(`${GBConfigService.get('CLOUD_USERNAME')} must be authorized on SharePoint related site to download to: ${localFolder}`);
await s.downloadFolder(
@ -261,7 +277,7 @@ export class GBAdminService implements IGBAdminService {
const refreshToken = await this.getValue(instanceId, 'refreshToken');
const resource = 'https://graph.microsoft.com';
const authenticationContext = new AuthenticationContext(authorizationUrl);
authenticationContext.acquireTokenWithRefreshToken(
refreshToken,
instance.marketplaceId,

View file

@ -289,8 +289,12 @@ export class GBVMService extends GBService {
return `sendFile (step, ${$3})\n`;
});
code = code.replace(/(COPY)(\s*)(.*)/gi, ($0, $1, $2, $3) => {
return `sys().copyFile (step, ${$3})\n`;
code = code.replace(/(copy)(\s*)(.*)/gi, ($0, $1, $2, $3) => {
return `sys().copyFile(step, ${$3})\n`;
});
code = code.replace(/(convert)(\s*)(.*)/gi, ($0, $1, $2, $3) => {
return `sys().convert(step, ${$3})\n`;
});
code = code.replace(/(save)(\s)(.*)/gi, ($0, $1, $2, $3) => {
@ -433,9 +437,6 @@ export class GBVMService extends GBService {
code = code.replace(/("[^"]*"|'[^']*')|\bsendFileTo\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.sendFileTo' : $1;
});
code = code.replace(/("[^"]*"|'[^']*')|\bcopyFile\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.copyFile' : $1;
});
code = code.replace(/("[^"]*"|'[^']*')|\bsendFile\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.sendFile' : $1;
});
@ -448,7 +449,7 @@ export class GBVMService extends GBService {
code = code.replace(/("[^"]*"|'[^']*')|\bmenu\b/gi, ($0, $1) => {
return $1 === undefined ? 'this.menu' : $1;
});
// await insertion.
code = code.replace(/this\./gm, 'await this.');

View file

@ -186,7 +186,7 @@ export class GBDeployer implements IGBDeployer {
const instances = await core.loadInstances();
await CollectionUtil.asyncForEach(instances, async instance => {
this.mountGBKBAssets(`${instance.botId}.gbkb`,
instance.botId, `${instance.botId}.gbkb`);
instance.botId, `${instance.botId}.gbkb`);
});
GBLog.info(`Package deployment done.`);
@ -320,7 +320,7 @@ export class GBDeployer implements IGBDeployer {
public async publishNLP(instance: IGBInstance): Promise<void> {
const service = new AzureDeployerService(this);
const res = await service.publishNLP(instance.cloudLocation, instance.nlpAppId,
instance.nlpAuthoringKey);
instance.nlpAuthoringKey);
if (res.status !== 200 && res.status !== 201) { throw res.bodyAsText; }
}
@ -371,29 +371,29 @@ export class GBDeployer implements IGBDeployer {
const libraryId = process.env.STORAGE_LIBRARY;
GBLog.info(`Connecting to Config.xslx (siteId: ${siteId}, libraryId: ${libraryId})...`);
// Connects to MSFT storage.
const token = await min.adminService.acquireElevatedToken(min.instance.instanceId);
const client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
}
});
// Retrieves all files in .bot folder.
const botId = min.instance.botId;
const path = `/${botId}.gbai/${botId}.gbot`;
let url = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:${path}:/children`;
GBLog.info(`Loading .gbot from Excel: ${url}`);
const res = await client
.api(url)
.get();
.api(url)
.get();
// Finds Config.xlsx.
const document = res.value.filter(m => {
return m.name === 'Config.xlsx';
});
@ -496,14 +496,14 @@ export class GBDeployer implements IGBDeployer {
if (handled) {
return pck;
}
// Deploy platform packages here accordingly to their extension.
switch (packageType) {
case '.gbot':
// Extracts configuration information from .gbot files.
if (process.env.ENABLE_PARAMS_ONLINE === 'false') {
if (Fs.existsSync(localPath)) {
GBLog.info(`Loading .gbot from ${localPath}.`);
@ -512,9 +512,9 @@ export class GBDeployer implements IGBDeployer {
} else {
min.instance.params = await this.loadParamsFromTabular(min);
}
// Updates instance object.
await this.core.saveInstance(min.instance);
break;
@ -561,6 +561,7 @@ export class GBDeployer implements IGBDeployer {
break;
default:
const err = GBError.create(`Unhandled package type: ${packageType}.`);
Promise.reject(err);
break;
@ -581,6 +582,7 @@ export class GBDeployer implements IGBDeployer {
// Removes objects from storage, cloud resources and local files if any.
switch (packageType) {
case '.gbot':
const packageObject = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'package.json'), 'utf8'));
await this.undeployBot(packageObject.botId, packageName);
@ -596,17 +598,17 @@ export class GBDeployer implements IGBDeployer {
break;
case '.gbtheme':
rimraf.sync(localPath);
break;
case '.gbdialog':
rimraf.sync(localPath);
break;
case '.gblib':
break;
case '.gbapp':
break;
default:
const err = GBError.create(`Unhandled package type: ${packageType}.`);
Promise.reject(err);
@ -647,6 +649,7 @@ export class GBDeployer implements IGBDeployer {
}
// Removes the index.
try {
await search.deleteIndex();
} catch (err) {
@ -659,6 +662,7 @@ export class GBDeployer implements IGBDeployer {
}
// Creates the data source and index on the cloud.
try {
await search.createDataSource(dsName, dsName, 'GuaribasQuestion', 'azuresql', connectionString);
} catch (err) {
@ -694,7 +698,7 @@ export class GBDeployer implements IGBDeployer {
if (!Fs.existsSync(`${root}/build`) && process.env.DISABLE_WEB !== 'true') {
// Write a .env required to fix some bungs in create-react-app facility.
// Write a .env required to fix some bungs in create-react-app tool.
Fs.writeFileSync(`${root}/.env`, 'SKIP_PREFLIGHT_CHECK=true');
@ -702,15 +706,18 @@ export class GBDeployer implements IGBDeployer {
GBLog.info(`Installing modules default.gbui (It may take a few minutes)...`);
child_process.execSync(`${npm} install`, { cwd: root });
GBLog.info(`Transpiling default.gbui...`);
child_process.execSync(`${npm} run build`, { cwd: root });
}
// Clean up node_modules folder as it is only needed during compile time.
GBLog.info(`Cleaning default.gbui node_modules...`);
const nodeModules = urlJoin(root, 'node_modules');
rimraf.sync(nodeModules);
if (Fs.existsSync(nodeModules)) {
rimraf.sync(nodeModules);
GBLog.info(`Cleaning default.gbui node_modules...`);
}
}
/**
@ -729,13 +736,13 @@ export class GBDeployer implements IGBDeployer {
const gbaiName = `${botId}.gbai`;
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/assets`,
express.static(urlJoin('work', gbaiName, filename, 'assets')));
express.static(urlJoin('work', gbaiName, filename, 'assets')));
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/images`,
express.static(urlJoin('work', gbaiName, filename, 'images')));
express.static(urlJoin('work', gbaiName, filename, 'images')));
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/audios`,
express.static(urlJoin('work', gbaiName, filename, 'audios')));
express.static(urlJoin('work', gbaiName, filename, 'audios')));
GBServer.globals.server.use(`/kb/${gbaiName}/${packageName}/videos`,
express.static(urlJoin('work', gbaiName, filename, 'videos')));
express.static(urlJoin('work', gbaiName, filename, 'videos')));
GBLog.info(`KB (.gbkb) assets accessible at: /kb/${botId}.gbai/${packageName}.`);
}

View file

@ -131,6 +131,7 @@ export class GBMinService {
public async buildMin(instances: IGBInstance[]) {
// Servers default UI on root address '/' if web enabled.
if (process.env.DISABLE_WEB !== 'true') {
const url = GBServer.globals.wwwroot
? GBServer.globals.wwwroot
@ -139,6 +140,8 @@ export class GBMinService {
GBServer.globals.server.use('/', express.static(url));
}
// Servers the bot information object via HTTP so clients can get
// instance information stored on server.
@ -216,12 +219,13 @@ export class GBMinService {
await this.invokeLoadBot(GBServer.globals.appPackages, GBServer.globals.sysPackages, min);
// Serves individual URL for each bot conversational interface...
// Serves individual URL for each bot conversational interface.
const url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, async (req, res) => {
const receiver = async (req, res) => {
await this.receiver(adapter, req, res, conversationState, min, instance, GBServer.globals.appPackages);
});
};
const url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, receiver);
GBLog.info(`GeneralBots(${instance.engineName}) listening on: ${url}.`);
// Serves individual URL for each bot user interface.
@ -237,7 +241,14 @@ export class GBMinService {
uiUrlAlt,
express.static(urlJoin(GBDeployer.deployFolder, GBMinService.uiPackage, 'build'))
);
const domain = min.core.getParam(min.instance, 'Domain', null);
if (domain) {
GBServer.globals.server.use(
domain,
express.static(urlJoin(GBDeployer.deployFolder, GBMinService.uiPackage, 'build'))
);
GBLog.info(`Bot UI ${GBMinService.uiPackage} accessible at custom domain: ${domain}.`);
}
GBLog.info(`Bot UI ${GBMinService.uiPackage} accessible at: ${uiUrl} and ${uiUrlAlt}.`);
}
@ -512,7 +523,12 @@ export class GBMinService {
speechToken: speechToken,
conversationId: webchatTokenContainer.conversationId,
authenticatorTenant: instance.authenticatorTenant,
authenticatorClientId: instance.marketplaceId
authenticatorClientId: instance.marketplaceId,
paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null),
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null)
})
);
} else {
@ -603,6 +619,7 @@ export class GBMinService {
if (GBServer.globals.minBoot === undefined) {
GBServer.globals.minBoot = min;
}
// TODO: min.appPackages = core.getPackagesByInstanceId(min.instance.instanceId);
// Creates a hub of services available in .gbapps.
@ -735,7 +752,7 @@ export class GBMinService {
user.welcomed = false;
firstTime = true;
// Sends loadInstance event to .gbui clients.
// Sends loadInstance event to .gbui clients and loads FAQ.
await min.conversationalService.sendEvent(min, step, 'loadInstance', {
instanceId: instance.instanceId,
@ -743,6 +760,12 @@ export class GBMinService {
theme: instance.theme ? instance.theme : 'default.gbtheme',
secret: instance.webchatKey
});
const service = new KBService(min.core.sequelize);
const data = await service.getFaqBySubjectArray('faq', undefined);
await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'bullet',
data: data.slice(0, 10)
});
// This same event is dispatched either to all participants
// including the bot, that is filtered bellow.

View file

@ -1199,6 +1199,11 @@
"@types/yargs": "^13.0.0"
}
},
"@midudev/react-static-content": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@midudev/react-static-content/-/react-static-content-1.0.4.tgz",
"integrity": "sha512-P2+rdqhysYNon5/noOoUiFJghVxbl64qGgxsKwe10mjrkQvgIy4vCcOLN5Nw00er/cBSuWY/hVHkuSeQ6sI5VA=="
},
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@ -11919,6 +11924,37 @@
"shallowequal": "^1.0.1"
}
},
"react-super-seo": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/react-super-seo/-/react-super-seo-1.0.5.tgz",
"integrity": "sha512-OtenkmcfjA97kaem/HxL0oaRO3hYhfalaoNt2I1nAVMzGh3I/xDWwy+J4ynA6LlPc+SqBY5BHcrRzjWT75hr1A==",
"requires": {
"react-helmet": "^6.0.0"
},
"dependencies": {
"react-fast-compare": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
},
"react-helmet": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
"integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.1.1",
"react-side-effect": "^2.1.0"
}
},
"react-side-effect": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz",
"integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ=="
}
}
},
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",

View file

@ -7,6 +7,7 @@
"license": "AGPL-3.0",
"homepage": ".",
"dependencies": {
"@midudev/react-static-content": "^1.0.4",
"ajv": "^6.10.2",
"botframework-directlinejs": "0.11.6",
"botframework-webchat": "^4.7.1",
@ -20,6 +21,7 @@
"react-player": "^1.14.2",
"react-powerbi": "0.5.2",
"react-scripts": "^3.3.0",
"react-super-seo": "^1.0.5",
"react-transition-group": "^4.3.0",
"rxjs": "^6.5.4",
"url-join": "4.0.1"

View file

@ -37,12 +37,13 @@ import GBVideoPlayer from './players/GBVideoPlayer.js';
import GBLoginPlayer from './players/GBLoginPlayer.js';
import GBBulletPlayer from './players/GBBulletPlayer.js';
import SidebarMenu from './components/SidebarMenu.js';
import SEO from './components/SEO.js';
import GBCss from './components/GBCss.js';
import { DirectLine } from 'botframework-directlinejs';
import { ConnectionStatus } from 'botframework-directlinejs';
import ReactWebChat from 'botframework-webchat';
import { UserAgentApplication } from 'msal';
import StaticContent from '@midudev/react-static-content'
class GBUIApp extends React.Component {
constructor() {
@ -138,7 +139,7 @@ class GBUIApp extends React.Component {
}
authenticate() {
if (this.state.instanceClient.authenticatorClientId === null) {
return;
}
@ -343,14 +344,17 @@ class GBUIApp extends React.Component {
}
return (
<div>
{gbCss}
{sideBar}
<div className="player">{playerComponent}</div>
<div className="webchat">
{chat}
<StaticContent>
<SEO></SEO>
<div>
{gbCss}
{sideBar}
<div className="player">{playerComponent}</div>
<div className="webchat">
{chat}
</div>
</div>
</div>
</StaticContent>
);
}
}

View file

@ -0,0 +1,58 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' v `\ /'_`\ |
| | (_) )| | ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) ) |
| | ,__/'(_) `\__,_)`\__ |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/' |
| | | ( )_) | |
| (_) \___/' |
| |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of Pragmatismo.io. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
import React from "react"
import { SuperSEO } from 'react-super-seo';
const footer = () => (
<SuperSEO
title={this.props.instance.title}
description={this.props.instance.description}
lang="en"
openGraph={{
ogImage: {
ogImage: this.props.instance.paramLogoImageUrl,
ogImageAlt: this.props.instance.paramLogoImageAlt,
ogImageWidth: this.props.instance.paramLogoImageWidth,
ogImageHeight: this.props.instance.paramLogoImageHeight,
ogImageType: this.props.instance.paramLogoImageType,
},
}}
twitter={{
twitterSummaryCard: {
summaryCardImage: this.props.instance.paramLogoImageUrl,
summaryCardImageAlt: this.props.instance.paramLogoImageAlt,
summaryCardSiteUsername: this.props.instance.paramTwitterUsername,
},
}}
/>);
export default footer

View file

@ -50,11 +50,10 @@ class SideBarMenu extends React.Component {
return (
<div>
<div className="tittleSideBarMenu">
<img
className="pragmatismoLogo"
src={"/themes/" + this.props.instance.theme + "/images/logo.png"}
alt="General Bots Logo"
/>
<img
className="pragmatismoLogo"
src={"/themes/" + this.props.instance.theme + "/images/logo.png"}
alt="General Bots Logo" />
</div>
<div className="SidebarMenu">
@ -72,16 +71,14 @@ class SideBarMenu extends React.Component {
<div className="iconMenu">
<span
className="iconText"
onClick={() => this.send("showSubjects")}
>
onClick={() => this.send("showSubjects")}>
Subjects
</span>
</div>
<div className="iconMenu">
<span
className="iconText"
onClick={() => this.send("giveFeedback")}
>
onClick={() => this.send("giveFeedback")}>
Suggestions
</span>
</div>

View file

@ -44,6 +44,7 @@ const walkPromise = require('walk-promise');
// tslint:disable-next-line:newline-per-chained-call
const { SearchService } = require('azure-search-client');
const Excel = require('exceljs');
const getSlug = require('speakingurl');
import {
GBDialogStep,
GBLog,
@ -53,15 +54,14 @@ import {
IGBInstance,
IGBKBService
} from 'botlib';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { Op } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { GBServer } from '../../../src/app';
import { AzureDeployerService } from '../../azuredeployer.gbapp/services/AzureDeployerService';
import { GuaribasPackage } from '../../core.gbapp/models/GBModel';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer';
import { CSService } from '../../customer-satisfaction.gbapp/services/CSService';
import { SecService } from '../../security.gbapp/services/SecService';
import { GuaribasAnswer, GuaribasQuestion, GuaribasSubject } from '../models';
import { Messages } from '../strings';
import { GBConfigService } from './../../core.gbapp/services/GBConfigService';
@ -137,6 +137,54 @@ export class KBService implements IGBKBService {
});
}
/**
* Returns a question object given a SEO friendly URL.
*/
public async getQuestionIdFromURL(core: IGBCoreService, url: string) {
// Extracts questionId from URL.
const id = url.substr(url.lastIndexOf('-') + 1);
// Extracts botId from URL.
let path = /(http[s]?:\/\/)?([^\/\s]+\/)(.*)/gi;
const botId = url.replace(path, ($0, $1, $2, $3) => {
return $3.substr($3.indexOf('/'));
});
// Finds the associated question.
const instance = await core.loadInstanceByBotId(botId);
const question = await GuaribasQuestion.findAll({
where: {
instanceId: instance.instanceId,
questionId: id
}
});
return question;
}
public async getQuestionsSEO(instanceId: number) {
const questions = await GuaribasQuestion.findAll({
where: {
instanceId: instanceId
}
});
let output = [];
for (let i = 0; i < questions.length; i++) {
const answer = questions[i];
const text = getSlug(answer.content);
let url = `${text}-${i}`;
output.push(url);
}
return output;
}
public async getAnswerByText(instanceId: number, text: string): Promise<any> {
text = text.trim();
const service = new CSService();
@ -519,6 +567,7 @@ export class KBService implements IGBKBService {
}
});
}
public async importKbTabularDirectory(localPath: string, instance: IGBInstance, packageId: number): Promise<any> {
const files = await walkPromise(localPath);

View file

@ -62,8 +62,8 @@ export class RootData {
public appPackages: any[]; // Loaded .gbapp package list
public minService: GBMinService; // Minimalist service core
public bootInstance: IGBInstance; // General Bot Interface Instance
public minInstances: any[]; //
public minBoot: GBMinInstance;
public minInstances: any[]; // List of bot instances.
public minBoot: GBMinInstance; // Reference to boot bot.
public wwwroot: string; // .gbui or a static webapp.
public entryPointDialog: string; // To replace default welcome dialog.
}