Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions DialogflowApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ 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 { ISetting } from '@rocket.chat/apps-engine/definition/settings';
import { IUIKitLivechatInteractionHandler, IUIKitResponse, UIKitLivechatBlockInteractionContext } from '@rocket.chat/apps-engine/definition/uikit';
import { IUIKitInteractionHandler, IUIKitLivechatInteractionHandler, IUIKitResponse, UIKitBlockInteractionContext, UIKitLivechatBlockInteractionContext } from '@rocket.chat/apps-engine/definition/uikit';
import { settings } from './config/Settings';
import { FulfillmentsEndpoint } from './endpoints/FulfillmentsEndpoint';
import { IncomingEndpoint } from './endpoints/IncomingEndpoint';
import { ExecuteBlockActionHandler } from './handler/ExecuteBlockActionHandler';
import { ExecuteLivechatBlockActionHandler } from './handler/ExecuteLivechatBlockActionHandler';
import { OnSettingUpdatedHandler } from './handler/OnSettingUpdatedHandler';
import { PostMessageSentHandler } from './handler/PostMessageSentHandler';

export class DialogflowApp extends App implements IPostMessageSent, IUIKitLivechatInteractionHandler {
export class DialogflowApp extends App implements IPostMessageSent, IUIKitLivechatInteractionHandler, IUIKitInteractionHandler {
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
super(info, logger, accessors);
}
Expand All @@ -36,6 +37,15 @@ export class DialogflowApp extends App implements IPostMessageSent, IUIKitLivech
return await handler.run();
}

public async executeBlockActionHandler(context: UIKitBlockInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify): Promise<IUIKitResponse> {
const handler = new ExecuteBlockActionHandler(this, context, read, http, persistence, modify);
return await handler.run();
}

