new(all): TRUE multicloud.

This commit is contained in:
Rodrigo Rodriguez 2024-08-19 16:12:23 -03:00
parent 3299683268
commit 5880355349
14 changed files with 518 additions and 1358 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
{
"swagger": "2.0",
"openapi": "3.0.0",
"info": {
"version": "v3",
"title": "Bot Connector - Direct Line API - v3.0",
@ -15,7 +15,7 @@
"url": "https://opensource.org/licenses/MIT"
}
},
"host": "directline.botframework.com",
"servers": [{"url": "http://127.0.0.1:4242"}],
"schemes": [
"https"
],

View file

@ -76,7 +76,7 @@
"@azure/keyvault-keys": "4.8.0",
"@azure/ms-rest-js": "2.7.0",
"@azure/msal-node": "2.8.1",
"@azure/openai": "^2.0.0-beta.1",
"@azure/openai": "2.0.0-beta.1",
"@azure/search-documents": "12.0.0",
"@azure/storage-blob": "12.18.0",
"@google-cloud/pubsub": "4.4.0",
@ -137,11 +137,12 @@
"google-libphonenumber": "3.2.34",
"googleapis": "126.0.1",
"hnswlib-node": "3.0.0",
"html-to-md": "^0.8.5",
"html-to-md": "0.8.5",
"http-proxy": "1.18.1",
"ibm-watson": "9.1.0",
"instagram-private-api": "^1.46.1",
"instagram-private-api": "1.46.1",
"iso-639-1": "3.1.2",
"isomorphic-fetch": "3.0.0",
"join-images-updated": "1.1.11",
"js-md5": "0.8.3",
"json-schema-to-zod": "2.1.0",
@ -210,7 +211,7 @@
"textract": "2.5.0",
"twilio": "5.1.0",
"twitter-api-v2": "1.17.0",
"typeorm": "^0.3.20",
"typeorm": "0.3.20",
"typescript": "5.4.5",
"url-join": "5.0.0",
"vhost": "3.0.2",
@ -218,6 +219,7 @@
"vm2-process": "2.1.5",
"walk-promise": "0.2.0",
"washyourmouthoutwithsoap": "1.0.2",
"webdav-server": "2.6.2",
"whatsapp-cloud-api": "0.3.1",
"whatsapp-web.js": "https://github.com/Julzk/whatsapp-web.js/tarball/jkr_hotfix_7",
"winston": "3.13.0",

View file

