diff --git a/app.json b/app.json index 4273c2e..cdedeec 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "id": "21b7d3ba-031b-41d9-8ff2-fbbfa081ae90", - "version": "1.2.2", + "version": "1.2.3", "requiredApiVersion": "^1.17.0", "iconFile": "icon.png", "author": { @@ -16,4 +16,4 @@ "IPostMessageSent", "IUIKitLivechatInteractionHandler" ] -} +} \ No newline at end of file diff --git a/config/Settings.ts b/config/Settings.ts index 8c6ca0a..864e257 100644 --- a/config/Settings.ts +++ b/config/Settings.ts @@ -9,6 +9,7 @@ export enum AppSetting { FallbackTargetDepartment = 'fallback_target_department', DialogflowHandoverMessage = 'dialogflow_handover_message', DialogflowServiceUnavailableMessage = 'dialogflow_service_unavailable_message', + DialogflowHandoverFailedMessage = 'dialogflow_no_agents_online_for_handover', DialogflowCloseChatMessage = 'dialogflow_close_chat_message', DialogflowHideQuickReplies = 'dialogflow_hide_quick_replies', } @@ -81,6 +82,15 @@ export const settings: Array = [ i18nDescription: 'dialogflow_handover_message_description', required: false, }, + { + id: AppSetting.DialogflowHandoverFailedMessage, + public: true, + type: SettingType.STRING, + packageValue: '', + i18nLabel: 'dialogflow_handover_failed_message', + i18nDescription: 'dialogflow_handover_failed_message_description', + required: false, + }, { id: AppSetting.DialogflowServiceUnavailableMessage, public: true, diff --git a/endpoints/IncomingEndpoint.ts b/endpoints/IncomingEndpoint.ts index 6483788..63e96e5 100644 --- a/endpoints/IncomingEndpoint.ts +++ b/endpoints/IncomingEndpoint.ts @@ -22,16 +22,19 @@ export class IncomingEndpoint extends ApiEndpoint { this.app.getLogger().info(Logs.ENDPOINT_RECEIVED_REQUEST); try { - await this.processRequest(read, modify, http, request.content); - return createHttpResponse(HttpStatusCode.OK, { 'Content-Type': Headers.CONTENT_TYPE_JSON }, { result: Response.SUCCESS }); + const { statusCode = HttpStatusCode.OK, data = null } = await this.processRequest(read, modify, http, request.content); + return createHttpResponse(statusCode, { 'Content-Type': Headers.CONTENT_TYPE_JSON }, { ...data ? { ...data } : { result: Response.SUCCESS } }); } catch (error) { this.app.getLogger().error(Logs.ENDPOINT_REQUEST_PROCESSING_ERROR, error); return createHttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR, { 'Content-Type': Headers.CONTENT_TYPE_JSON }, { error: error.message }); } } - private async processRequest(read: IRead, modify: IModify, http: IHttp, endpointContent: IActionsEndpointContent) { - + private async processRequest(read: IRead, + modify: IModify, + http: IHttp, + endpointContent: IActionsEndpointContent, + ): Promise<{ statusCode: HttpStatusCode, data?: { result?: string; error?: string } }> { const { action, sessionId } = endpointContent; if (!sessionId) { throw new Error(Logs.INVALID_SESSION_ID); } switch (action) { @@ -41,9 +44,12 @@ export class IncomingEndpoint extends ApiEndpoint { case EndpointActionNames.HANDOVER: const { actionData: { targetDepartment = '' } = {} } = endpointContent; const room = await read.getRoomReader().getById(sessionId) as ILivechatRoom; - if (!room) { throw new Error(); } + if (!room || !room.isOpen) { throw new Error('Error! Invalid session Id. No active room found with the given session id'); } const { visitor: { token: visitorToken } } = room; - await performHandover(this.app, modify, read, sessionId, visitorToken, targetDepartment); + const result = await performHandover(this.app, modify, read, sessionId, visitorToken, targetDepartment); + if (!result) { + return { statusCode: HttpStatusCode.CONFLICT, data: { error: Response.NO_AGENTS_ONLINE }}; + } break; case EndpointActionNames.TRIGGER_EVENT: const { actionData: { event = null } = {} } = endpointContent; @@ -65,5 +71,7 @@ export class IncomingEndpoint extends ApiEndpoint { default: throw new Error(Logs.INVALID_ENDPOINT_ACTION); } + + return { statusCode: HttpStatusCode.OK, data: { result: Response.SUCCESS }}; } } diff --git a/enum/Http.ts b/enum/Http.ts index 4a0966e..dbd3b14 100644 --- a/enum/Http.ts +++ b/enum/Http.ts @@ -6,4 +6,5 @@ export enum Headers { export enum Response { SUCCESS = 'Your request was processed successfully', + NO_AGENTS_ONLINE = 'Handover failed! No agents online', } diff --git a/enum/Logs.ts b/enum/Logs.ts index 9a0212b..5dd401d 100644 --- a/enum/Logs.ts +++ b/enum/Logs.ts @@ -20,4 +20,5 @@ export enum Logs { HTTP_REQUEST_ERROR = 'Error occurred while sending a HTTP Request', CLOSE_CHAT_REQUEST_FAILED_ERROR = 'Error: Internal Server Error. Could not close the chat', HANDOVER_REQUEST_FAILED_ERROR = 'Error occurred while processing handover. Details', + NO_AGENTS_ONLINE = 'Handover failed! No agents online', } diff --git a/i18n/en.json b/i18n/en.json index fa36e66..f4a6491 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,5 +14,7 @@ "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_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" } diff --git a/lib/Room.ts b/lib/Room.ts index 38798b0..9a33cdd 100644 --- a/lib/Room.ts +++ b/lib/Room.ts @@ -43,11 +43,7 @@ export const closeChat = async (modify: IModify, read: IRead, rid: string) => { if (!result) { throw new Error(Logs.CLOSE_CHAT_REQUEST_FAILED_ERROR); } }; -export const performHandover = async (app: IApp, modify: IModify, read: IRead, rid: string, visitorToken: string, targetDepartmentName?: string) => { - - const handoverMessage: string = await getAppSettingValue(read, AppSetting.DialogflowHandoverMessage); - await createMessage(app, rid, read, modify, { text: handoverMessage ? handoverMessage : DefaultMessage.DEFAULT_DialogflowHandoverMessage }); - +export const performHandover = async (app: IApp, modify: IModify, read: IRead, rid: string, visitorToken: string, targetDepartmentName?: string): Promise => { const room: ILivechatRoom = (await read.getRoomReader().getById(rid)) as ILivechatRoom; if (!room) { throw new Error(Logs.INVALID_ROOM_ID); } @@ -65,13 +61,30 @@ export const performHandover = async (app: IApp, modify: IModify, read: IRead, r livechatTransferData.targetDepartment = targetDepartment.id; } + // check if any agent is online in the department where we're transferring this chat + const serviceOnline = await read.getLivechatReader().isOnlineAsync(livechatTransferData.targetDepartment); + if (!serviceOnline) { + const offlineMessage: string = await getAppSettingValue(read, AppSetting.DialogflowHandoverFailedMessage); + if (offlineMessage && offlineMessage.trim()) { + await createMessage(app, rid, read, modify, { text: offlineMessage }); + } + return false; + } + + const handoverMessage: string = await getAppSettingValue(read, AppSetting.DialogflowHandoverMessage); + await createMessage(app, rid, read, modify, { text: handoverMessage ? handoverMessage : DefaultMessage.DEFAULT_DialogflowHandoverMessage }); + const result = await modify.getUpdater().getLivechatUpdater().transferVisitor(visitor, livechatTransferData) .catch((error) => { throw new Error(`${Logs.HANDOVER_REQUEST_FAILED_ERROR} ${error}`); }); if (!result) { - const offlineMessage: string = await getAppSettingValue(read, AppSetting.DialogflowServiceUnavailableMessage); + const offlineMessage: string = await getAppSettingValue(read, AppSetting.DialogflowHandoverFailedMessage); + if (offlineMessage && offlineMessage.trim()) { + await createMessage(app, rid, read, modify, { text: offlineMessage }); + } - await createMessage(app, rid, read, modify, { text: offlineMessage ? offlineMessage : DefaultMessage.DEFAULT_DialogflowServiceUnavailableMessage }); + return false; } + return true; };