Unverified Commit 5ebac03f authored by Renato Becker's avatar Renato Becker Committed by GitHub

Merge pull request #8 from RocketChat/uikit-support

[NEW] add UIKit support to show rich messages
parents b6aab9a2 f1418f12
......@@ -77,6 +77,8 @@ Integration between Rocket.Chat and the RASA Chatbot platform
- Enter the target department name where you want to transfer the visitor upon handover. Note that you can override setting using [Handover](./docs/api-endpoints/perform-handover.md) action.
7. Enable Callbacks
- Enabling this setting will allow the app to use only callback messages. This feature is useful when you are using Reminder messages in your RASA bot.
8. Hide Quick Replies (required)
- If enabled, then all quick-replies will hide when a visitor clicks on any one of them
### Apps.Rasa's API
......
......@@ -12,9 +12,11 @@ import { App } from '@rocket.chat/apps-engine/definition/App';
import { ILivechatMessage } from '@rocket.chat/apps-engine/definition/livechat';
import { IPostMessageSent } from '@rocket.chat/apps-engine/definition/messages';
import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata';
import { IUIKitResponse, UIKitLivechatBlockInteractionContext } from '@rocket.chat/apps-engine/definition/uikit';
import { settings } from './config/Settings';
import { CallbackInputEndpoint } from './endpoints/CallbackInputEndpoint';
import { IncomingEndpoint } from './endpoints/IncomingEndpoint';
import { ExecuteLivechatBlockActionHandler } from './handler/ExecuteLivechatBlockActionHandler';
import { PostMessageSentHandler } from './handler/PostMessageSentHandler';
export class RasaApp extends App implements IPostMessageSent {
......@@ -22,6 +24,15 @@ export class RasaApp extends App implements IPostMessageSent {
super(info, logger, accessors);
}
public async executeLivechatBlockActionHandler(context: UIKitLivechatBlockInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify): Promise<IUIKitResponse> {
const handler = new ExecuteLivechatBlockActionHandler(this, context, read, http, persistence, modify);
return await handler.run();
}
public async executePostMessageSent(message: ILivechatMessage,
read: IRead,
http: IHttp,
......
{
"id": "646b8e7d-f1e1-419e-9478-10d0f5bc74d9",
"version": "1.0.0",
"requiredApiVersion": "^1.15.0",
"version": "1.1.0",
"requiredApiVersion": "^1.17.0",
"iconFile": "icon.png",
"author": {
"name": "Rocket.Chat",
......
......@@ -8,6 +8,7 @@ export enum AppSetting {
RasaCloseChatMessage = 'rasa_close_chat_message',
RasaEnableCallbacks = 'rasa_enable_callbacks',
RasaDefaultHandoverDepartment = 'rasa_target_handover_department',
RasaHideQuickReplies = 'rasa_hide_quick_replies',
}
export enum DefaultMessage {
......@@ -79,4 +80,14 @@ export const settings: Array<ISetting> = [
i18nDescription: 'rasa_callback_message_description',
required: true,
},
{
id: AppSetting.RasaHideQuickReplies,
public: true,
type: SettingType.BOOLEAN,
packageValue: true,
value: true,
i18nLabel: 'rasa_hide_quick_replies',
i18nDescription: 'rasa_hide_quick_replies_description',
required: true,
},
];
import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { IApp } from '@rocket.chat/apps-engine/definition/IApp';
import { ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat';
import { IUIKitResponse, UIKitLivechatBlockInteractionContext } from '@rocket.chat/apps-engine/definition/uikit';
import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer';
import { IUser } from '@rocket.chat/apps-engine/definition/users';
import { AppSetting } from '../config/Settings';
import { createLivechatMessage, deleteAllActionBlocks } from '../lib/Message';
import { getAppSettingValue } from '../lib/Setting';
export class ExecuteLivechatBlockActionHandler {
constructor(private readonly app: IApp,
private context: UIKitLivechatBlockInteractionContext,
private read: IRead,
private http: IHttp,
private persistence: IPersistence,
private modify: IModify) {}
public async run(): Promise<IUIKitResponse> {
try {
const interactionData = this.context.getInteractionData();
const { visitor, room, container: { id, type }, value } = interactionData;
if (type !== UIKitIncomingInteractionContainerType.MESSAGE) {
return this.context.getInteractionResponder().successResponse();
}
const RasaBotUsername: string = await getAppSettingValue(this.read, AppSetting.RasaBotUsername);
const { servedBy: { username = null } = {}, id: rid } = room as ILivechatRoom;
if (!username || RasaBotUsername !== username) {
return this.context.getInteractionResponder().successResponse();
}
const appUser = await this.read.getUserReader().getAppUser(this.app.getID()) as IUser;
await createLivechatMessage(rid, this.read, this.modify, { text: value }, visitor);
const { value: hideQuickRepliesSetting } = await this.read.getEnvironmentReader().getSettings().getById(AppSetting.RasaHideQuickReplies);
if (hideQuickRepliesSetting) {
await deleteAllActionBlocks(this.modify, appUser, id);
}
return this.context.getInteractionResponder().successResponse();
} catch (error) {
this.app.getLogger().error(error);
return this.context.getInteractionResponder().errorResponse();
}
}
}
......@@ -10,5 +10,7 @@
"rasa_callback_message": "Enable Callbacks",
"rasa_callback_message_description": "Enabling this setting will allow the app to use only callback messages. This feature is useful when you are using Reminder messages in your RASA bot.",
"rasa_default_handover_department": "Default Handover Department Name",
"rasa_default_handover_department_description": "Enter the target department name where you want to transfer the visitor upon handover"
"rasa_default_handover_department_description": "Enter the target department name where you want to transfer the visitor upon handover",
"rasa_hide_quick_replies": "Hide Quick Replies",
"rasa_hide_quick_replies_description": "If enabled, then all quick-replies will hide when a visitor clicks on any one of them"
}
export const uuid = (): string => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
import { IModify, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { IMessageAction, IMessageAttachment, MessageActionType, MessageProcessingType } from '@rocket.chat/apps-engine/definition/messages';
import { IVisitor } from '@rocket.chat/apps-engine/definition/livechat';
import { BlockElementType, BlockType, IActionsBlock, IButtonElement, TextObjectType } from '@rocket.chat/apps-engine/definition/uikit';
import { IUser } from '@rocket.chat/apps-engine/definition/users';
import { AppSetting } from '../config/Settings';
import { Logs } from '../enum/Logs';
import { IRasaMessage, IRasaQuickReplies, IRasaQuickReply } from '../enum/Rasa';
import { getAppSettingValue } from './Setting';
import { uuid } from './Helper';
export const createRasaMessage = async (rid: string, read: IRead, modify: IModify, rasaMessage: IRasaMessage): Promise<any> => {
const { text, quickReplies } = rasaMessage.message as IRasaQuickReplies;
if (text && quickReplies) {
// rasaMessage is instanceof IRasaQuickReplies
const actions: Array<IMessageAction> = quickReplies.map((payload: IRasaQuickReply) => ({
type: MessageActionType.BUTTON,
text: payload.title,
msg: payload.payload,
msg_in_chat_window: true,
msg_processing_type: MessageProcessingType.SendMessage,
} as IMessageAction));
const attachment: IMessageAttachment = { actions };
await createMessage(rid, read, modify, { text, attachment });
const elements: Array<IButtonElement> = quickReplies.map((payload: IRasaQuickReply) => ({
type: BlockElementType.BUTTON,
text: {
type: TextObjectType.PLAINTEXT,
text: payload.title,
},
value: payload.payload,
actionId: uuid(),
} as IButtonElement));
const actionsBlock: IActionsBlock = { type: BlockType.ACTIONS, elements };
await createMessage(rid, read, modify, { text });
await createMessage(rid, read, modify, { actionsBlock });
} else {
// rasaMessage is instanceof string
await createMessage(rid, read, modify, { text: rasaMessage.message });
......@@ -49,6 +57,43 @@ export const createMessage = async (rid: string, read: IRead, modify: IModify,
}
const msg = modify.getCreator().startMessage().setRoom(room).setSender(sender);
const { text, actionsBlock } = message;
if (text) {
msg.setText(text);
}
if (actionsBlock) {
const { elements } = actionsBlock as IActionsBlock;
msg.addBlocks(modify.getCreator().getBlockBuilder().addActionsBlock({ elements }));
}
return new Promise(async (resolve) => {
modify.getCreator().finish(msg)
.then((result) => resolve(result))
.catch((error) => console.error(error));
});
};
export const createLivechatMessage = async (rid: string, read: IRead, modify: IModify, message: any, visitor: IVisitor ): Promise<any> => {
if (!message) {
return;
}
const botUserName = await getAppSettingValue(read, AppSetting.RasaBotUsername);
if (!botUserName) {
this.app.getLogger().error(Logs.EMPTY_BOT_USERNAME_SETTING);
return;
}
const room = await read.getRoomReader().getById(rid);
if (!room) {
this.app.getLogger().error(`${ Logs.INVALID_ROOM_ID } ${ rid }`);
return;
}
const msg = modify.getCreator().startLivechatMessage().setRoom(room).setVisitor(visitor);
const { text, attachment } = message;
if (text) {
......@@ -65,3 +110,9 @@ export const createMessage = async (rid: string, read: IRead, modify: IModify,
.catch((error) => console.error(error));
});
};
export const deleteAllActionBlocks = async (modify: IModify, appUser: IUser, msgId: string): Promise<void> => {
const msgBuilder = await modify.getUpdater().message(msgId, appUser);
msgBuilder.setEditor(appUser).setBlocks(modify.getCreator().getBlockBuilder().getBlocks());
return modify.getUpdater().finish(msgBuilder);
};
......@@ -29,9 +29,9 @@
}
},
"@rocket.chat/apps-engine": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.15.0.tgz",
"integrity": "sha512-gwsHa/zTYMmoSG3PP3sZfmVRDRBmIDacOAdCv1FsgJog89ZBCICeoab3VyYAdOMliV5XoygygduYFtc6rinFHQ==",
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.17.0.tgz",
"integrity": "sha512-4jsN9RUQR9zvZnOr4IZj9bnZR5/kAuSpKJ3JwqTv/DXBeHRSrCCCX0EDoZh5akmQu6x9tRpAXd1DRJWRfI5/Jw==",
"dev": true,
"requires": {
"adm-zip": "^0.4.9",
......@@ -50,9 +50,9 @@
"dev": true
},
"adm-zip": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
"integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==",
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
"dev": true
},
"ansi-styles": {
......
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