diff --git a/DialogflowApp.ts b/DialogflowApp.ts index e8e497d..de824ec 100644 --- a/DialogflowApp.ts +++ b/DialogflowApp.ts @@ -25,7 +25,6 @@ import { OnAgentUnassignedHandler } from './handler/OnAgentUnassignedHandler'; import { OnSettingUpdatedHandler } from './handler/OnSettingUpdatedHandler'; import { PostMessageSentHandler } from './handler/PostMessageSentHandler'; import { EventScheduler } from './lib/EventTimeoutProcessor'; -import { IdleSessionTimeoutProcessor } from './lib/IdleSessionTimeoutProcessor'; import { SessionMaintenanceProcessor } from './lib/sessionMaintenance/SessionMaintenanceProcessor'; export class DialogflowApp extends App implements IPostMessageSent, IPostLivechatAgentAssigned, IPostLivechatAgentUnassigned, IPostLivechatRoomClosed, IUIKitLivechatInteractionHandler { @@ -92,7 +91,6 @@ export class DialogflowApp extends App implements IPostMessageSent, IPostLivecha await configuration.scheduler.registerProcessors([ new SessionMaintenanceProcessor('session-maintenance'), new EventScheduler('event-scheduler'), - new IdleSessionTimeoutProcessor('idle-session-timeout'), ]); await Promise.all(settings.map((setting) => configuration.settings.provideSetting(setting))); diff --git a/endpoints/IncomingEndpoint.ts b/endpoints/IncomingEndpoint.ts index b14efb3..b2081aa 100644 --- a/endpoints/IncomingEndpoint.ts +++ b/endpoints/IncomingEndpoint.ts @@ -37,7 +37,7 @@ export class IncomingEndpoint extends ApiEndpoint { if (!sessionId) { throw new Error(Logs.INVALID_SESSION_ID); } switch (action) { case EndpointActionNames.CLOSE_CHAT: - await closeChat(modify, read, sessionId); + await closeChat(modify, read, sessionId, persistence); break; case EndpointActionNames.HANDOVER: const { actionData: { targetDepartment = '' } = {} } = endpointContent; diff --git a/handler/ExecuteLivechatBlockActionHandler.ts b/handler/ExecuteLivechatBlockActionHandler.ts index 174c014..89f43a9 100644 --- a/handler/ExecuteLivechatBlockActionHandler.ts +++ b/handler/ExecuteLivechatBlockActionHandler.ts @@ -47,7 +47,7 @@ export class ExecuteLivechatBlockActionHandler { break; case ActionIds.CLOSE_CHAT: - await closeChat(this.modify, this.read, rid); + await closeChat(this.modify, this.read, rid, this.persistence); break; default: diff --git a/handler/OnAgentUnassignedHandler.ts b/handler/OnAgentUnassignedHandler.ts index a37b115..cce6609 100644 --- a/handler/OnAgentUnassignedHandler.ts +++ b/handler/OnAgentUnassignedHandler.ts @@ -1,12 +1,12 @@ 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 { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { AppSetting, DefaultMessage } from '../config/Settings'; +import { Logs } from '../enum/Logs'; import { removeBotTypingListener } from '../lib//BotTyping'; import { createMessage, sendCloseChatButton } from '../lib/Message'; import { getAppSettingValue } from '../lib/Settings'; -import { Logs } from '../enum/Logs'; -import { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; export class OnAgentUnassignedHandler { constructor(private readonly app: IApp, diff --git a/handler/PostMessageSentHandler.ts b/handler/PostMessageSentHandler.ts index 07e252f..bf56d95 100644 --- a/handler/PostMessageSentHandler.ts +++ b/handler/PostMessageSentHandler.ts @@ -45,7 +45,7 @@ export class PostMessageSentHandler { return; } await this.handleClosedByVisitor(rid); - await closeChat(this.modify, this.read, rid); + await closeChat(this.modify, this.read, rid, this.persistence); return; } diff --git a/lib/Dialogflow.ts b/lib/Dialogflow.ts index 7f0d157..1a41871 100644 --- a/lib/Dialogflow.ts +++ b/lib/Dialogflow.ts @@ -191,7 +191,6 @@ class DialogflowClass { } public async parseCXRequest(read: IRead, response: any): Promise { - console.log(response); if (!response) { throw new Error(Logs.INVALID_RESPONSE_FROM_DIALOGFLOW_CONTENT_UNDEFINED); } const { session, queryResult } = response; diff --git a/lib/IdleSessionTimeoutProcessor.ts b/lib/IdleSessionTimeoutProcessor.ts deleted file mode 100644 index 0038a49..0000000 --- a/lib/IdleSessionTimeoutProcessor.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; -import { ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat/ILivechatRoom'; -import { IJobContext, IProcessor } from '@rocket.chat/apps-engine/definition/scheduler'; -import { DialogflowRequestType, IDialogflowMessage } from '../enum/Dialogflow'; -import { removeBotTypingListener } from './BotTyping'; -import { Dialogflow } from './Dialogflow'; -import { createDialogflowMessage } from './Message'; -import { handlePayloadActions } from './payloadAction'; -import { resetFallbackIntent } from './SynchronousHandover'; - -export class IdleSessionTimeoutProcessor implements IProcessor { - public id: string; - - constructor(id: string) { - this.id = id; - } - - public async processor(jobContext: IJobContext, read: IRead, modify: IModify, http: IHttp, persis: IPersistence): Promise { - - const event = { name: 'Close_Session', languageCode: 'en', parameters: {} }; - const response: IDialogflowMessage = await Dialogflow.sendRequest(http, - read, - modify, - persis, - jobContext.rid, - event, - DialogflowRequestType.EVENT); - - const createResponseMessage = async () => await createDialogflowMessage(jobContext.rid, read, modify, response); - - await createResponseMessage(); - - await removeBotTypingListener(jobContext.rid); - - const room = await read.getRoomReader().getById(jobContext.rid); - const { visitor: { token: visitorToken } } = room as ILivechatRoom; - - handlePayloadActions(read, modify, http, persis, jobContext.rid, visitorToken, response); - - return resetFallbackIntent(read, modify, jobContext.rid); - } -} diff --git a/lib/Room.ts b/lib/Room.ts index 8a1bd72..d90a75a 100644 --- a/lib/Room.ts +++ b/lib/Room.ts @@ -1,4 +1,4 @@ -import { IModify, IRead } from '@rocket.chat/apps-engine/definition/accessors'; +import { IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; import { IDepartment, ILivechatRoom, ILivechatTransferData, IVisitor } from '@rocket.chat/apps-engine/definition/livechat'; import { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { AppSetting, DefaultMessage } from '../config/Settings'; @@ -33,8 +33,7 @@ export const updateRoomCustomFields = async (rid: string, data: any, read: IRead } }; -export const closeChat = async (modify: IModify, read: IRead, rid: string) => { - await modify.getScheduler().cancelJobByDataQuery({ sessionId: rid }); +export const closeChat = async (modify: IModify, read: IRead, rid: string, persistence?: IPersistence) => { const room: IRoom = (await read.getRoomReader().getById(rid)) as IRoom; if (!room) { throw new Error(Logs.INVALID_ROOM_ID); } diff --git a/lib/Timeout.ts b/lib/Timeout.ts index a751380..e88e933 100644 --- a/lib/Timeout.ts +++ b/lib/Timeout.ts @@ -1,108 +1,77 @@ import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; import { IApp } from '@rocket.chat/apps-engine/definition/IApp'; import { IMessage } from '@rocket.chat/apps-engine/definition/messages'; -import { RocketChatAssociationModel, RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import { AppSetting } from '../config/Settings'; import { getAppSettingValue } from '../lib/Settings'; -import { retrieveDataByAssociation } from './retrieveDataByAssociation'; export const handleTimeout = async (app: IApp, message: IMessage, read: IRead, http: IHttp, persistence: IPersistence, modify: IModify ) => { - if (message.room.type !== RoomType.LIVE_CHAT || (message.customFields && message.customFields.idleTimeoutConfig)) { - return; - } - - const dialogflowBotUsername: string = (await getAppSettingValue(read, AppSetting.DialogflowBotUsername)); - const chasitorIdleTimeoutIsEnabled: string = (await getAppSettingValue(read, AppSetting.DialogflowEnableCustomerTimeout)); - - if (chasitorIdleTimeoutIsEnabled) { - - /** - * Sets the amount of time that a customer has to respond to an agent message before a warning appears and a timer begins a countdown. - * The warning disappears (and the timer stops) each time the customer sends a message. - * The warning disappears (and the timer resets to 0) each time the agent sends message. - * The warning value must be shorter than the time-out value (we recommend at least 30 seconds). - */ - const warningTime: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutWarningTime)); - - /** - * Sets the amount of time that a customer has to respond to an agent message before the session ends. - * The timer stops when the customer sends a message and starts again from 0 on the next agent's message. - */ - const timeoutTime: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutTime)); - - // ------ When agent sends message ----- - // Send new timeout msg and reset previous timeout - - // ------ When customer sends message ----- - // Send timeout msg to cancel previous timeout - - // On Timeout : Close chat - // On Warning : Show Countdown Popup in Livechat Widget - - const timeoutWarningMessage: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutWarningMessage)); - - if (message.sender.username === dialogflowBotUsername) { - // Agent sent message - if (!message.id) { - return; - } - const user = await read.getUserReader().getByUsername(dialogflowBotUsername); - const msgExtender = modify.getExtender().extendMessage(message.id, user); - (await msgExtender).addCustomField('idleTimeoutConfig', { - idleTimeoutAction: 'start', - idleTimeoutWarningTime: warningTime, - idleTimeoutTimeoutTime: timeoutTime, - idleTimeoutMessage: timeoutWarningMessage, - }); - modify.getExtender().finish(await msgExtender); - } else { - - // Guest sent message - - if (!message.id) { - return; - } - const user = await read.getUserReader().getByUsername(dialogflowBotUsername); - const msgExtender = modify.getExtender().extendMessage(message.id, user); - (await msgExtender).addCustomField('idleTimeoutConfig', { - idleTimeoutAction: 'stop', - idleTimeoutWarningTime: warningTime, - idleTimeoutTimeoutTime: timeoutTime, - idleTimeoutMessage: timeoutWarningMessage, - }); - modify.getExtender().finish(await msgExtender); - - await scheduleTimeOut(message, read, modify, persistence); - - } - } -}; - -async function scheduleTimeOut(message: IMessage, read: IRead, modify: IModify, persistence: IPersistence) { - const idleTimeoutTimeoutTime: string = await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutTime); - const rid = message.room.id; - - updateIdleSessionScheduleStatus(read, modify, persistence, rid); - - const task = { - id: 'idle-session-timeout', - when: `${idleTimeoutTimeoutTime} seconds`, - data: {rid}, - }; - await modify.getScheduler().scheduleOnce(task); -} - -export const updateIdleSessionScheduleStatus = async (read: IRead, modify: IModify, persistence: IPersistence, rid: string) => { - const assoc = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, `SFLAIA-${rid}`); - const data = await retrieveDataByAssociation(read, assoc); - - if (data && data.idleSessionScheduleStarted) { - await persistence.updateByAssociation(assoc, { idleSessionScheduleStarted: true }); - await modify.getScheduler().cancelJob('idle-session-timeout'); - - } else { - await persistence.createWithAssociation({ idleSessionScheduleStarted: true }, assoc); - } + if (message.room.type !== RoomType.LIVE_CHAT || (message.customFields && message.customFields.idleTimeoutConfig)) { + return; + } + + const dialogflowBotUsername: string = (await getAppSettingValue(read, AppSetting.DialogflowBotUsername)); + const chasitorIdleTimeoutIsEnabled: string = (await getAppSettingValue(read, AppSetting.DialogflowEnableCustomerTimeout)); + + if (chasitorIdleTimeoutIsEnabled) { + + /** + * Sets the amount of time that a customer has to respond to an agent message before a warning appears and a timer begins a countdown. + * The warning disappears (and the timer stops) each time the customer sends a message. + * The warning disappears (and the timer resets to 0) each time the agent sends message. + * The warning value must be shorter than the time-out value (we recommend at least 30 seconds). + */ + const warningTime: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutWarningTime)); + + /** + * Sets the amount of time that a customer has to respond to an agent message before the session ends. + * The timer stops when the customer sends a message and starts again from 0 on the next agent's message. + */ + const timeoutTime: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutTime)); + + // ------ When agent sends message ----- + // Send new timeout msg and reset previous timeout + + // ------ When customer sends message ----- + // Send timeout msg to cancel previous timeout + + // On Timeout : Close chat + // On Warning : Show Countdown Popup in Livechat Widget + + const timeoutWarningMessage: string = (await getAppSettingValue(read, AppSetting.DialogflowCustomerTimeoutWarningMessage)); + + if (message.sender.username === dialogflowBotUsername) { + // Agent sent message + if (!message.id) { + return; + } + const user = await read.getUserReader().getByUsername(dialogflowBotUsername); + const msgExtender = modify.getExtender().extendMessage(message.id, user); + (await msgExtender).addCustomField('idleTimeoutConfig', { + idleTimeoutAction: 'start', + idleTimeoutWarningTime: warningTime, + idleTimeoutTimeoutTime: timeoutTime, + idleTimeoutMessage: timeoutWarningMessage, + }); + modify.getExtender().finish(await msgExtender); + } else { + + // Guest sent message + + if (!message.id) { + return; + } + const user = await read.getUserReader().getByUsername(dialogflowBotUsername); + const msgExtender = modify.getExtender().extendMessage(message.id, user); + (await msgExtender).addCustomField('idleTimeoutConfig', { + idleTimeoutAction: 'stop', + idleTimeoutWarningTime: warningTime, + idleTimeoutTimeoutTime: timeoutTime, + idleTimeoutMessage: timeoutWarningMessage, + }); + modify.getExtender().finish(await msgExtender); + + } + } }; diff --git a/lib/payloadAction.ts b/lib/payloadAction.ts index fde7639..46363ad 100644 --- a/lib/payloadAction.ts +++ b/lib/payloadAction.ts @@ -1,5 +1,4 @@ import { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; -import { RocketChatAssociationModel, RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata'; import { AppSetting, DefaultMessage } from '../config/Settings'; import { ActionIds } from '../enum/ActionIds'; import { DialogflowRequestType, IDialogflowAction, IDialogflowMessage, IDialogflowPayload} from '../enum/Dialogflow'; @@ -7,7 +6,6 @@ import { closeChat, performHandover, updateRoomCustomFields } from '../lib/Room' import { getAppSettingValue } from '../lib/Settings'; import { Dialogflow } from './Dialogflow'; import { createMessage } from './Message'; -import { updateIdleSessionScheduleStatus } from './Timeout'; export const handlePayloadActions = async (read: IRead, modify: IModify, http: IHttp, persistence: IPersistence, rid: string, visitorToken: string, dialogflowMessage: IDialogflowMessage) => { const { messages = [] } = dialogflowMessage; @@ -35,13 +33,7 @@ export const handlePayloadActions = async (read: IRead, modify: IModify, http: } await performHandover(modify, read, rid, visitorToken, targetDepartment); } else if (actionName === ActionIds.CLOSE_CHAT) { - - await updateIdleSessionScheduleStatus(read, modify, persistence, rid); - - const assoc = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, `SFLAIA-${rid}`); - await persistence.updateByAssociation(assoc, { idleSessionScheduleStarted: false }); - - await closeChat(modify, read, rid); + await closeChat(modify, read, rid, persistence); } else if (actionName === ActionIds.SET_TIMEOUT) { const event = { name: params.eventName, languageCode: 'en', parameters: {} };