diff --git a/DialogflowApp.ts b/DialogflowApp.ts index a17b4c3..2cb7c0f 100644 --- a/DialogflowApp.ts +++ b/DialogflowApp.ts @@ -10,7 +10,7 @@ import { } from '@rocket.chat/apps-engine/definition/accessors'; import { ApiSecurity, ApiVisibility } from '@rocket.chat/apps-engine/definition/api'; import { App } from '@rocket.chat/apps-engine/definition/App'; -import { ILivechatMessage } from '@rocket.chat/apps-engine/definition/livechat'; +import { ILivechatMessage, ILivechatEventContext, IPostLivechatAgentAssigned } 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'; @@ -20,9 +20,10 @@ import { FulfillmentsEndpoint } from './endpoints/FulfillmentsEndpoint'; import { IncomingEndpoint } from './endpoints/IncomingEndpoint'; import { ExecuteLivechatBlockActionHandler } from './handler/ExecuteLivechatBlockActionHandler'; import { OnSettingUpdatedHandler } from './handler/OnSettingUpdatedHandler'; +import { OnAgentAssignedHandler } from './handler/OnAgentAssignedHandler'; import { PostMessageSentHandler } from './handler/PostMessageSentHandler'; -export class DialogflowApp extends App implements IPostMessageSent, IUIKitLivechatInteractionHandler { +export class DialogflowApp extends App implements IPostMessageSent, IPostLivechatAgentAssigned, IUIKitLivechatInteractionHandler { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { super(info, logger, accessors); } @@ -45,6 +46,15 @@ export class DialogflowApp extends App implements IPostMessageSent, IUIKitLivech await handler.run(); } + public async executePostLivechatAgentAssigned(context: ILivechatEventContext, + read: IRead, + http: IHttp, + persis: IPersistence, + modify: IModify): Promise { + const handler = new OnAgentAssignedHandler(this, context, read, http, persis, modify); + await handler.run(); + } + public async onSettingUpdated(setting: ISetting, configurationModify: IConfigurationModify, read: IRead, http: IHttp): Promise { const onSettingUpdatedHandler: OnSettingUpdatedHandler = new OnSettingUpdatedHandler(this, read, http); await onSettingUpdatedHandler.run(); diff --git a/config/Settings.ts b/config/Settings.ts index f52cb84..6a9fba0 100644 --- a/config/Settings.ts +++ b/config/Settings.ts @@ -11,6 +11,9 @@ export enum AppSetting { DialogflowServiceUnavailableMessage = 'dialogflow_service_unavailable_message', DialogflowCloseChatMessage = 'dialogflow_close_chat_message', DialogflowHideQuickReplies = 'dialogflow_hide_quick_replies', + DialogflowEnableChatClosedByVisitorEvent = 'dialogflow_enable_chat_closed_by_visitor_event', + DialogflowChatClosedByVisitorEventName = 'dialogflow_chat_closed_by_visitor_event_name', + DialogflowWelcomeIntentOnStart = 'dialogflow_welcome_intent_on_start', } export enum DefaultMessage { @@ -108,4 +111,32 @@ export const settings: Array = [ i18nDescription: 'dialogflow_hide_quick_replies_description', required: true, }, + { + id: AppSetting.DialogflowEnableChatClosedByVisitorEvent, + public: true, + type: SettingType.BOOLEAN, + packageValue: true, + value: true, + i18nLabel: 'dialogflow_enable_chat_closed_by_visitor_event', + i18nDescription: 'dialogflow_enable_chat_closed_by_visitor_event_description', + required: false, + }, + { + id: AppSetting.DialogflowChatClosedByVisitorEventName, + public: true, + type: SettingType.STRING, + packageValue: 'closed_by_visitor', + i18nLabel: 'dialogflow_chat_closed_by_visitor_event_name', + i18nDescription: 'dialogflow_chat_closed_by_visitor_event_name_description', + required: false, + }, + { + id: AppSetting.DialogflowWelcomeIntentOnStart, + public: true, + type: SettingType.BOOLEAN, + packageValue: false, + i18nLabel: 'dialogflow_welcome_intent_on_start', + i18nDescription: 'dialogflow_welcome_intent_on_start_description', + required: true, + }, ]; diff --git a/enum/Dialogflow.ts b/enum/Dialogflow.ts index 0d7b550..93e4588 100644 --- a/enum/Dialogflow.ts +++ b/enum/Dialogflow.ts @@ -57,3 +57,7 @@ export enum DialogflowRequestType { MESSAGE = 'message', EVENT = 'event', } + +export enum Message { + CLOSED_BY_VISITOR = 'Closed by visitor', +} diff --git a/handler/OnAgentAssignedHandler.ts b/handler/OnAgentAssignedHandler.ts new file mode 100644 index 0000000..3b75189 --- /dev/null +++ b/handler/OnAgentAssignedHandler.ts @@ -0,0 +1,58 @@ +import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; +import { IApp } from '@rocket.chat/apps-engine/definition/IApp'; +import { ILivechatEventContext, ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat'; +import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; +import { AppSetting, DefaultMessage } from '../config/Settings'; +import { DialogflowRequestType, IDialogflowMessage } from '../enum/Dialogflow'; +import { Logs } from '../enum/Logs'; +import { Dialogflow } from '../lib/Dialogflow'; +import { createDialogflowMessage, createMessage } from '../lib/Message'; +import { getAppSettingValue } from '../lib/Settings'; + +export class OnAgentAssignedHandler { + constructor(private readonly app: IApp, + private readonly context: ILivechatEventContext, + private readonly read: IRead, + private readonly http: IHttp, + private readonly persis: IPersistence, + private readonly modify: IModify) {} + + public async run() { + const { room } = this.context; + const livechatRoom = room as ILivechatRoom; + + const { id: rid, type, servedBy, isOpen } = livechatRoom; + + const DialogflowBotUsername: string = await getAppSettingValue(this.read, AppSetting.DialogflowBotUsername); + const { value: sendWelcomeEvent } = await this.read.getEnvironmentReader().getSettings().getById(AppSetting.DialogflowWelcomeIntentOnStart); + + if (!type || type !== RoomType.LIVE_CHAT) { + return; + } + + if (!isOpen || !sendWelcomeEvent) { + return; + } + + if (!servedBy || servedBy.username !== DialogflowBotUsername) { + return; + } + + try { + const event = { name: "Welcome", languageCode: "en" }; + const response: IDialogflowMessage = await Dialogflow.sendRequest(this.http, this.read, this.modify, rid, event, DialogflowRequestType.EVENT); + await createDialogflowMessage(rid, this.read, this.modify, response); + } catch (error) { + this.app.getLogger().error(`${Logs.DIALOGFLOW_REST_API_ERROR} ${error.message}`); + + const serviceUnavailable: string = await getAppSettingValue(this.read, AppSetting.DialogflowServiceUnavailableMessage); + + await createMessage(rid, + this.read, + this.modify, + { text: serviceUnavailable ? serviceUnavailable : DefaultMessage.DEFAULT_DialogflowServiceUnavailableMessage }); + + return; + } + } +} diff --git a/handler/PostMessageSentHandler.ts b/handler/PostMessageSentHandler.ts index 3c3325e..35c251b 100644 --- a/handler/PostMessageSentHandler.ts +++ b/handler/PostMessageSentHandler.ts @@ -3,7 +3,7 @@ import { IApp } from '@rocket.chat/apps-engine/definition/IApp'; import { ILivechatMessage, ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import { AppSetting, DefaultMessage } from '../config/Settings'; -import { DialogflowRequestType, IDialogflowMessage } from '../enum/Dialogflow'; +import { DialogflowRequestType, IDialogflowMessage, LanguageCode, Message } from '../enum/Dialogflow'; import { Logs } from '../enum/Logs'; import { Dialogflow } from '../lib/Dialogflow'; import { createDialogflowMessage, createMessage } from '../lib/Message'; @@ -26,6 +26,10 @@ export class PostMessageSentHandler { const DialogflowBotUsername: string = await getAppSettingValue(this.read, AppSetting.DialogflowBotUsername); + if (text === Message.CLOSED_BY_VISITOR) { + this.handleClosedByVisitor(rid); + } + if (!type || type !== RoomType.LIVE_CHAT) { return; } @@ -71,4 +75,20 @@ export class PostMessageSentHandler { } return resetFallbackIntent(this.read, this.modify, rid); } + + private async handleClosedByVisitor(rid: string) { + const DialogflowEnableChatClosedByVisitorEvent: boolean = await getAppSettingValue(this.read, AppSetting.DialogflowEnableChatClosedByVisitorEvent); + const DialogflowChatClosedByVisitorEventName: string = await getAppSettingValue(this.read, AppSetting.DialogflowChatClosedByVisitorEventName); + if (DialogflowEnableChatClosedByVisitorEvent) { + try { + let res: IDialogflowMessage; + res = (await Dialogflow.sendRequest(this.http, this.read, this.modify, rid, { + name: DialogflowChatClosedByVisitorEventName, + languageCode: LanguageCode.EN, + }, DialogflowRequestType.EVENT)); + } catch (error) { + this.app.getLogger().error(`${Logs.DIALOGFLOW_REST_API_ERROR} ${error.message}`); + } + } + } } diff --git a/i18n/en.json b/i18n/en.json index fa36e66..6e3ed3e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,5 +14,11 @@ "dialogflow_close_chat_message": "Close Chat Message", "dialogflow_close_chat_message_description": "This message will be sent automatically when a chat is closed", "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_hide_quick_replies_description": "If enabled, then all quick-replies will hide when a visitor clicks on any one of them", + "dialogflow_enable_chat_closed_by_visitor_event": "Enable Dialogflow Event When Visitor Ends Chat", + "dialogflow_enable_chat_closed_by_visitor_event_description": "If enabled, then a dialogflow event will be triggered when visitor ends chat", + "dialogflow_chat_closed_by_visitor_event_name": "Dialogflow Event Name When Visitor Ends Chat", + "dialogflow_chat_closed_by_visitor_event_name_description": "The bot will trigger this dialogflow event when visitor ends chat", + "dialogflow_welcome_intent_on_start": "Send Welcome Intent", + "dialogflow_welcome_intent_on_start_description": "If enabled, then a welcome message will be sent when bot is assigned to any visitor" }