new(core.gbapp): Broadcast for msteams available.

This commit is contained in:
Rodrigo Rodriguez 2020-10-18 13:24:19 -03:00
parent a7712fba28
commit 01e959fa61
8 changed files with 223 additions and 49 deletions

View file

@ -223,10 +223,6 @@ export class GBAdminService implements IGBAdminService {
await deployer.undeployPackageFromLocalPath(min.instance, urlJoin(GBDeployer.workFolder, packageName));
}
public static async broadcastCommand(text: any, min: GBMinInstance) {
const packageName = text.split(' ')[1];
}
public static isSharePointPath(path: string) {
return path.indexOf('sharepoint.com') > 0;
}

View file

@ -0,0 +1,71 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' 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. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib';
import { Messages } from '../strings';
import { SecService } from '../../security.gbapp/services/SecService';
import { GBServer } from '../../../src/app';
import { GBConversationalService } from '../services/GBConversationalService';
/**
* Dialog for the bot explains about itself.
*/
export class BroadcastDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/gb-broadcast', [
async step => {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, 'Type the message and the broadcast will start.');
},
async step => {
await min.conversationalService['broadcast'](min, step.result);
return await step.next();
}
])
);
}
}

View file

@ -38,6 +38,7 @@
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib';
import { Sequelize } from 'sequelize-typescript';
import { BroadcastDialog } from './dialogs/BroadcastDialog';
import { SwitchBotDialog } from './dialogs/SwitchBot';
import { WelcomeDialog } from './dialogs/WelcomeDialog';
import { WhoAmIDialog } from './dialogs/WhoAmIDialog';
@ -76,5 +77,6 @@ export class GBCorePackage implements IGBPackage {
WhoAmIDialog.setup(min.bot, min);
SwitchBotDialog.setup(min.bot, min);
DialogClass.setup(min.bot, min);
BroadcastDialog.setup(min.bot, min);
}
}

View file

@ -44,6 +44,9 @@ import { Readable } from 'stream';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService';
import { SecService } from '../../security.gbapp/services/SecService';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { WaterfallStep, WaterfallStepContext } from 'botbuilder-dialogs';
import { MicrosoftAppCredentials } from 'botframework-connector';
const urlJoin = require('url-join');
const PasswordGenerator = require('strict-password-generator').default;
const Nexmo = require('nexmo');
@ -625,4 +628,31 @@ export class GBConversationalService {
}
}
}
public async broadcast(min: GBMinInstance, message: string) {
GBLog.info(`Sending broadcast notifications...`);
let sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const service = new SecService();
const users = await service.getAllUsers(min.instance.instanceId);
await CollectionUtil.asyncForEach(users, async user => {
if (user.conversationReference) {
const ref = JSON.parse(user.conversationReference);
MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl);
await min.bot['createConversation'](ref, async t1 => {
const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async t2 => {
await t2.sendActivity(message);
});
});
} else {
GBLog.info(`User: ${user.systemUserId} with no conversation ID while broadcasting.`);
}
});
}
}

View file

