Commit e5a93309 authored by murtaza98's avatar murtaza98

[NEW] connect with Rasa's rest api + multi-user support

parent 619d4289
......@@ -9,12 +9,14 @@ import {
IRead,
} from '@rocket.chat/apps-engine/definition/accessors';
import { App } from '@rocket.chat/apps-engine/definition/App';
import { ILivechatEventContext, IPostLivechatAgentAssigned } from '@rocket.chat/apps-engine/definition/livechat';
import { IMessage, IPostMessageSent } from '@rocket.chat/apps-engine/definition/messages';
import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata';
import { PostLivechatAgentAssignedHandler } from './handler/PostLivechatAgentAssignedHandler';
import { PostMessageSentHandler } from './handler/PostMessageSentHandler';
import { AppSettings } from './lib/AppSettings';
export class AppsRasaApp extends App implements IPostMessageSent {
export class AppsRasaApp extends App implements IPostMessageSent, IPostLivechatAgentAssigned {
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
super(info, logger, accessors);
}
......@@ -37,4 +39,9 @@ export class AppsRasaApp extends App implements IPostMessageSent {
}
}
public async executePostLivechatAgentAssigned(context: ILivechatEventContext, read: IRead, http: IHttp, persistence: IPersistence): Promise<void> {
const postLivechatAgentAssignedHandler = new PostLivechatAgentAssignedHandler(context, read, http, persistence);
await postLivechatAgentAssignedHandler.run();
}
}
export interface IParsedRasaResponse {
messages: Array<string>;
}
import { ILivechatEventContext, ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat';
import { IHttp, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { getAppSetting } from '../helper';
import { AppSettingId } from '../lib/AppSettings';
import { AppPersistence } from '../lib/persistence';
export class PostLivechatAgentAssignedHandler {
constructor(private context: ILivechatEventContext,
private read: IRead,
private http: IHttp,
private persis: IPersistence) {}
public async run() {
const SettingBotUsername: string = await getAppSetting(this.read, AppSettingId.RasaBotUsername);
if (SettingBotUsername !== this.context.agent.username) { return; }
await this.saveVisitorSession();
}
/**
*
* @description - save visitor.token and session id.
* - This will provide a mapping between visitor.token n session id.
* - This is required for implementing `perform-handover` webhooks since it requires a Visitor object
* which can be obtained from using visitor.token we save here in Persistant storage
*/
private async saveVisitorSession() {
const persistence = new AppPersistence(this.persis, this.read.getPersistenceReader());
const lroom = this.context.room as ILivechatRoom;
if (!lroom) { throw new Error('Error!! Could not create session. room object is undefined'); }
// session Id for Dialogflow will be the same as Room Id
const sessionId = lroom.id;
await persistence.saveSessionId(sessionId);
}
}
......@@ -2,8 +2,11 @@ import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/de
import { IApp } from '@rocket.chat/apps-engine/definition/IApp';
import { IMessage } from '@rocket.chat/apps-engine/definition/messages';
import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
import { getAppSetting, getBotUser } from '../helper';
import { IUser } from '@rocket.chat/apps-engine/definition/users';
import { IParsedRasaResponse } from '../definition/IParsedRasaResponse';
import { getAppSetting, getBotUser, getSessionId } from '../helper';
import { AppSettingId } from '../lib/AppSettings';
import { RasaSDK } from '../lib/RasaSDK';
export class PostMessageSentHandler {
constructor(private app: IApp,
......@@ -27,5 +30,25 @@ export class PostMessageSentHandler {
}
// send request to Rasa
if (!this.message.text || (this.message.text && this.message.text.trim().length === 0)) { return; }
const messageText: string = this.message.text;
const sessionId = getSessionId(this.message);
const rasaSDK: RasaSDK = new RasaSDK(this.http, this.read, this.persis, sessionId, messageText);
const response: IParsedRasaResponse = await rasaSDK.sendMessage();
for (const message of response.messages) {
await this.sendMessageToVisitor(message);
}
}
private async sendMessageToVisitor(message: string) {
const sender: IUser = getBotUser(this.message);
// build the message for Livechat widget
const builder = this.modify.getNotifier().getMessageBuilder();
builder.setRoom(this.message.room).setText(message).setSender(sender);
await this.modify.getCreator().finish(builder);
}
}
......@@ -2,6 +2,7 @@ import { ISetting, SettingType} from '@rocket.chat/apps-engine/definition/settin
export enum AppSettingId {
RasaBotUsername = 'rasa_bot_username',
RasaServerUrl = 'rasa_server_url',
}
export const AppSettings: Array<ISetting> = [
......@@ -13,4 +14,13 @@ export const AppSettings: Array<ISetting> = [
i18nLabel: 'Bot Username',
required: true,
},
{
id: AppSettingId.RasaServerUrl,
public: true,
type: SettingType.STRING,
packageValue: '',
i18nLabel: 'Rasa Server Url',
i18nDescription: 'Here enter the RASA url where the RASA server is hosted. Make sure to add `/webhooks/rest/webhook` to the end of url. Eg: https://efee760b.ngrok.io/webhooks/rest/webhook',
required: true,
},
];
import { IHttp, IHttpRequest, IHttpResponse, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { IParsedRasaResponse } from '../definition/IParsedRasaResponse';
import { getAppSetting } from '../helper';
import { AppSettingId } from './AppSettings';
export class RasaSDK {
constructor(private http: IHttp,
private read: IRead,
private persis: IPersistence,
private sessionId: string,
private messageText: string) {}
public async sendMessage(): Promise<IParsedRasaResponse> {
const rasaServerUrl = await getAppSetting(this.read, AppSettingId.RasaServerUrl);
const httpRequestContent: IHttpRequest = this.buildRasaHTTPRequest();
// send request to dialogflow
const response = await this.http.post(rasaServerUrl, httpRequestContent);
if (response.statusCode !== 200) { throw Error(`Error occured while interacting with Rasa Rest API. Details: ${response.content}`); }
const parsedMessage = this.parseRasaResponse(response);
return parsedMessage;
}
private parseRasaResponse(response: IHttpResponse): IParsedRasaResponse {
if (!response.content) { throw new Error('Error Parsing Dialogflow\'s Response. Content is undefined'); }
const responseJSON = JSON.parse(response.content);
const messages: Array<string> = [];
responseJSON.forEach((element) => {
messages.push(element.text);
});
return {
messages,
};
}
private buildRasaHTTPRequest(): IHttpRequest {
return {
headers: {
'Content-Type': 'application/json',
},
data: {
sender: this.sessionId,
message: this.messageText,
},
};
}
}
import { IPersistence, IPersistenceRead } from '@rocket.chat/apps-engine/definition/accessors';
import { RocketChatAssociationModel, RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata';
export class AppPersistence {
constructor(private readonly persistence: IPersistence, private readonly persistenceRead: IPersistenceRead) {}
public async saveSessionId(sessionId: string): Promise<void> {
const sessionIdAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, sessionId);
const sessionAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, 'session-Id');
await this.persistence.updateByAssociations([sessionIdAssociation, sessionAssociation], {
sessionId,
}, true);
}
public async checkIfSessionExists(sessionId: string): Promise<boolean> {
const sessionIdAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, sessionId);
const sessionAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, 'session-Id');
const [result] = await this.persistenceRead.readByAssociations([sessionIdAssociation, sessionAssociation]);
return result && (result as any).sessionId;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment