Skip to content
Closed
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
10 changes: 8 additions & 2 deletions SalesforceLiveAgentApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,14 @@ export class SalesforceLiveAgentApp
await postMessageClassInitiate.exec();
}

public async executeOnRoomUserTyping(data: IRoomUserTypingContext, read: IRead, http: IHttp, persistence: IPersistence): Promise<void> {
const onUserTypingHandler = new OnUserTypingHandler(this, data, read, http, persistence);
public async executeOnRoomUserTyping(
data: IRoomUserTypingContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify,
): Promise<void> {
const onUserTypingHandler = new OnUserTypingHandler(this, data, read, http, persistence, modify);
await onUserTypingHandler.exec();
}

Expand Down
2 changes: 1 addition & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"IRoomUserTyping",
"IUIKitLivechatInteractionHandler"
],
"commitHash": "29fb3e0d0ff0e996e4770fd1d7a44f4cfb65d938"
"commitHash": "6e60ce77d18dcac939d5250843b42f83ce3242e2"
}
12 changes: 10 additions & 2 deletions helperFunctions/LivechatMessageHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ import { ErrorLogs } from '../enum/ErrorLogs';
import { getAppSettingValue } from '../lib/Settings';
import { getRoomAssoc, retrievePersistentData } from './PersistenceHelpers';

