-
Notifications
You must be signed in to change notification settings - Fork 10.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NEW][ENTERPRISE] Automatic transfer of unanswered conversations to a…
…nother agent (#20090) * [Omnichannel] Auto transfer chat based on inactivity * Updated Livechat.transfer() method to support a new param - ignoredUserId - This will prevent the transfer to the same agent - For Manual selection method, place the chat back to the queue, if the agents doesn't respond back within the set duration * Apply suggestions from code review Co-authored-by: Renato Becker <renato.augusto.becker@gmail.com> * Apply suggestion from code review * Fix merge conflict * Apply suggestions from code review * Fix PR review. * cancel previous jobs b4 scheduling new one + minor improvements * Use a dedicated variable to read setting value. * [optimize] prevent cancelling job after each message sent * Improve codebase. * Remove unnecessary import. * Add PT-BR translations. * Fix class methods. * Improve class code. * Added final improvements to the codebase. * remove unnused import files. * Move hardcoded variables to const. Co-authored-by: Renato Becker <renato.augusto.becker@gmail.com>
- Loading branch information
1 parent
768e709
commit f7caaf2
Showing
15 changed files
with
260 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
ee/app/livechat-enterprise/server/hooks/scheduleAutoTransfer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { AutoTransferChatScheduler } from '../lib/AutoTransferChatScheduler'; | ||
import { callbacks } from '../../../../../app/callbacks/server'; | ||
import { settings } from '../../../../../app/settings/server'; | ||
import { LivechatRooms } from '../../../../../app/models/server'; | ||
|
||
let autoTransferTimeout = 0; | ||
|
||
const handleAfterTakeInquiryCallback = async (inquiry: any = {}): Promise<any> => { | ||
const { rid } = inquiry; | ||
if (!rid || !rid.trim()) { | ||
return; | ||
} | ||
|
||
if (!autoTransferTimeout || autoTransferTimeout <= 0) { | ||
return inquiry; | ||
} | ||
|
||
const room = LivechatRooms.findOneById(rid, { autoTransferredAt: 1, autoTransferOngoing: 1 }); | ||
if (!room || room.autoTransferredAt || room.autoTransferOngoing) { | ||
return inquiry; | ||
} | ||
|
||
await AutoTransferChatScheduler.scheduleRoom(rid, autoTransferTimeout as number); | ||
|
||
return inquiry; | ||
}; | ||
|
||
const handleAfterSaveMessage = async (message: any = {}, room: any = {}): Promise<any> => { | ||
const { _id: rid, t, autoTransferredAt, autoTransferOngoing } = room; | ||
const { token } = message; | ||
|
||
if (!autoTransferTimeout || autoTransferTimeout <= 0) { | ||
return message; | ||
} | ||
|
||
if (!rid || !message || rid === '' || t !== 'l' || token) { | ||
return message; | ||
} | ||
|
||
if (autoTransferredAt) { | ||
return message; | ||
} | ||
|
||
if (!autoTransferOngoing) { | ||
return message; | ||
} | ||
|
||
await AutoTransferChatScheduler.unscheduleRoom(rid); | ||
return message; | ||
}; | ||
|
||
|
||
const handleAfterCloseRoom = async (room: any = {}): Promise<any> => { | ||
const { _id: rid, autoTransferredAt, autoTransferOngoing } = room; | ||
|
||
if (!autoTransferTimeout || autoTransferTimeout <= 0) { | ||
return room; | ||
} | ||
|
||
if (autoTransferredAt) { | ||
return room; | ||
} | ||
|
||
if (!autoTransferOngoing) { | ||
return room; | ||
} | ||
|
||
await AutoTransferChatScheduler.unscheduleRoom(rid); | ||
return room; | ||
}; | ||
|
||
settings.get('Livechat_auto_transfer_chat_timeout', function(_, value) { | ||
autoTransferTimeout = value as number; | ||
if (!autoTransferTimeout || autoTransferTimeout === 0) { | ||
callbacks.remove('livechat.afterTakeInquiry', 'livechat-auto-transfer-job-inquiry'); | ||
callbacks.remove('afterSaveMessage', 'livechat-cancel-auto-transfer-job-after-message'); | ||
callbacks.remove('livechat.closeRoom', 'livechat-cancel-auto-transfer-on-close-room'); | ||
return; | ||
} | ||
|
||
callbacks.add('livechat.afterTakeInquiry', handleAfterTakeInquiryCallback, callbacks.priority.MEDIUM, 'livechat-auto-transfer-job-inquiry'); | ||
callbacks.add('afterSaveMessage', handleAfterSaveMessage, callbacks.priority.HIGH, 'livechat-cancel-auto-transfer-job-after-message'); | ||
callbacks.add('livechat.closeRoom', handleAfterCloseRoom, callbacks.priority.HIGH, 'livechat-cancel-auto-transfer-on-close-room'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import Agenda from 'agenda'; | ||
import { MongoInternals } from 'meteor/mongo'; | ||
import { Meteor } from 'meteor/meteor'; | ||
|
||
import { LivechatRooms, Users } from '../../../../../app/models/server'; | ||
import { Livechat } from '../../../../../app/livechat/server'; | ||
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; | ||
import { forwardRoomToAgent } from '../../../../../app/livechat/server/lib/Helper'; | ||
|
||
const schedulerUser = Users.findOneById('rocket.cat'); | ||
const SCHEDULER_NAME = 'omnichannel_scheduler'; | ||
|
||
class AutoTransferChatSchedulerClass { | ||
scheduler: Agenda; | ||
|
||
running: boolean; | ||
|
||
user: {}; | ||
|
||
public init(): void { | ||
if (this.running) { | ||
return; | ||
} | ||
|
||
this.scheduler = new Agenda({ | ||
mongo: (MongoInternals.defaultRemoteCollectionDriver().mongo as any).client.db(), | ||
db: { collection: SCHEDULER_NAME }, | ||
defaultConcurrency: 1, | ||
}); | ||
|
||
this.scheduler.start(); | ||
this.running = true; | ||
} | ||
|
||
public async scheduleRoom(roomId: string, timeout: number): Promise<void> { | ||
await this.unscheduleRoom(roomId); | ||
|
||
const jobName = `${ SCHEDULER_NAME }-${ roomId }`; | ||
const when = new Date(); | ||
when.setSeconds(when.getSeconds() + timeout); | ||
|
||
this.scheduler.define(jobName, this.executeJob.bind(this)); | ||
await this.scheduler.schedule(when, jobName, { roomId }); | ||
await LivechatRooms.setAutoTransferOngoingById(roomId); | ||
} | ||
|
||
public async unscheduleRoom(roomId: string): Promise<void> { | ||
const jobName = `${ SCHEDULER_NAME }-${ roomId }`; | ||
|
||
await LivechatRooms.unsetAutoTransferOngoingById(roomId); | ||
await this.scheduler.cancel({ name: jobName }); | ||
} | ||
|
||
private async transferRoom(roomId: string): Promise<boolean> { | ||
const room = LivechatRooms.findOneById(roomId, { _id: 1, v: 1, servedBy: 1, open: 1, departmentId: 1 }); | ||
if (!room?.open || !room?.servedBy?._id) { | ||
return false; | ||
} | ||
|
||
const { departmentId, servedBy: { _id: ignoreAgentId } } = room; | ||
|
||
if (!RoutingManager.getConfig().autoAssignAgent) { | ||
return Livechat.returnRoomAsInquiry(room._id, departmentId); | ||
} | ||
|
||
const agent = await RoutingManager.getNextAgent(departmentId, ignoreAgentId); | ||
if (agent) { | ||
return forwardRoomToAgent(room, { userId: agent.agentId, transferredBy: schedulerUser, transferredTo: agent }); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private async executeJob({ attrs: { data } }: any = {}): Promise<void> { | ||
const { roomId } = data; | ||
|
||
if (await this.transferRoom(roomId)) { | ||
LivechatRooms.setAutoTransferredAtById(roomId); | ||
} | ||
|
||
await this.unscheduleRoom(roomId); | ||
} | ||
} | ||
|
||
export const AutoTransferChatScheduler = new AutoTransferChatSchedulerClass(); | ||
|
||
Meteor.startup(() => { | ||
AutoTransferChatScheduler.init(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.