@ -43,9 +43,16 @@ const request = require('request-promise-native');
const removeRoute = require('express-remove-route');
const AuthenticationContext = require('adal-node').AuthenticationContext;
const wash = require('washyourmouthoutwithsoap');
import { AutoSaveStateMiddleware, BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } from 'botbuilder';
import {
AutoSaveStateMiddleware,
BotFrameworkAdapter,
ConversationState,
MemoryStorage,
TurnContext,
UserState
} from 'botbuilder';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { ConfirmPrompt, WaterfallDialog } from 'botbuilder-dialogs';
import { ConfirmPrompt, OAuthPrompt, WaterfallDialog } from 'botbuilder-dialogs';
import {
GBDialogStep,
GBLog,
@ -227,7 +234,7 @@ export class GBMinService {
GBServer.globals.minInstances.push(min);
await this.deployer.deployPackage(min, 'packages/default.gbtheme');
// Install per bot deployed packages.
let packagePath = `work/${min.botId}.gbai/${min.botId}.gbdialog`;
@ -527,6 +534,14 @@ export class GBMinService {
min.dialogs = new DialogSet(dialogState);
min.dialogs.add(new TextPrompt('textPrompt'));
min.dialogs.add(new ConfirmPrompt('confirmPrompt'));
min.dialogs.add(
new OAuthPrompt('oAuthPrompt', {
connectionName: 'OAuth2',
text: 'Please sign in.',
title: 'Sign in',
timeout: 300000
})
);
return { min, adapter, conversationState };
}
@ -582,6 +597,7 @@ export class GBMinService {
// First time processing.
const sec = new SecService();
if (!user.loaded) {
await min.conversationalService.sendEvent(min, step, 'loadInstance', {
instanceId: instance.instanceId,
@ -594,7 +610,6 @@ export class GBMinService {
user.cb = undefined;
if (context.activity.from.id !== min.botId) {
let sec = new SecService();
const member = context.activity.from;
const persistedUser = await sec.ensureUser(
@ -605,7 +620,10 @@ export class GBMinService {
'web',
member.name
);
if (step.context.activity.channelId === "msteams"){
persistedUser.conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
await persistedUser.save();
}
const analytics = new AnalyticsService();
user.systemUser = persistedUser;

View file

@ -0,0 +1,69 @@
/*****************************************************************************\
| ( )_ _ |
| _ _ _ __ _ _ __ ___ ___ _ _ | ,_)(_) ___ ___ _ |
| ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| | | |/',__)/' 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. |
| |
\*****************************************************************************/
/**
* @fileoverview Dialog for handling OAuth scenarios.
*/
'use strict';
import { TokenResponse } from 'botbuilder';
import { IGBDialog, GBLog, GBMinInstance } from 'botlib';
import { Messages } from '../strings';
/**
* Dialogs for handling Menu control.
*/
export class OAuthDialog extends IGBDialog {
public static getOAuthDialog(min: GBMinInstance) {
return {
id: '/auth',
waterfall: [
async step => {
step.activeDialog.state.options = step.options;
return await step.beginDialog('oAuthPrompt');
},
async step => {
const tokenResponse: TokenResponse = step.result;
if (tokenResponse) {
GBLog.info('Token acquired.');
return await step.endDialog(tokenResponse);
} else {
await step.context.sendActivity('Please sign in so I can show you your profile.');
return await step.replaceDialog('/auth');
}
}
]
};
}
}

View file

@ -42,6 +42,7 @@ import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from '
import {ProfileDialog} from './dialogs/ProfileDialog'
import { Sequelize } from 'sequelize-typescript';
import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from './models';
import { OAuthDialog } from './dialogs/OAuthDialog';
/**
* Package for the security module.
@ -54,8 +55,9 @@ export class GBSecurityPackage implements IGBPackage {
ProfileDialog.getEmailDialog(min),
ProfileDialog.getMobileDialog(min),
ProfileDialog.getMobileConfirmDialog(min),
OAuthDialog.getOAuthDialog(min),
];
GBLog.verbose(`getDialogs called.`);
}
public async unloadPackage(core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
@ -72,8 +74,7 @@ export class GBSecurityPackage implements IGBPackage {
public async onExchangeData(min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`);
}
public async loadPackage(core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasGroup, GuaribasUser, GuaribasUserGroup]);
}

View file

@ -6,12 +6,10 @@ import { GuaribasGroup, GuaribasUser, GuaribasUserGroup } from '../models';
import { ConversationReference } from 'botbuilder';
import { CollectionUtil } from 'pragmatismo-io-framework';
/**
* Security service layer.
*/
export class SecService extends GBService {
public async importSecurityFile(localPath: string, instance: IGBInstance) {
const security = JSON.parse(Fs.readFileSync(urlJoin(localPath, 'security.json'), 'utf8'));
await CollectionUtil.asyncForEach(security.groups, async group => {
@ -31,7 +29,6 @@ export class SecService extends GBService {
userGroup.groupId = g1.groupId;
userGroup.userId = user2.userId;
await userGroup.save();
});
});
}
@ -45,7 +42,6 @@ export class SecService extends GBService {
displayName: string
): Promise<GuaribasUser> {
let user = await GuaribasUser.findOne({
where: {
userSystemId: userSystemId
}
@ -85,13 +81,7 @@ export class SecService extends GBService {
await user.save();
}
public async updateUserInstance(
userSystemId: string,
instanceId: number
): Promise<GuaribasUser> {
public async updateUserInstance(userSystemId: string, instanceId: number): Promise<GuaribasUser> {
let user = await GuaribasUser.findOne({
where: {
userSystemId: userSystemId
@ -114,7 +104,6 @@ export class SecService extends GBService {
});
if (agentSystemId === null) {
const agent = await GuaribasUser.findOne({
where: {
userSystemId: user.agentSystemId
@ -122,16 +111,15 @@ export class SecService extends GBService {
});
if (agent !== null && agent !== undefined) {
agent.agentMode = "bot";
agent.agentMode = 'bot';
agent.agentSystemId = null;
await agent.save();
}
user.agentMode = "bot";
user.agentMode = 'bot';
user.agentSystemId = null;
} else {
user.agentMode = "human";
user.agentMode = 'human';
user.agentSystemId = agentSystemId;
const agent = await GuaribasUser.findOne({
where: {
@ -140,7 +128,7 @@ export class SecService extends GBService {
});
agent.instanceId = user.instanceId;
agent.agentMode = "self";
agent.agentMode = 'self';
agent.agentSystemId = null;
await agent.save();
}
@ -160,35 +148,30 @@ export class SecService extends GBService {
throw `TRANSFER_TO phones must talk first to the bot before becoming an agent.`;
}
return (user.agentMode === "self");
return user.agentMode === 'self';
}
public async assignHumanAgent(
userSystemId: string,
instanceId: number
): Promise<string> {
public async assignHumanAgent(userSystemId: string, instanceId: number): Promise<string> {
let agentSystemId;
const list = process.env.TRANSFER_TO.split(';');
await CollectionUtil.asyncForEach(list, async item => {
if (!await this.isAgentSystemId(item) && item !== undefined &&
agentSystemId === undefined && item !== userSystemId) { // TODO: Optimize loop.
if (
!(await this.isAgentSystemId(item)) &&
item !== undefined &&
agentSystemId === undefined &&
item !== userSystemId
) {
// TODO: Optimize loop.
agentSystemId = item;
}
});
await this.updateCurrentAgent(userSystemId, instanceId, agentSystemId);
return agentSystemId;
}
public async getUserFromSystemId(
systemId: string
): Promise<GuaribasUser> {
public async getUserFromSystemId(systemId: string): Promise<GuaribasUser> {
return await GuaribasUser.findOne({
where: {
userSystemId: systemId
@ -196,15 +179,19 @@ export class SecService extends GBService {
});
}
public async getUserFromAgentSystemId(
systemId: string
): Promise<GuaribasUser> {
public async getUserFromAgentSystemId(systemId: string): Promise<GuaribasUser> {
return await GuaribasUser.findOne({
where: {
agentSystemId: systemId,
agentSystemId: systemId
}
});
}
public async getAllUsers(instanceId: number): Promise<GuaribasUser[]> {
return await GuaribasUser.findAll({
where: {
instanceId: instanceId
}
});
}
}