export async function sendLCMessage(read: IRead, modify: IModify, room: IRoom, messageText: string, sender: IUser, disableInput?: boolean) {
export async function sendLCMessage(
read: IRead,
modify: IModify,
room: IRoom,
messageText: string,
sender: IUser,
disableInput?: boolean,
): Promise<string | undefined> {
try {
const messageBuilder = modify.getCreator().startMessage();
const message: IMessage = {
Expand Down Expand Up @@ -43,7 +50,8 @@ export async function sendLCMessage(read: IRead, modify: IModify, room: IRoom, m
};
}
messageBuilder.setData(message);
await modify.getCreator().finish(messageBuilder);
const messageId = await modify.getCreator().finish(messageBuilder);
return messageId;
} catch (error) {
throw new Error(error);
}
Expand Down
16 changes: 14 additions & 2 deletions helperFunctions/TimeoutHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@ import { AppSettingId } from '../enum/AppSettingId';
import { getRoomAssoc, retrievePersistentData, updatePersistentData } from '../helperFunctions/PersistenceHelpers';
import { getAppSettingValue } from '../lib/Settings';

/**
* 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 timer stops when the customer sends a message and starts again from 0 on the next agent's message.
*
* The warning value must be shorter than the time-out value (we recommend at least 30 seconds).
*/
export const handleTimeout = async (app: IApp, message: IMessage, read: IRead, http: IHttp, persistence: IPersistence, modify: IModify) => {
if (message.room.type !== 'l' || (message.customFields && message.customFields.idleTimeoutConfig)) {
if (message.room.type !== 'l' || message.customFields?.idleTimeoutConfig) {
return;
}

const salesforceBotUsername: string = await getAppSettingValue(read, AppSettingId.SALESFORCE_BOT_USERNAME);
const assoc = getRoomAssoc(message.room.id);
const { chasitorIdleTimeout, sneakPeekEnabled } = await retrievePersistentData(read, assoc);

if (chasitorIdleTimeout && chasitorIdleTimeout.isEnabled) {
if (chasitorIdleTimeout?.isEnabled) {
/**
* 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.
Expand Down Expand Up @@ -49,6 +58,8 @@ export const handleTimeout = async (app: IApp, message: IMessage, read: IRead, h
}

if (sessionTimeoutHandler === 'app') {
await updatePersistentData(read, persistence, assoc, { isIdleSessionTimerScheduled: false, idleSessionTimerId: '' });
await modify.getScheduler().cancelJobByDataQuery({ rid: message.room.id, taskType: 'sessionTimeout' });
await scheduleTimeOut(message, read, modify, persistence, timeoutTime, app, assoc);
}

Expand Down Expand Up @@ -118,6 +129,7 @@ async function scheduleTimeOut(
when: new Date(new Date().getTime() + idleTimeoutTimeoutTime * 1000),
data: { rid, taskType: 'sessionTimeout' },
};
console.log(`>>> SCHEDULE NEW TIMEOUT AT: ${new Date(new Date().getTime() + idleTimeoutTimeoutTime * 1000)}`);
const jobId = await modify.getScheduler().scheduleOnce(task);
await updatePersistentData(read, persistence, assoc, { isIdleSessionTimerScheduled: true, idleSessionTimerId: jobId });
}
33 changes: 19 additions & 14 deletions lib/OnUserTypingHandler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IHttp, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors';
import { IHttp, IPersistence, IRead, IModify } from '@rocket.chat/apps-engine/definition/accessors';
import { IMessage } from '@rocket.chat/apps-engine/definition/messages';
import { IApp } from '@rocket.chat/apps-engine/definition/IApp';
import { ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat';
import { IRoomUserTypingContext } from '@rocket.chat/apps-engine/definition/rooms';
Expand All @@ -7,6 +8,8 @@ import { ErrorLogs } from '../enum/ErrorLogs';
import { getRoomAssoc, retrievePersistentData } from '../helperFunctions/PersistenceHelpers';
import { chasitorSneakPeak, chasitorTyping } from '../helperFunctions/SalesforceAPIHelpers';
import { getAppSettingValue } from '../lib/Settings';
import { handleTimeout } from '../helperFunctions/TimeoutHelper';
import { sendLCMessage } from '../helperFunctions/LivechatMessageHelpers';

export class OnUserTypingHandler {
constructor(
Expand All @@ -15,6 +18,7 @@ export class OnUserTypingHandler {
private read: IRead,
private http: IHttp,
private persistence: IPersistence,
private modify: IModify,
) {}

public async exec() {
Expand Down Expand Up @@ -45,25 +49,26 @@ export class OnUserTypingHandler {
const assoc = getRoomAssoc(this.data.roomId);
const { persistentAffinity, persistentKey, sneakPeekEnabled } = await retrievePersistentData(this.read, assoc);

const user = await this.read.getUserReader().getByUsername('guest-2');

if (persistentAffinity !== null && persistentKey !== null) {
// reset the app timer when user typing handler is called
const messageId = await sendLCMessage(this.read, this.modify, room, 'user_typing', user, false);
const message = await this.read.getMessageReader().getById(messageId as string);
await handleTimeout(this.app, message as IMessage, this.read, this.http, this.persistence, this.modify);

if (sneakPeekEnabled) {
if (this.data.data.text || this.data.data.text === '') {
await chasitorSneakPeak(this.http, salesforceChatApiEndpoint, persistentAffinity, persistentKey, this.data.data.text)
.then(async () => {
// ChasitorSneakPeak API Success
})
.catch((error) => {
await chasitorSneakPeak(this.http, salesforceChatApiEndpoint, persistentAffinity, persistentKey, this.data.data.text).catch(
(error) => {
console.error(ErrorLogs.CHASITOR_SNEAKPEEK_API_CALL_FAIL, error);
});
},
);
}
} else {
await chasitorTyping(this.http, salesforceChatApiEndpoint, persistentAffinity, persistentKey, this.data.typing)
.then(async () => {
// ChasitorTyping/ChasitorNotTyping API Success
})
.catch((error) => {
console.error(ErrorLogs.CHASITOR_TYPING_API_CALL_FAIL, error);
});
await chasitorTyping(this.http, salesforceChatApiEndpoint, persistentAffinity, persistentKey, this.data.typing).catch((error) => {
console.error(ErrorLogs.CHASITOR_TYPING_API_CALL_FAIL, error);
});
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions lib/PostMessageClassInitiateHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export class PostMessageClassInitiate {

const assoc = getRoomAssoc(this.message.room.id);

if (text === 'customer_idle_timeout') {
const sessionTimeoutHandler: string = await getAppSettingValue(this.read, AppSettingId.TIMEOUT_HANDLER);
const isAppTimeoutHandler = sessionTimeoutHandler === 'app';

if (text === 'customer_idle_timeout' && !isAppTimeoutHandler) {
if (roomCustomFields && roomCustomFields.isHandedOverFromDialogFlow === true) {
await this.modify.getUpdater().getLivechatUpdater().closeRoom(this.message.room, 'Chat closed due to timeout');
await updateRoomCustomFields(this.message.room.id, { customerIdleTimeout: true }, this.read, this.modify);
Expand All @@ -52,7 +55,7 @@ export class PostMessageClassInitiate {

handleTimeout(this.app, this.message, this.read, this.http, this.persistence, this.modify);

if (this.message && this.message.id) {
if (this.message?.id) {
const { salesforceAgentName } = await retrievePersistentData(this.read, assoc);
const user = await this.read.getUserReader().getByUsername(salesforceBotUsername);
const msgExtender = this.modify.getExtender().extendMessage(this.message.id, user);
Expand Down
Loading