diff --git a/DATABASE-CHANGES.md b/DATABASE-CHANGES.md
index 55108766..4af86281 100644
--- a/DATABASE-CHANGES.md
+++ b/DATABASE-CHANGES.md
@@ -80,3 +80,13 @@ GO
ALTER TABLE dbo.GuaribasInstance ADD
translatorEndpoint nvarchar(128) NULL
GO
+
+
+# 2.0.108
+
+ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [agentSystemId]
+GO
+
+ALTER TABLE dbo.GuaribasUser ADD
+ agentSystemId nvarchar(255) NULL,
+GO
diff --git a/packages/basic.gblib/services/SystemKeywords.ts b/packages/basic.gblib/services/SystemKeywords.ts
index d447c1a2..b19107f9 100644
--- a/packages/basic.gblib/services/SystemKeywords.ts
+++ b/packages/basic.gblib/services/SystemKeywords.ts
@@ -125,7 +125,7 @@ export class SystemKeywords {
let sec = new SecService();
let user = await sec.getUserFromSystemId(from);
if (!user) {
- user = await sec.ensureUser(this.min.instance.instanceId, from, from, null, 'whatsapp', 'from');
+ user = await sec.ensureUser(this.min.instance.instanceId, from, from, null, 'whatsapp', 'from', null);
}
await sec.updateUserHearOnDialog(user.userId, dialogName);
}
diff --git a/packages/core.gbapp/services/GBConversationalService.ts b/packages/core.gbapp/services/GBConversationalService.ts
index 2dafc71d..e0dce88f 100644
--- a/packages/core.gbapp/services/GBConversationalService.ts
+++ b/packages/core.gbapp/services/GBConversationalService.ts
@@ -47,6 +47,7 @@ import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsServic
import { MicrosoftAppCredentials } from 'botframework-connector';
import { GBConfigService } from './GBConfigService';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
+import { GuaribasUser } from '../../security.gbapp/models';
const urlJoin = require('url-join');
const PasswordGenerator = require('strict-password-generator').default;
const Nexmo = require('nexmo');
@@ -851,19 +852,27 @@ export class GBConversationalService {
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);
- });
- });
+ await this.sendOnConversation(min, user, message);
} else {
GBLog.info(`User: ${user.systemUserId} with no conversation ID while broadcasting.`);
}
});
}
+
+ /**
+ *
+ * Sends a message in a user with an already started conversation (got ConversationReference set)
+ */
+ public async sendOnConversation(min: GBMinInstance, user: GuaribasUser, message: string) {
+ 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);
+ });
+ });
+ }
public static kmpSearch(pattern, text) {
pattern = pattern.toLowerCase();
diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts
index 6203cacf..44f4b48f 100644
--- a/packages/core.gbapp/services/GBMinService.ts
+++ b/packages/core.gbapp/services/GBMinService.ts
@@ -307,7 +307,7 @@ export class GBMinService {
const sec = new SecService();
let user = await sec.getUserFromSystemId(id);
if (user === null || user.hearOnDialog) {
- user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName);
+ user = await sec.ensureUser(activeMin.instance.instanceId, id, senderName, '', 'whatsapp', senderName, null);
const startDialog = user.hearOnDialog ?
user.hearOnDialog :
@@ -760,10 +760,10 @@ export class GBMinService {
});
const service = new KBService(min.core.sequelize);
const data = await service.getFaqBySubjectArray(instance.instanceId, 'faq', undefined);
- await min.conversationalService.sendEvent(min, step, 'play', {
- playerType: 'bullet',
- data: data.slice(0, 10)
- });
+ 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.
@@ -779,17 +779,10 @@ export class GBMinService {
member.name,
'',
'web',
- member.name
+ member.name,
+ null
);
- // Required for MSTEAMS handling of persisted conversations.
-
- if (step.context.activity.channelId === 'msteams') {
- persistedUser.conversationReference = JSON.stringify(
- TurnContext.getConversationReference(context.activity)
- );
- await persistedUser.save();
- }
// Stores conversation associated to the user to group each message.
@@ -804,6 +797,18 @@ export class GBMinService {
await min.userProfile.set(step.context, user);
}
+ user.systemUser = await sec.getUserFromSystemId( user.systemUser.userSystemId);
+ await min.userProfile.set(step.context, user);
+
+ // Required for MSTEAMS handling of persisted conversations.
+
+ if (step.context.activity.channelId === 'msteams') {
+ const conversationReference = JSON.stringify(
+ TurnContext.getConversationReference(context.activity)
+ );
+ await sec.updateConversationReferenceById(user.systemUser.userId, conversationReference);
+ }
+
GBLog.info(`User>: text:${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId}, value: ${context.activity.value})`);
// Answer to specific BOT Framework event conversationUpdate to auto start dialogs.
@@ -911,6 +916,8 @@ export class GBMinService {
*/
private async processMessageActivity(context, min: GBMinInstance, step: GBDialogStep) {
+ const sec = new SecService();
+
// Removes Bot Id from MS Teams.
context.activity.text = context.activity.text.trim();
@@ -1055,7 +1062,7 @@ export class GBMinService {
if (detectLanguage || !locale) {
locale = await min.conversationalService.getLanguage(min, text);
if (systemUser.locale != locale) {
- const sec = new SecService();
+
user.systemUser = await sec.updateUserLocale(systemUser.userId, locale);
await min.userProfile.set(step.context, user);
}
@@ -1092,28 +1099,39 @@ export class GBMinService {
context.activity.originalText = originalText;
GBLog.info(`Final text ready for NLP/Search/.gbapp: ${text}.`);
- // If there is a dialog in course, continue to the next step.
- if (step.activeDialog !== undefined) {
- await step.continueDialog();
+ if (user.systemUser.agentMode === 'self') {
+ const manualUser = await sec.getUserFromAgentSystemId(user.systemUser.userSystemId);
- } else {
+ GBLog.info(`HUMAN AGENT (${user.systemUser.userSystemId}) TO USER ${manualUser.userSystemId}: ${text}`);
+ await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId, `${manualUser.agentSystemId}: ${text}`, locale);
- let nextDialog = null;
- await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => {
- nextDialog = await e.onExchangeData(min, 'handleAnswer', {
- query: text,
- step: step,
- notTranslatedQuery: originalText,
- message: message ? message['dataValues'] : null,
- user: user ? user.dataValues : null
+ }
+ else {
+
+ // If there is a dialog in course, continue to the next step.
+
+ if (step.activeDialog !== undefined) {
+ await step.continueDialog();
+
+ } else {
+
+ let nextDialog = null;
+ await CollectionUtil.asyncForEach(min.appPackages, async (e: IGBPackage) => {
+ nextDialog = await e.onExchangeData(min, 'handleAnswer', {
+ query: text,
+ step: step,
+ notTranslatedQuery: originalText,
+ message: message ? message['dataValues'] : null,
+ user: user ? user.dataValues : null
+ });
});
- });
- await step.beginDialog(nextDialog ? nextDialog : '/answer', {
- query: text,
- user: user ? user.dataValues : null,
- message: message
- });
+ await step.beginDialog(nextDialog ? nextDialog : '/answer', {
+ query: text,
+ user: user ? user.dataValues : null,
+ message: message
+ });
+ }
}
}
}
diff --git a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts
index c39ad7e8..618484b8 100644
--- a/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts
+++ b/packages/customer-satisfaction.gbapp/dialogs/FeedbackDialog.ts
@@ -75,14 +75,25 @@ export class FeedbackDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sec = new SecService();
- const from = step.context.activity.from.id;
+ let from = step.context.activity.from.id;
await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering);
const agentSystemId = await sec.assignHumanAgent(from, min.instance.instanceId);
- await min.whatsAppDirectLine.sendToDevice(agentSystemId,
- Messages[locale].notify_agent(step.context.activity.from.name));
+ const user = await min.userProfile.get(step.context, {});
+ user.systemUser = await sec.getUserFromAgentSystemId(agentSystemId);
+ await min.userProfile.set(step.context, user);
+ if (agentSystemId.charAt(2) === ":") { // Agent is from Teams.
+ const agent = await sec.getUserFromSystemId(agentSystemId);
+ await min.conversationalService['sendOnConversation'](min, agent,
+ Messages[locale].notify_agent(step.context.activity.from.name));
+
+ }
+ else {
+ await min.whatsAppDirectLine.sendToDevice(agentSystemId, Messages[locale].notify_agent(step.context.activity.from.name));
+
+ }
return await step.next();
}
])
@@ -95,10 +106,65 @@ export class FeedbackDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sec = new SecService();
- const from = step.context.activity.from.id;
+ const userSystemId = step.context.activity.from.id;
+ const user = await min.userProfile.get(step.context, {});
+
+ if (user.systemUser.agentMode === 'self') {
+ const manualUser = await sec.getUserFromAgentSystemId(userSystemId);
+
+ await min.whatsAppDirectLine.sendToDeviceEx(manualUser.userSystemId,
+ Messages[locale].notify_end_transfer(min.instance.botId), locale);
+
+ if (userSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId));
+ }
+ else {
+ await min.whatsAppDirectLine.sendToDeviceEx(userSystemId,
+ Messages[locale].notify_end_transfer(min.instance.botId), locale);
+ }
+
+ await sec.updateHumanAgent(userSystemId, min.instance.instanceId, null);
+ await sec.updateHumanAgent(manualUser.userSystemId, min.instance.instanceId, null);
+
+ user.systemUser = await sec.getUserFromSystemId(userSystemId);
+ await min.userProfile.set(step.context, user);
+
+ }
+
+ else if (user.systemUser.agentMode === 'human') {
+ const agent = await sec.getUserFromSystemId(user.systemUser.agentSystemId);
+
+ await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId,
+ Messages[locale].notify_end_transfer(min.instance.botId), locale);
+
+
+ if (user.systemUser.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId));
+ }
+ else {
+ await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.agentSystemId,
+ Messages[locale].notify_end_transfer(min.instance.botId), locale);
+ }
+
+ await sec.updateHumanAgent(user.systemUser.userSystemId, min.instance.instanceId, null);
+ await sec.updateHumanAgent(agent.userSystemId, min.instance.instanceId, null);
+
+ user.systemUser = await sec.getUserFromSystemId(userSystemId);
+ await min.userProfile.set(step.context, user);
+
+ }
+ else
+ {
+ if (user.systemUser.userSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await min.conversationalService.sendText(min, step, 'Nenhum atendimento em andamento.');
+ }
+ else {
+ await min.whatsAppDirectLine.sendToDeviceEx(user.systemUser.userSystemId,
+ 'Nenhum atendimento em andamento.');
+ }
+
+ }
- await sec.updateCurrentAgent(from, min.instance.instanceId, null);
- await min.conversationalService.sendText(min, step, Messages[locale].notify_end_transfer(min.instance.botId));
return await step.next();
}
@@ -149,7 +215,7 @@ export class FeedbackDialog extends IGBDialog {
} else {
const message = min.core.getParam(min.instance, 'Feedback Improve Message',
- Messages[fixedLocale].we_will_improve); // TODO: Improve to be multi-language.
+ Messages[fixedLocale].we_will_improve); // TODO: Improve to be multi-language.
await min.conversationalService.sendText(min, step, message);
}
diff --git a/packages/customer-satisfaction.gbapp/strings.ts b/packages/customer-satisfaction.gbapp/strings.ts
index d006a3f2..634ee147 100644
--- a/packages/customer-satisfaction.gbapp/strings.ts
+++ b/packages/customer-satisfaction.gbapp/strings.ts
@@ -25,7 +25,8 @@ export const Messages = {
great_thanks: 'Ótimo, obrigado por contribuir com sua resposta.',
please_no_bad_words: 'Por favor, sem palavrões!',
please_wait_transfering: 'Por favor, aguarde enquanto eu localizo alguém para te atender.',
- notify_agent: (name) => `Existe um novo atendimento para *${name}*, por favor, responda aqui mesmo para a pessoa. Para finalizar, digite /qt.`
+ notify_agent: (name) => `Existe um novo atendimento para *${name}*, por favor, responda aqui mesmo para a pessoa. Para finalizar, digite /qt.`,
+ notify_end_transfer: (botName) => `Falando novamente com o bot ${botName}.`
}
};
diff --git a/packages/kb.gbapp/dialogs/AskDialog.ts b/packages/kb.gbapp/dialogs/AskDialog.ts
index 5edb4896..51c4548a 100644
--- a/packages/kb.gbapp/dialogs/AskDialog.ts
+++ b/packages/kb.gbapp/dialogs/AskDialog.ts
@@ -113,7 +113,7 @@ export class AskDialog extends IGBDialog {
let sec = new SecService();
const member = step.context.activity.from;
- const user = await sec.ensureUser(min.instance.instanceId, member.id, member.name, '', 'web', member.name);
+ const user = await sec.ensureUser(min.instance.instanceId, member.id, member.name, '', 'web', member.name, null);
let handled = false;
let nextDialog = null;
diff --git a/packages/security.gbapp/models/index.ts b/packages/security.gbapp/models/index.ts
index cc696e8e..d7bf875c 100644
--- a/packages/security.gbapp/models/index.ts
+++ b/packages/security.gbapp/models/index.ts
@@ -78,7 +78,7 @@ export class GuaribasUser extends Model {
@BelongsTo(() => GuaribasInstance)
public instance: GuaribasInstance;
- @Column(DataType.STRING(16))
+ @Column(DataType.STRING(255))
public agentSystemId: string;
@Column(DataType.DATE)
diff --git a/packages/security.gbapp/services/SecService.ts b/packages/security.gbapp/services/SecService.ts
index e63912a7..1cb0b84e 100644
--- a/packages/security.gbapp/services/SecService.ts
+++ b/packages/security.gbapp/services/SecService.ts
@@ -39,7 +39,8 @@ export class SecService extends GBService {
userName: string,
address: string,
channelName: string,
- displayName: string
+ displayName: string,
+ email: string
): Promise {
let user = await GuaribasUser.findOne({
where: {
@@ -55,7 +56,7 @@ export class SecService extends GBService {
user.userSystemId = userSystemId;
user.userName = userName;
user.displayName = displayName;
- user.email = userName;
+ user.email = email;
user.defaultChannel = channelName;
return await user.save();
@@ -82,6 +83,14 @@ export class SecService extends GBService {
await user.save();
}
+ public async updateConversationReferenceById(userId: number, conversationReference: string) {
+ const options = { where: { userId: userId } };
+ const user = await GuaribasUser.findOne(options);
+
+ user.conversationReference = conversationReference;
+ await user.save();
+ }
+
public async updateUserLocale(userId: number, locale: any): Promise {
const user = await GuaribasUser.findOne({
where: {
@@ -115,14 +124,18 @@ export class SecService extends GBService {
return await user.save();
}
- public async updateCurrentAgent(
+ /**
+ * Finds and update user agent information to a next available person.
+ */
+ public async updateHumanAgent(
userSystemId: string,
instanceId: number,
agentSystemId: string
): Promise {
const user = await GuaribasUser.findOne({
where: {
- userSystemId: userSystemId
+ userSystemId: userSystemId,
+ instanceId: instanceId
}
});
@@ -190,7 +203,7 @@ export class SecService extends GBService {
}
});
- await this.updateCurrentAgent(userSystemId, instanceId, agentSystemId);
+ await this.updateHumanAgent(userSystemId, instanceId, agentSystemId);
return agentSystemId;
}
diff --git a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts
index fe2cca1b..e4f2b826 100644
--- a/packages/whatsapp.gblib/services/WhatsappDirectLine.ts
+++ b/packages/whatsapp.gblib/services/WhatsappDirectLine.ts
@@ -178,7 +178,7 @@ export class WhatsappDirectLine extends GBService {
const sec = new SecService();
const user = await sec.ensureUser(this.min.instance.instanceId, id,
- senderName, '', 'whatsapp', senderName);
+ senderName, '', 'whatsapp', senderName, null);
const locale = user.locale ? user.locale : 'pt';
if (message.type === 'ptt') {
@@ -205,12 +205,22 @@ export class WhatsappDirectLine extends GBService {
const conversationId = WhatsappDirectLine.conversationIds[from];
const client = await this.directLineClient;
- if (user.agentMode === 'self') {
- const manualUser = await sec.getUserFromAgentSystemId(id);
+
+ // Check if this message is from a Human Agent itself.
+
+ if (user.agentMode === 'self') {
+
+ // Check if there is someone being handled by this Human Agent.
+
+ const manualUser = await sec.getUserFromAgentSystemId(id);
if (manualUser === null) {
- await sec.updateCurrentAgent(id, this.min.instance.instanceId, null);
+
+ await sec.updateHumanAgent(id, this.min.instance.instanceId, null);
+
} else {
+ const agent = await sec.getUserFromSystemId(user.agentSystemId);
+
const cmd = '/reply ';
if (text.startsWith(cmd)) {
const filename = text.substr(cmd.length);
@@ -218,37 +228,60 @@ export class WhatsappDirectLine extends GBService {
if (message === null) {
await this.sendToDeviceEx(user.userSystemId, `File ${filename} not found in any .gbkb published. Check the name or publish again the associated .gbkb.`,
- locale);
+ locale);
} else {
await this.min.conversationalService.sendMarkdownToMobile(this.min, null, user.userSystemId, message);
}
} else if (text === '/qt') {
// TODO: Transfers only in pt-br for now.
await this.sendToDeviceEx(manualUser.userSystemId,
- Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
- await this.sendToDeviceEx(user.agentSystemId,
- Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
+ Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
- await sec.updateCurrentAgent(manualUser.userSystemId, this.min.instance.instanceId, null);
+ if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId));
+ }
+ else {
+ await this.sendToDeviceEx(user.agentSystemId,
+ Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
+
+ }
+ await sec.updateHumanAgent(manualUser.userSystemId, this.min.instance.instanceId, null);
+ await sec.updateHumanAgent(user.agentSystemId, this.min.instance.instanceId, null);
} else {
GBLog.info(`HUMAN AGENT (${id}) TO USER ${manualUser.userSystemId}: ${text}`);
- this.sendToDeviceEx(manualUser.userSystemId, `${manualUser.agentSystemId}: ${text}`, locale);
+ await this.sendToDeviceEx(manualUser.userSystemId, `${manualUser.agentSystemId}: ${text}`, locale);
}
}
+
+
} else if (user.agentMode === 'human') {
+
const agent = await sec.getUserFromSystemId(user.agentSystemId);
if (text === '/t') {
await this.sendToDeviceEx(user.userSystemId, `Você já está sendo atendido por ${agent.userSystemId}.`, locale);
} else if (text === '/qt' || text === 'Sair' || text === 'Fechar') {
// TODO: Transfers only in pt-br for now.
await this.sendToDeviceEx(id,
- Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
- await this.sendToDeviceEx(user.agentSystemId, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
+ Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
- await sec.updateCurrentAgent(id, this.min.instance.instanceId, null);
+ if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await this.min.conversationalService['sendOnConversation'](this.min, agent, Messages[this.locale].notify_end_transfer(this.min.instance.botId));
+ }
+ else {
+ await this.sendToDeviceEx(user.agentSystemId, Messages[this.locale].notify_end_transfer(this.min.instance.botId), locale);
+ }
+
+ await sec.updateHumanAgent(id, this.min.instance.instanceId, null);
} else {
- GBLog.info(`USER (${id}) TO AGENT ${user.userSystemId}: ${text}`);
- this.sendToDeviceEx(user.agentSystemId, `Bot: ${this.min.instance.botId}\n${id}: ${text}`, locale);
+ GBLog.info(`USER (${id}) TO AGENT ${agent.userSystemId}: ${text}`);
+
+ if (user.agentSystemId.charAt(2) === ":") { // Agent is from Teams.
+ await this.min.conversationalService['sendOnConversation'](this.min, agent, text);
+ }
+ else {
+ await this.sendToDeviceEx(user.agentSystemId, `Bot: ${this.min.instance.botId}\n${id}: ${text}`, locale);
+ }
+
}
} else if (user.agentMode === 'bot' || user.agentMode === null || user.agentMode === undefined) {
@@ -451,7 +484,7 @@ export class WhatsappDirectLine extends GBService {
}
}
- private async sendToDeviceEx(to, text, locale) {
+ public async sendToDeviceEx(to, text, locale) {
const minBoot = GBServer.globals.minBoot as any;
text = await minBoot.conversationalService.translate(