@ -37,6 +37,7 @@ import SwaggerClient from 'swagger-client';
import { spawn } from 'child_process';
import { CodeServices } from '../../gpt.gblib/services/CodeServices.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* Web Automation services of conversation to be called by BASIC.
@ -154,12 +155,8 @@ export class DebuggerService {
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
GBServer.globals.debuggers[botId].client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;

View file

@ -1348,12 +1348,7 @@ export class DialogKeywords {
const conversation = min['apiConversations'][pid];
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
conversation.client = client;
const response = await client.apis.Conversations.Conversations_StartConversation();
conversation.conversationId = response.obj.conversationId;

View file

@ -106,7 +106,7 @@ export class GBConfigService {
value = undefined;
break;
case 'STORAGE_FILE':
value = './guaribas.sqlite';
value = './data.db';
break;
case 'GBKB_AUTO_DEPLOY':
value = false;

View file

@ -42,6 +42,7 @@ import { FacebookAdapter } from 'botbuilder-adapter-facebook';
import mkdirp from 'mkdirp';
import Fs from 'fs';
import arrayBufferToBuffer from 'arraybuffer-to-buffer';
import { v2 as webdav } from 'webdav-server';
import { NlpManager } from 'node-nlp';
import Koa from 'koa';
import { createRpcServer } from '@push-rpc/core';
@ -253,6 +254,8 @@ export class GBMinService {
GBServer.globals.minInstances.push(min);
const user = null; // No user context.
// Serves individual URL for each bot conversational interface.
await this.deployer['deployPackage2'](min, user, 'packages/default.gbtheme');
// Install per bot deployed packages.
@ -313,6 +316,14 @@ export class GBMinService {
if (!Fs.existsSync(dir)) {
mkdirp.sync(dir);
}
dir = Path.join(process.env.PWD, 'work', gbai);
const server = new webdav.WebDAVServer();
server.setFileSystem(`/${botId}`,
new webdav.PhysicalFileSystem(dir), (success) => {
server.start(() => console.log('WEBDAV READY'));
})
// Loads Named Entity data for this bot.
@ -321,16 +332,11 @@ export class GBMinService {
// Calls the loadBot context.activity for all packages.
await this.invokeLoadBot(min.appPackages, GBServer.globals.sysPackages, min);
// Serves individual URL for each bot conversational interface.
const receiver = async (req, res) => {
await this.receiver(req, res, conversationState, min, instance, GBServer.globals.appPackages);
};
let url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, receiver);
url = `/api/messages`;
GBServer.globals.server.post(url, receiver);
GBServer.globals.server.get(url, (req, res) => {
if (req.query['hub.mode'] === 'subscribe') {
if (req.query['hub.verify_token'] === process.env.FACEBOOK_VERIFY_TOKEN) {
@ -349,12 +355,7 @@ export class GBMinService {
if (process.env.TEST_MESSAGE) {
GBLogEx.info(min, `Starting auto test with '${process.env.TEST_MESSAGE}'.`);
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
const response = await client.apis.Conversations.Conversations_StartConversation();
const conversationId = response.obj.conversationId;
@ -450,7 +451,9 @@ export class GBMinService {
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
}
await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
if (whatsAppDirectLine) {
await whatsAppDirectLine.WhatsAppCallback(req, res, whatsAppDirectLine.botId);
}
})
.bind(min);
@ -672,27 +675,31 @@ export class GBMinService {
theme = `default.gbtheme`;
}
res.send(
JSON.stringify({
instanceId: instance.instanceId,
botId: botId,
theme: theme,
//webchatToken: webchatTokenContainer.token,
domain: 'http://localhost:3978/directline',
speechToken: speechToken,
conversationId: webchatTokenContainer.conversationId,
authenticatorTenant: instance.authenticatorTenant,
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),
logo: this.core.getParam(instance, 'Logo', null),
color1: this.core.getParam(instance, 'Color1', null),
color2: this.core.getParam(instance, 'Color2', null)
})
);
let config = {
instanceId: instance.instanceId,
botId: botId,
theme: theme,
speechToken: speechToken,
conversationId: webchatTokenContainer.conversationId,
authenticatorTenant: instance.authenticatorTenant,
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),
logo: this.core.getParam(instance, 'Logo', null),
color1: this.core.getParam(instance, 'Color1', null),
color2: this.core.getParam(instance, 'Color2', null)
};
if (process.env.STORAGE_FILE) {
config['domain'] = `http://localhost:${process.env.PORT}/directline`;
} else {
config['webchatToken'] = webchatTokenContainer.token;
}
res.send(JSON.stringify(config));
} else {
const error = `Instance not found while retrieving from .gbui web client: ${botId}.`;
res.sendStatus(error);
@ -749,15 +756,18 @@ export class GBMinService {
*/
private async buildBotAdapter(instance: any, sysPackages: IGBPackage[], appPackages: IGBPackage[]) {
// MSFT stuff.
const uri = 'http://localhost:3978';
const adapter = new BotFrameworkAdapter({
clientOptions: { baseUri: uri },
let config = {
appId: instance.marketplaceId ? instance.marketplaceId : GBConfigService.get('MARKETPLACE_ID'),
appPassword: instance.marketplacePassword
? instance.marketplacePassword
: GBConfigService.get('MARKETPLACE_SECRET')
});
};
if (process.env.STORAGE_FILE) {
config['clientOptions'] = { baseUri: `http://localhost:${process.env.PORT}` };
}
const adapter = new BotFrameworkAdapter(config);
const storage = new MemoryStorage();
const conversationState = new ConversationState(storage);
const userState = new UserState(storage);
@ -770,6 +780,28 @@ export class GBMinService {
// The minimal bot is built here.
const min = new GBMinInstance();
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
const dialogState = conversationState.createProperty('dialogState');
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
min.dialogs.add(new AttachmentPrompt('attachmentPrompt'));
min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
if (process.env.ENABLE_AUTH) {
min.dialogs.add(
new OAuthPrompt('oAuthPrompt', {
connectionName: 'OAuth2',
text: 'Please sign in to General Bots.',
title: 'Sign in',
timeout: 300000
})
);
}
min.botId = instance.botId;
min.bot = adapter;
min.userState = userState;
@ -792,6 +824,15 @@ export class GBMinService {
min['apiConversations'] = {};
min.packages = sysPackages;
const receiver = async (req, res) => {
await this.receiver(req, res, conversationState, min, instance, GBServer.globals.appPackages);
};
let url = `/api/messages/${instance.botId}`;
GBServer.globals.server.post(url, receiver);
url = `/api/messages`;
GBServer.globals.server.post(url, receiver);
// NLP Manager.
const manager = new NlpManager({ languages: ['pt'], forceNER: true });
@ -879,26 +920,6 @@ export class GBMinService {
WhatsappDirectLine.botsByNumber[botNumber] = min.whatsAppDirectLine;
}
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
const dialogState = conversationState.createProperty('dialogState');
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
min.dialogs.add(new AttachmentPrompt('attachmentPrompt'));
min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
if (process.env.ENABLE_AUTH) {
min.dialogs.add(
new OAuthPrompt('oAuthPrompt', {
connectionName: 'OAuth2',
text: 'Please sign in to General Bots.',
title: 'Sign in',
timeout: 300000
})
);
}
return { min, adapter, conversationState };
}
@ -1177,7 +1198,7 @@ export class GBMinService {
await handler(context);
// Return status
res.status(200);
res.end();
} else {
await adapter['processActivity'](req, res, handler);
@ -1376,7 +1397,7 @@ export class GBMinService {
context.activity.text = context.activity.text.trim();
const member = context.activity.from;
let memberId, email;
let memberId = null, email = null;
// Processes e-mail from id in case of Teams messages.
@ -1648,12 +1669,7 @@ export class GBMinService {
if (script === 'start') {
pid = GBVMService.createProcessInfo(user, min, 'api', null);
const client = await new SwaggerClient({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
});
const client = await GBUtil.getDirectLineClient(min);
const response = await client.apis.Conversations.Conversations_StartConversation();
min['apiConversations'][pid] = { conversation: response.obj, client: client };

View file

@ -0,0 +1,322 @@
import bodyParser from 'body-parser';
import express from 'express';
import fetch from 'isomorphic-fetch';
import moment from 'moment';
import * as uuidv4 from 'uuid';
import { IActivity, IBotData, IConversation, IConversationUpdateActivity, IMessageActivity } from './types';
const expiresIn = 1800;
const conversationsCleanupInterval = 10000;
const conversations: { [key: string]: IConversation } = {};
const botDataStore: { [key: string]: IBotData } = {};
export const getRouter = (serviceUrl: string, botUrl: string, conversationInitRequired = true): express.Router => {
const router = express.Router();
router.use(bodyParser.json()); // for parsing application/json
router.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
router.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, PATCH, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, x-ms-bot-agent');
next();
});
// CLIENT ENDPOINT
router.options('/directline', (req, res) => {
res.status(200).end();
});
// Creates a conversation
const reqs = (req, res) => {
const conversationId: string = uuidv4.v4().toString();
conversations[conversationId] = {
conversationId,
history: [],
};
console.log('Created conversation with conversationId: ' + conversationId);
const activity = createConversationUpdateActivity(serviceUrl, conversationId);
fetch(botUrl, {
method: 'POST',
body: JSON.stringify(activity),
headers: {
'Content-Type': 'application/json',
},
}).then((response) => {
res.status(response.status).send({
conversationId,
expiresIn,
});
});
};
router.post('/v3/directline/conversations',reqs );
router.post('/directline/conversations',reqs );
// Reconnect API
router.get('/v3/directline/conversations/:conversationId', (req, res) => {
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
res.status(200).send(conversation);
} else {
// Conversation was never initialized
res.status(400).send();
}
console.warn('/v3/directline/conversations/:conversationId not implemented');
});
// Gets activities from store (local history array for now)
router.get('/directline/conversations/:conversationId/activities', (req, res) => {
const watermark = req.query.watermark && req.query.watermark !== 'null' ? Number(req.query.watermark) : 0;
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
// If the bot has pushed anything into the history array
if (conversation.history.length > watermark) {
const activities = conversation.history.slice(watermark);
res.status(200).json({
activities,
watermark: watermark + activities.length,
});
} else {
res.status(200).send({
activities: [],
watermark,
});
}
} else {
// Conversation was never initialized
res.status(400).send();
}
});
// Sends message to bot. Assumes message activities
router.post('/directline/conversations/:conversationId/activities', (req, res) => {
const incomingActivity = req.body;
// Make copy of activity. Add required fields
const activity = createMessageActivity(incomingActivity, serviceUrl, req.params.conversationId);
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
fetch(botUrl, {
method: 'POST',
body: JSON.stringify(activity),
headers: {
'Content-Type': 'application/json',
},
}).then((response) => {
res.status(response.status).json({ id: activity.id });
});
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.post('/v3/directline/conversations/:conversationId/upload', (req, res) => { console.warn('/v3/directline/conversations/:conversationId/upload not implemented'); });
router.get('/v3/directline/conversations/:conversationId/stream', (req, res) => { console.warn('/v3/directline/conversations/:conversationId/stream not implemented'); });
// BOT CONVERSATION ENDPOINT
router.post('/v3/conversations', (req, res) => { console.warn('/v3/conversations not implemented'); });
router.post('/v3/conversations/:conversationId/activities', (req, res) => {
let activity: IActivity;
activity = req.body;
activity.id = uuidv4.v4();
activity.from = { id: 'id', name: 'Bot' };
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
res.status(200).send();
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.post('/v3/conversations/:conversationId/activities/:activityId', (req, res) => {
let activity: IActivity;
activity = req.body;
activity.id = uuidv4.v4();
activity.from = { id: 'id', name: 'Bot' };
const conversation = getConversation(req.params.conversationId, conversationInitRequired);
if (conversation) {
conversation.history.push(activity);
res.status(200).send();
} else {
// Conversation was never initialized
res.status(400).send();
}
});
router.get('/v3/conversations/:conversationId/members', (req, res) => { console.warn('/v3/conversations/:conversationId/members not implemented'); });
router.get('/v3/conversations/:conversationId/activities/:activityId/members', (req, res) => { console.warn('/v3/conversations/:conversationId/activities/:activityId/members'); });
// BOTSTATE ENDPOINT
router.get('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called GET user data');
getBotData(req, res);
});
router.get('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
console.log(('Called GET conversation data'));
getBotData(req, res);
});
router.get('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
console.log('Called GET private conversation data');
getBotData(req, res);
});
router.post('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called POST setUserData');
setUserData(req, res);
});
router.post('/v3/botstate/:channelId/conversations/:conversationId', (req, res) => {
console.log('Called POST setConversationData');
setConversationData(req, res);
});
router.post('/v3/botstate/:channelId/conversations/:conversationId/users/:userId', (req, res) => {
setPrivateConversationData(req, res);
});
router.delete('/v3/botstate/:channelId/users/:userId', (req, res) => {
console.log('Called DELETE deleteStateForUser');
deleteStateForUser(req, res);
});
return router;
};
/**
* @param app The express app where your offline-directline endpoint will live
* @param port The port where your offline-directline will be hosted
* @param botUrl The url of the bot (e.g. http://127.0.0.1:3978/api/messages)
* @param conversationInitRequired Requires that a conversation is initialized before it is accessed, returning a 400
* when not the case. If set to false, a new conversation reference is created on the fly. This is true by default.
*/
export const initializeRoutes = (app: express.Express, port: number, botUrl: string, conversationInitRequired = true) => {
conversationsCleanup();
const directLineEndpoint = `http://127.0.0.1:${port}`;
const router = getRouter(directLineEndpoint, botUrl, conversationInitRequired);
app.use(router);
console.log(`Routing messages to bot on ${botUrl}`);
};
const getConversation = (conversationId: string, conversationInitRequired: boolean) => {
// Create conversation on the fly when needed and init not required
if (!conversations[conversationId] && !conversationInitRequired) {
conversations[conversationId] = {
conversationId,
history: [],
};
}
return conversations[conversationId];
};
const getBotDataKey = (channelId: string, conversationId: string, userId: string) => {
return `$${channelId || '*'}!${conversationId || '*'}!${userId || '*'}`;
};
const setBotData = (channelId: string, conversationId: string, userId: string, incomingData: IBotData): IBotData => {
const key = getBotDataKey(channelId, conversationId, userId);
const newData: IBotData = {
eTag: new Date().getTime().toString(),
data: incomingData.data,
};
if (incomingData) {
botDataStore[key] = newData;
} else {
delete botDataStore[key];
newData.eTag = '*';
}
return newData;
};
const getBotData = (req: express.Request, res: express.Response) => {
const key = getBotDataKey(req.params.channelId, req.params.conversationId, req.params.userId);
console.log('Data key: ' + key);
res.status(200).send(botDataStore[key] || { data: null, eTag: '*' });
};
const setUserData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
const setConversationData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
const setPrivateConversationData = (req: express.Request, res: express.Response) => {
res.status(200).send(setBotData(req.params.channelId, req.params.conversationId, req.params.userId, req.body));
};
export const start = (server)=>{
initializeRoutes(server, Number(process.env.PORT), `http://127.0.0.1:${process.env.PORT}/api/messages`);
}
const deleteStateForUser = (req: express.Request, res: express.Response) => {
Object.keys(botDataStore)
.forEach((key) => {
if (key.endsWith(`!{req.query.userId}`)) {
delete botDataStore[key];
}
});
res.status(200).send();
};
// CLIENT ENDPOINT HELPERS
const createMessageActivity = (incomingActivity: IMessageActivity, serviceUrl: string, conversationId: string): IMessageActivity => {
return { ...incomingActivity, channelId: 'emulator', serviceUrl, conversation: { id: conversationId }, id: uuidv4.v4() };
};
const createConversationUpdateActivity = (serviceUrl: string, conversationId: string): IConversationUpdateActivity => {
const activity: IConversationUpdateActivity = {
type: 'conversationUpdate',
channelId: 'emulator',
serviceUrl,
conversation: { id: conversationId },
id: uuidv4.v4(),
membersAdded: [],
membersRemoved: [],
from: { id: 'offline-directline', name: 'Offline Directline Server' },
};
return activity;
};
const conversationsCleanup = () => {
setInterval(() => {
const expiresTime = moment().subtract(expiresIn, 'seconds');
Object.keys(conversations).forEach((conversationId) => {
if (conversations[conversationId].history.length > 0) {
const lastTime = moment(conversations[conversationId].history[conversations[conversationId].history.length - 1].localTimestamp);
if (lastTime < expiresTime) {
delete conversations[conversationId];
console.log('deleted cId: ' + conversationId);
}
}
});
}, conversationsCleanupInterval);
};

View file

@ -0,0 +1,66 @@
export interface IUser {
id: string,
name: string
}
export interface IChannelAccount {
id?: string,
name?: string,
}
export interface IConversationAccount extends IChannelAccount {
isGroup?: boolean,
}
export interface IAttachment {
contentType?: string,
contentUrl?: string,
content?: any,
name?: string,
thumbnailUrl?: string,
}
export interface IEntity {
type?: string,
}
export interface IActivity {
type?: string,
id?: string,
serviceUrl?: string,
timestamp?: string,
localTimestamp?: string,
channelId?: string,
from?: IChannelAccount,
conversation?: IConversationAccount,
recipient?: IChannelAccount,
replyToId?: string,
channelData?: any,
}
export interface IMessageActivity extends IActivity {
locale?: string,
text?: string,
summary?: string,
textFormat?: string,
attachmentLayout?: string,
attachments?: IAttachment[],
entities?: IEntity[],
}
export interface IBotData {
eTag: string;
data: any;
}
export interface IConversation {
conversationId: string,
history?: IActivity[]
}
export interface IConversationUpdateActivity extends IActivity {
membersAdded?: IChannelAccount[],
membersRemoved?: IChannelAccount[],
topicName?: string,
historyDisclosed?: boolean,
}

View file

@ -36,6 +36,7 @@ import { GBLog, GBMinInstance, GBService } from 'botlib';
import { GBServer } from '../../../src/app.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* Support for Google Chat.
@ -90,10 +91,7 @@ export class GoogleChatDirectLine extends GBService {
}
public async setup (setUrl) {
this.directLineClient = new Swagger({
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
usePromise: true
});
this.directLineClient = await GBUtil.getDirectLineClient(this.min);
const client = await this.directLineClient;
client.clientAuthorizations.add(

View file

@ -54,8 +54,10 @@ export class SecService extends GBService {
user.email = email;
user.defaultChannel = channelName;
GBServer.globals.users [user.userId] = user;
return await user.save();
if(user.changed()){
await user.save();
}
return user;
}
/**

View file

@ -30,7 +30,6 @@
import mime from 'mime-types';
import urlJoin from 'url-join';
import SwaggerClient from 'swagger-client';
import Path from 'path';
import Fs from 'fs';
import { GBLog, GBMinInstance, GBService, IGBPackage } from 'botlib';
@ -46,7 +45,7 @@ import qrcode from 'qrcode-terminal';
import express from 'express';
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import pkg from 'whatsapp-web.js';
import fetch, { Response } from 'node-fetch';
import fetch from 'node-fetch';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { ChatServices } from '../../gpt.gblib/services/ChatServices.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
@ -117,20 +116,9 @@ export class WhatsappDirectLine extends GBService {
}
public async setup(setUrl: boolean) {
const client = await new SwaggerClient({
url: 'http://127.0.0.1:3978/api/messages', // TODO:
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${this.min.instance.webchatKey}`;
}
});
this.directLineClient = client;
this.directLineClient = GBUtil.getDirectLineClient(this.min);
// Warms up MSBF.
await client.apis.Conversations.Conversations_StartConversation();
let url: string;
let options: any;

View file

@ -40,6 +40,8 @@ import bodyParser from 'body-parser';
import { GBLog, GBMinInstance, IGBCoreService, IGBInstance } from 'botlib';
import child_process from 'child_process';
import express from 'express';
import {start as startRouter} from '../packages/core.gbapp/services/router/bridge.js'
import fs from 'fs';
import http from 'http';
import httpProxy from 'http-proxy';
@ -87,8 +89,9 @@ export class GBServer {
const server = express();
this.initEndpointsDocs(server);
startRouter(server);
GBServer.globals.server = server;
GBServer.globals.httpsServer = null;
GBServer.globals.webSessions = {};
GBServer.globals.processes = {};
@ -118,7 +121,7 @@ export class GBServer {
});
process.on('uncaughtException', (err, p) => {
GBLogEx.error(0, `GBEXCEPTION: ${GBUtil.toYAML(err)} ${GBUtil.toYAML(p)}`);
GBLogEx.error(0, `GBEXCEPTION: ${GBUtil.toYAML(JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err))))}`);
});
process.on('unhandledRejection', (err, p) => {
@ -131,7 +134,7 @@ export class GBServer {
}
if (!bypass) {
GBLogEx.error(0, `GBREJECTION: ${GBUtil.toYAML(err)} ${GBUtil.toYAML(p)}`);
GBLogEx.error(0,`GBREJECTION: ${GBUtil.toYAML(JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err))))}`);
}
});
@ -147,6 +150,7 @@ export class GBServer {
(async () => {
try {
GBLogEx.info(0, `Now accepting connections on ${port}...`);
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
// Reads basic configuration, initialize minimal services.
@ -247,7 +251,7 @@ export class GBServer {
const minService: GBMinService = new GBMinService(core, conversationalService, adminService, deployer);
GBServer.globals.minService = minService;
await minService.buildMin(instances);
server.all('*', async (req, res, next) => {
const host = req.headers.host;

View file

@ -34,6 +34,8 @@
'use strict';
import * as YAML from 'yaml';
import SwaggerClient from 'swagger-client';
import Fs from 'fs';
export class GBUtil {
public static repeat(chr, count) {
@ -53,7 +55,7 @@ export class GBUtil {
return (GBUtil.repeat(pad, length) + value).substr(0, width);
}
public static padR(value, width, pad) {
if (!width || width < 1) return value;
@ -64,6 +66,22 @@ export class GBUtil {
return (value + GBUtil.repeat(pad, length)).substr(0, width);
}
public static async getDirectLineClient(min) {
let config = {
url: `http://127.0.0.1:${process.env.port}/api/messages`,
spec: JSON.parse(Fs.readFileSync('directline-3.0.json', 'utf8')),
requestInterceptor: req => {
req.headers['Authorization'] = `Bearer ${min.instance.webchatKey}`;
}
};
if (process.env.STORAGE_FILE) {
config['spec'].servers = [{ url: `http://127.0.0.1:${process.env.PORT}/api/messages` }];
config['openapi'] = '3.0.0';
}
return await new SwaggerClient(config);
}
public static toYAML(json) {
const doc = new YAML.Document();
doc.contents = json;