Skip to content

Commit

Permalink
Propagate message lifecycle to Smarti (RocketChat#301)
Browse files Browse the repository at this point in the history
* Once a conversation is closed, don't submit messages to Smarti anymore

* Update Chatpal-Search

* Smarti-52: Add support for edited messages

* Propagate updates and deletions to Smarti

* Add tests for message lifecycle. They are failing due to Smarti-error

* fix issue in smarti-integration-tests

* Propagate request deletion to Smarti and reorder tests

* Fix messages for livechat not sent to Smarti

* Merge branch 'develop' into fix/smarti/message-lifecycle
  • Loading branch information
mrsimpson authored Apr 12, 2018
1 parent 9d84c9f commit 692b380
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 123 deletions.
67 changes: 50 additions & 17 deletions packages/assistify-ai/server/hooks/sendMessageToKnowledgeAdapter.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,76 @@
/* globals RocketChat, SystemLogger */

import { getKnowledgeAdapter } from '../lib/KnowledgeAdapterProvider';
import {getKnowledgeAdapter} from '../lib/KnowledgeAdapterProvider';

RocketChat.callbacks.remove('afterSaveMessage', 'externalWebHook');

RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
// skips this callback if the message was edited
if (message.editedAt) {
return message;
}

function isMessageRelevant(message, room) {
let knowledgeEnabled = false;
RocketChat.settings.get('Assistify_AI_Enabled', function(key, value) {
knowledgeEnabled = value;
});

if (!knowledgeEnabled) {
return message;
return false;
}

//we only want to forward messages from livechat-rooms - requests are implemented in the help-request-package
if (!room) {
room = RocketChat.models.Rooms.findOneById(message.rid);
}

//we only want to forward messages from livechat-rooms
if (!(room && (room.t === 'l'))) {
return message;
return false;
}

const knowledgeAdapter = getKnowledgeAdapter();
if (!knowledgeAdapter) {
return;
return false;
}

return true;
}

RocketChat.callbacks.add('afterSaveMessage', function(message, room) {
if (isMessageRelevant(message, room)) {
const knowledgeAdapter = getKnowledgeAdapter();
SystemLogger.debug(`Send message ${ message._id } to knowledgeAdapter (Meteor.defer()`);
Meteor.defer(() => {
try {
SystemLogger.debug(`Calling onMessage(${ message._id });`);
knowledgeAdapter.onMessage(message);
} catch (e) {
SystemLogger.error('Error using knowledge provider ->', e);
}
});
}
}, RocketChat.callbacks.priority.LOW, 'Assistify_AI_OnMessage');

RocketChat.callbacks.add('afterDeleteMessage', function(message) {
if (isMessageRelevant(message)) {
const knowledgeAdapter = getKnowledgeAdapter();
SystemLogger.debug(`Propagating delete of message${ message._id } to knowledge-adapter`);
Meteor.defer(() => {
try {
SystemLogger.debug(`Calling afterDeleteMessage(${ message._id });`);
knowledgeAdapter.afterMessageDeleted(message);
} catch (e) {
SystemLogger.error('Error using knowledge provider ->', e);
}
});
}

SystemLogger.debug(`Send message ${ message._id } to knowledgeAdapter (Meteor.defer()`);
}, RocketChat.callbacks.priority.LOW, 'Assistify_AI_afterDeleteMessage');

RocketChat.callbacks.add('afterRoomErased', function(room) {
const knowledgeAdapter = getKnowledgeAdapter();
SystemLogger.debug(`Propagating delete of room ${ room._id } to knowledge-adapter`);
Meteor.defer(() => {
try {
SystemLogger.debug(`Calling onMessage(${ message._id });`);
knowledgeAdapter.onMessage(message);
SystemLogger.debug(`Calling afterRoomErased(${ room._id });`);
knowledgeAdapter.afterRoomErased(room);
} catch (e) {
SystemLogger.error('Error using knowledge provider ->', e);
}
});

return message;
}, RocketChat.callbacks.priority.LOW, 'Assistify_AI_OnMessage');
}, RocketChat.callbacks.priority.LOW, 'Assistify_AI_afterRoomErased');
160 changes: 96 additions & 64 deletions packages/assistify-ai/server/lib/SmartiAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* globals SystemLogger, RocketChat */

import { SmartiProxy, verbs } from '../SmartiProxy';
import {SmartiProxy, verbs} from '../SmartiProxy';

/**
* The SmartiAdapter handles the interaction with Smarti triggered by Rocket.Chat hooks (not by Smarti widget).
Expand All @@ -18,6 +18,53 @@ export class SmartiAdapter {
return RocketChat.settings.get('Assistify_AI_Smarti_Domain');
}

static _updateMapping(roomId, conversationId, timestamp) {
// update/insert channel/conversation specific timestamp
RocketChat.models.LivechatExternalMessage.update(
{
_id: roomId
}, {
rid: roomId,
knowledgeProvider: 'smarti',
conversationId,
ts: timestamp
}, {
upsert: true
}
);
}

/**
*
* @param {*} roomId - the room for which the Smarti conversationId shall be retrieved
* @param {*} message - An optional message for detsailed mapping information
*/
static _getConversationId(roomId, message) {
const smartiResponse = RocketChat.models.LivechatExternalMessage.findOneById(roomId);
let conversationId;

// conversation exists for channel?
if (smartiResponse && smartiResponse.conversationId) {
conversationId = smartiResponse.conversationId;
} else {
SystemLogger.debug('Smarti - Trying legacy service to retrieve conversation ID...');
const conversation = SmartiProxy.propagateToSmarti(verbs.get,
`legacy/rocket.chat?channel_id=${ roomId }`, null, (error) => {
// 404 is expected if no mapping exists
if (error.response.statusCode === 404) {
return null;
}
});
if (conversation && conversation.id) {
conversationId = conversation.id;
const timestamp = message ? message.ts : Date.now();
SmartiAdapter._updateMapping(roomId, conversationId, timestamp);
}
}

return conversationId;
}