public async executePostMessageSent(message: ILivechatMessage,
read: IRead,
http: IHttp,
Expand Down
5 changes: 3 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "21b7d3ba-031b-41d9-8ff2-fbbfa081ae90",
"version": "1.2.3",
"version": "1.2.4",
"requiredApiVersion": "^1.17.0",
"iconFile": "icon.png",
"author": {
Expand All @@ -14,6 +14,7 @@
"description": "Integration between Rocket.Chat and the Dialogflow Chatbot platform",
"implements": [
"IPostMessageSent",
"IUIKitLivechatInteractionHandler"
"IUIKitLivechatInteractionHandler",
"IUIKitInteractionHandler"
]
}
11 changes: 11 additions & 0 deletions config/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum AppSetting {
DialogflowHandoverFailedMessage = 'dialogflow_no_agents_online_for_handover',
DialogflowCloseChatMessage = 'dialogflow_close_chat_message',
DialogflowHideQuickReplies = 'dialogflow_hide_quick_replies',
DialogflowAllowDirectMessage = 'dialogflow_allow_direct_message',
}

export enum DefaultMessage {
Expand Down Expand Up @@ -119,4 +120,14 @@ export const settings: Array<ISetting> = [
i18nDescription: 'dialogflow_hide_quick_replies_description',
required: true,
},
{
id: AppSetting.DialogflowAllowDirectMessage,
public: true,
type: SettingType.BOOLEAN,
packageValue: true,
value: true,
i18nLabel: 'dialogflow_allow_direct_message',
i18nDescription: 'dialogflow_allow_direct_message_description',
required: true,
},
];
82 changes: 82 additions & 0 deletions handler/ExecuteBlockActionHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { IApp } from '@rocket.chat/apps-engine/definition/IApp';
import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';
import { IUIKitResponse, UIKitBlockInteractionContext } 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 { ActionIds } from '../enum/ActionIds';
import { createDirectMessage, deleteAllActionBlocks } from '../lib/Message';
import { getAppSettingValue } from '../lib/Settings';

export class ExecuteBlockActionHandler {
constructor(private readonly app: IApp,
private context: UIKitBlockInteractionContext,
private read: IRead,
private http: IHttp,
private persistence: IPersistence,
private modify: IModify) {}

public async run(): Promise<IUIKitResponse> {
try {
const interactionData = this.context.getInteractionData();
const { room, container: { id, type }, value, actionId, message } = interactionData;

if (type !== UIKitIncomingInteractionContainerType.MESSAGE || !room || !message) {
return this.context.getInteractionResponder().successResponse();
}

const { type: roomType, userIds, id: rid } = room;

if (!roomType || roomType !== RoomType.DIRECT_MESSAGE) {
return this.context.getInteractionResponder().successResponse();
}

const directMessageAllowed = await getAppSettingValue(this.read, AppSetting.DialogflowAllowDirectMessage);
if (!directMessageAllowed) {
return this.context.getInteractionResponder().successResponse();
}

// check if the DM is of the bot
if (!userIds) {
return this.context.getInteractionResponder().successResponse();
}
if (!userIds.some((userId) => message.sender.id === userId)) {
return this.context.getInteractionResponder().successResponse();
}

const appUser = await this.read.getUserReader().getAppUser(this.app.getID()) as IUser;
switch (actionId) {
case ActionIds.PERFORM_HANDOVER:
break;

case ActionIds.CLOSE_CHAT:
break;
default: {
const otherUserId = (userIds as any).find((userId) => userId !== message.sender.id);
if (!otherUserId) {
return this.context.getInteractionResponder().errorResponse();
}

const otherUser = await this.read.getUserReader().getById(otherUserId);
if (!otherUser) {
return this.context.getInteractionResponder().errorResponse();
}

await createDirectMessage(this.app, rid, this.read, this.modify, { text: value }, otherUser);
break;
}
}

const { value: hideQuickRepliesSetting } = await this.read.getEnvironmentReader().getSettings().getById(AppSetting.DialogflowHideQuickReplies);
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();
}
}
}
36 changes: 31 additions & 5 deletions handler/PostMessageSentHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,45 @@ export class PostMessageSentHandler {
const { text, editedAt, room, token, sender } = this.message;
const livechatRoom = room as ILivechatRoom;

const { id: rid, type, servedBy, isOpen } = livechatRoom;
const { id: rid, type, servedBy, isOpen, userIds } = livechatRoom;

const DialogflowBotUsername: string = await getAppSettingValue(this.read, AppSetting.DialogflowBotUsername);

if (!type || type !== RoomType.LIVE_CHAT) {
if (!type) {
return;
}

if (!isOpen || !token || editedAt || !text) {
return;
switch (type) {
case RoomType.LIVE_CHAT: {
if (!isOpen || !token) {
return;
}
if (!servedBy || servedBy.username !== DialogflowBotUsername) {
return;
}
break;
}
case RoomType.DIRECT_MESSAGE: {
const directMessageAllowed = await getAppSettingValue(this.read, AppSetting.DialogflowAllowDirectMessage);
if (!directMessageAllowed) {
return;
}

// check if the DM is of the bot
if (!userIds) {
return;
}
if (!userIds.some((userId) => sender.id === userId)) {
return;
}
break;
}
default: {
return;
}
}

if (!servedBy || servedBy.username !== DialogflowBotUsername) {
if (editedAt || !text) {
return;
}

Expand Down
4 changes: 3 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@
"dialogflow_hide_quick_replies": "Hide Quick Replies",
"dialogflow_hide_quick_replies_description": "If enabled, then all quick-replies will hide when a visitor clicks on any one of them",
"dialogflow_handover_failed_message": "Handover Failed Message",
"dialogflow_handover_failed_message_description": "The Bot will send this message to Visitor if the handover failed because no agents were online"
"dialogflow_handover_failed_message_description": "The Bot will send this message to Visitor if the handover failed because no agents were online",
"dialogflow_allow_direct_message": "Enable support for Direct Messages",
"dialogflow_allow_direct_message_description": "If enabled, then the Dialogflow bot will also answer any direct messages to the Bot (defined in 'Bot Username' setting) "
}
34 changes: 34 additions & 0 deletions lib/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,37 @@ export const deleteAllActionBlocks = async (modify: IModify, appUser: IUser, msg
msgBuilder.setEditor(appUser).setBlocks(withoutActionBlocks);
return modify.getUpdater().finish(msgBuilder);
};

export const createDirectMessage = async (app: IApp, rid: string, read: IRead, modify: IModify, message: any, sender: IUser): Promise<any> => {
if (!message) {
return;
}

const room = await read.getRoomReader().getById(rid);
if (!room) {
app.getLogger().error(`${Logs.INVALID_ROOM_ID} ${rid}`);
return;
}

const msg = modify.getCreator().startMessage().setRoom(room).setSender(sender);

const { text, blocks, attachment } = message;

if (text) {
msg.setText(text);
}

if (attachment) {
msg.addAttachment(attachment);
}

if (blocks) {
msg.addBlocks(blocks);
}

return new Promise(async (resolve) => {
modify.getCreator().finish(msg)
.then((result) => resolve(result))
.catch((error) => console.error(error));
});
};