[NEW][ENTERPRISE] Auto close abandoned Omnichannel rooms (#17055)
* close/freeze automatically inactive omnichannel rooms * Apply suggestions from review * Apply suggestions from review * Apply suggestions from review * Apply suggestions from review * Set livechat rooms as waiting response after visitor's messages * Remove unnecessary console.log calls. Co-authored-by: Renato Becker <renato.augusto.becker@gmail.com>pull/17141/head
parent
c180a53698
commit
6a94e3c9e5
@ -0,0 +1,24 @@ |
||||
|
||||
import { callbacks } from '../../../callbacks'; |
||||
import { LivechatRooms } from '../../../models'; |
||||
|
||||
callbacks.add('afterSaveMessage', function(message, room) { |
||||
// skips this callback if the message was edited
|
||||
if (!message || message.editedAt) { |
||||
return message; |
||||
} |
||||
|
||||
// if the message has not a token, it was sent by the agent, so ignore it
|
||||
if (!message.token) { |
||||
return message; |
||||
} |
||||
|
||||
// check if room is yet awaiting for response
|
||||
if (typeof room.t !== 'undefined' && room.t === 'l' && room.waitingResponse) { |
||||
return message; |
||||
} |
||||
|
||||
LivechatRooms.setNotResponseByRoomId(room._id); |
||||
|
||||
return message; |
||||
}, callbacks.priority.LOW, 'markRoomNotResponded'); |
||||
@ -0,0 +1,26 @@ |
||||
import { callbacks } from '../../../../../app/callbacks/server'; |
||||
import { settings } from '../../../../../app/settings/server'; |
||||
import { setPredictedVisitorAbandonmentTime } from '../lib/Helper'; |
||||
|
||||
callbacks.add('afterSaveMessage', function(message, room) { |
||||
if (!settings.get('Livechat_auto_close_abandoned_rooms') || settings.get('Livechat_visitor_inactivity_timeout') <= 0) { |
||||
return message; |
||||
} |
||||
// skips this callback if the message was edited
|
||||
if (message.editedAt) { |
||||
return false; |
||||
} |
||||
// message valid only if it is a livechat room
|
||||
if (!(typeof room.t !== 'undefined' && room.t === 'l' && room.v && room.v.token)) { |
||||
return false; |
||||
} |
||||
// if the message has a type means it is a special message (like the closing comment), so skips
|
||||
if (message.t) { |
||||
return false; |
||||
} |
||||
const sentByAgent = !message.token; |
||||
if (sentByAgent) { |
||||
setPredictedVisitorAbandonmentTime(room); |
||||
} |
||||
return message; |
||||
}, callbacks.priority.HIGH, 'save-visitor-inactivity'); |
||||
@ -0,0 +1,87 @@ |
||||
import { SyncedCron } from 'meteor/littledata:synced-cron'; |
||||
import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; |
||||
|
||||
import { settings } from '../../../../../app/settings/server'; |
||||
import { LivechatRooms, LivechatDepartment, Users } from '../../../../../app/models/server'; |
||||
import { Livechat } from '../../../../../app/livechat/server/lib/Livechat'; |
||||
|
||||
export class VisitorInactivityMonitor { |
||||
constructor() { |
||||
this._started = false; |
||||
this._name = 'Omnichannel Visitor Inactivity Monitor'; |
||||
this.messageCache = new Map(); |
||||
this.userToPerformAutomaticClosing; |
||||
} |
||||
|
||||
start() { |
||||
this._startMonitoring(); |
||||
this._initializeMessageCache(); |
||||
this.userToPerformAutomaticClosing = Users.findOneById('rocket.cat'); |
||||
} |
||||
|
||||
_startMonitoring() { |
||||
if (this.isRunning()) { |
||||
return; |
||||
} |
||||
const everyMinute = '* * * * *'; |
||||
SyncedCron.add({ |
||||
name: this._name, |
||||
schedule: (parser) => parser.cron(everyMinute), |
||||
job: () => { |
||||
this.handleAbandonedRooms(); |
||||
}, |
||||
}); |
||||
this._started = true; |
||||
} |
||||
|
||||
stop() { |
||||
if (!this.isRunning()) { |
||||
return; |
||||
} |
||||
|
||||
SyncedCron.remove(this._name); |
||||
|
||||
this._started = false; |
||||
} |
||||
|
||||
isRunning() { |
||||
return this._started; |
||||
} |
||||
|
||||
_initializeMessageCache() { |
||||
this.messageCache.clear(); |
||||
this.messageCache.set('default', settings.get('Livechat_abandoned_rooms_closed_custom_message') || TAPi18n.__('Closed_automatically')); |
||||
} |
||||
|
||||
_getDepartmentAbandonedCustomMessage(departmentId) { |
||||
if (this.messageCache.has('departmentId')) { |
||||
return this.messageCache.get('departmentId'); |
||||
} |
||||
const department = LivechatDepartment.findOneById(departmentId); |
||||
if (!department) { |
||||
return; |
||||
} |
||||
this.messageCache.set(department._id, department.abandonedRoomsCloseCustomMessage); |
||||
return department.abandonedRoomsCloseCustomMessage; |
||||
} |
||||
|
||||
closeRooms(room) { |
||||
let comment = this.messageCache.get('default'); |
||||
if (room.departmentId) { |
||||
comment = this._getDepartmentAbandonedCustomMessage(room.departmentId) || comment; |
||||
} |
||||
Livechat.closeRoom({ |
||||
comment, |
||||
room, |
||||
user: this.userToPerformAutomaticClosing, |
||||
}); |
||||
} |
||||
|
||||
handleAbandonedRooms() { |
||||
if (!settings.get('Livechat_auto_close_abandoned_rooms')) { |
||||
return; |
||||
} |
||||
LivechatRooms.findAbandonedOpenRooms(new Date()).forEach((room) => this.closeRooms(room)); |
||||
this._initializeMessageCache(); |
||||
} |
||||
} |
||||
@ -1,11 +1,24 @@ |
||||
import { Meteor } from 'meteor/meteor'; |
||||
|
||||
import { settings } from '../../../../app/settings'; |
||||
import { checkWaitingQueue } from './lib/Helper'; |
||||
import { checkWaitingQueue, updatePredictedVisitorAbandonment } from './lib/Helper'; |
||||
import { VisitorInactivityMonitor } from './lib/VisitorInactivityMonitor'; |
||||
import './lib/query.helper'; |
||||
|
||||
const visitorActivityMonitor = new VisitorInactivityMonitor(); |
||||
|
||||
Meteor.startup(function() { |
||||
settings.onload('Livechat_maximum_chats_per_agent', function(/* key, value */) { |
||||
checkWaitingQueue(); |
||||
}); |
||||
settings.onload('Livechat_auto_close_abandoned_rooms', function(_, value) { |
||||
updatePredictedVisitorAbandonment(); |
||||
if (!value) { |
||||
return visitorActivityMonitor.stop(); |
||||
} |
||||
visitorActivityMonitor.start(); |
||||
}); |
||||
settings.onload('Livechat_visitor_inactivity_timeout', function() { |
||||
updatePredictedVisitorAbandonment(); |
||||
}); |
||||
}); |
||||
|
||||
Loading…
Reference in new issue