/**
* Event implementation that posts the message to Smarti.
*
Expand All @@ -33,24 +80,6 @@ export class SmartiAdapter {
* @returns {*}
*/
static onMessage(message) {
function updateMapping(message, conversationId) {
// update/insert channel/conversation specific timestamp
RocketChat.models.LivechatExternalMessage.update(
{
_id: message.rid
}, {
rid: message.rid,
knowledgeProvider: 'smarti',
conversationId,
ts: message.ts
}, {
upsert: true
}
);
}


//TODO trigger on message update, if needed
const requestBodyMessage = {
'id': message._id,
'time': message.ts,
Expand All @@ -64,31 +93,25 @@ export class SmartiAdapter {

SystemLogger.debug('Message:', requestBodyMessage);

const m = RocketChat.models.LivechatExternalMessage.findOneById(message.rid);
let conversationId;
let conversationId = SmartiAdapter._getConversationId(message.rid, message);

// conversation exists for channel?
if (m && m.conversationId) {
conversationId = m.conversationId;
} else {
SystemLogger.debug('Smarti - Trying legacy service to retrieve conversation ID...');
const conversation = SmartiProxy.propagateToSmarti(verbs.get,
`legacy/rocket.chat?channel_id=${ message.rid }`, null, (error) => {
// 404 is expected if no mapping exists
if (conversationId) {
SystemLogger.debug(`Conversation ${ conversationId } found for channel ${ message.rid }`);
if (message.editedAt) {
SystemLogger.debug('Trying to update existing message...');
// update existing message
SmartiProxy.propagateToSmarti(verbs.put, `conversation/${ conversationId }/message/${ requestBodyMessage.id }`, requestBodyMessage, (error) => {
// 404 is expected if message doesn't exist
if (error.response.statusCode === 404) {
return null;
SystemLogger.debug('Message not found!');
SystemLogger.debug('Adding new message to conversation...');
SmartiProxy.propagateToSmarti(verbs.post, `conversation/${ conversationId }/message`, requestBodyMessage);
}
});
if (conversation && conversation.id) {
conversationId = conversation.id;
updateMapping(message, conversationId);
} else {
SystemLogger.debug('Adding new message to conversation...');
SmartiProxy.propagateToSmarti(verbs.post, `conversation/${ conversationId }/message`, requestBodyMessage);
}
}

if (conversationId) {
SystemLogger.debug(`Conversation ${ conversationId } found for channel ${ message.rid }`);
// add message to conversation
SmartiProxy.propagateToSmarti(verbs.post, `conversation/${ conversationId }/message`, requestBodyMessage);
} else {
SystemLogger.debug('Conversation not found for channel');
const helpRequest = RocketChat.models.HelpRequests.findOneByRoomId(message.rid);
Expand Down Expand Up @@ -120,12 +143,6 @@ export class SmartiAdapter {
'messages': [requestBodyMessage],
'context': {
'contextType': 'rocket.chat'
/*
"domain" : "test",
"environment" : {
}
*/
}
};

Expand All @@ -135,7 +152,7 @@ export class SmartiAdapter {
const conversation = SmartiProxy.propagateToSmarti(verbs.post, 'conversation', requestBodyConversation);
if (conversation && conversation.id) {
conversationId = conversation.id;
updateMapping(message, conversationId);
SmartiAdapter._updateMapping(message.rid, conversationId);
}
}

Expand All @@ -147,6 +164,37 @@ export class SmartiAdapter {
}
}

/**
* Event implementation for deletion of messages
* @param message - the message which has just been deleted
*/
static afterMessageDeleted(message) {

const conversationId = SmartiAdapter._getConversationId(message.rid, message);

if (conversationId) {
SystemLogger.debug(`Conversation ${ conversationId } found for channel ${ message.rid }`);

SystemLogger.debug(`Deleting message from conversation ${ conversationId } ...`);
// add message to conversation
SmartiProxy.propagateToSmarti(verbs.delete, `conversation/${ conversationId }/message/${ message._id }`);
}
}

/**
* Propagates the deletion of a complete conversation to Smarti
* @param room - the room just deleted
*/
static afterRoomErased(room) { //async
const conversationId = SmartiAdapter._getConversationId(room._id);

if (conversationId) {
SmartiProxy.propagateToSmarti(verbs.delete, `/conversation/${ conversationId }`);
} else {
SystemLogger.error(`Smarti - closing room failed: No conversation id for room: ${ room._id }`);
}
}

/**
* Event implementation that publishes the conversation in Smarti.
*
Expand All @@ -155,24 +203,8 @@ export class SmartiAdapter {
* @returns {*}
*/
static onClose(room) { //async
let conversationId;
// get conversation id
const m = RocketChat.models.LivechatExternalMessage.findOneById(room._id);
if (m && m.conversationId) {
conversationId = m.conversationId;
} else {
SystemLogger.debug('Smarti - Trying legacy service to retrieve conversation ID...');
const conversation = SmartiProxy.propagateToSmarti(verbs.get,
`legacy/rocket.chat?channel_id=${ room._id }`, null, (error) => {
// 404 is expected if no mapping exists
if (error.response.statusCode === 404) {
return null;
}
});
if (conversation && conversation.id) {
conversationId = conversation.id;
}
}
const conversationId = SmartiAdapter._getConversationId(room._id);

if (conversationId) {
SmartiProxy.propagateToSmarti(verbs.put, `/conversation/${ conversationId }/meta.status`, 'Complete');
} else {
Expand Down
Loading

0 comments on commit 692b380

Please sign in to comment.