mod_callcenter is an inbound call queuing application that can be used for call center needs.
The callcenter dialplan application provides call center functionality by distributing calls to agents using various scenarios and rules. A score-based system is used to distribute inbound calls. A caller's score increases by 1 for every second he waits. You can add a base score to help move a caller to the front of a queue or just remove the wait time from that queue. The callcenter application also has a tiered system for creating different agent 'priorities' as needed.
A simpler alternative means of handling incoming call queues is with mod_fifo, a first-in, first-out (FIFO) queuing system.
Click to expand Table of Contents
The callcenter will use the supplied ODBC database instead of the default behavior, which is to use the internal SQLite database.
This is to specify a different name or path and name of the SQLite database. Useful to put into a ram disk for better performance.
We currently support 2 types, 'callback' and 'uuid-standby'. callback will try to reach the agent via the contact fields value. uuid-standby will try to bridge the call directly using the agent uuid.
A simple dial string can be put in here, like: user/1000@default. If using verto: ${verto_contact(1000@default)}
Define the current status of an agent. Check the Agents Status table for more information.
If the agent fails to answer calls this number of times, his status is changed to On Break automatically.
The amount of time to wait before putting the agent back in the available queue to receive another call, to allow her to complete notes or other tasks.
If the agent presses the reject button on her phone, wait this defined time amount.
If the agent is on Do Not Disturb, wait this defined time before trying him again.
If the agent does not answer the call, wait this defined time before trying him again.
If defined to true, agent state is changed to Reserved if the old state is Receiving, the call will only be sent to him if the state get's changed.
This is useful if you're manipulating agent state external to mod_callcenter. false by default.
If defined to true, we'll delete all the agents when the module is loaded. false by default.
If defined to true, we'll delete all the tiers when the module is loaded. false by default.
The strategy defines how calls are distributed in a queue. A table of different strategies can be found below.
The system will playback whatever you specify to incoming callers. You can use any type of input here that is supported by the FreeSWITCH playback system:
). (I use this to play multiple prompts after each other.)
(JB - resume editing here)
Use the record-template to save your recording wherever you would like on the filesystem. It's not uncommon for this setting to start with "$${base_dir}/recordings/". Whatever directory you choose, make sure it already exists and that FreeSWITCH has the required permissions to write to it.
This can be either 'queue' or 'system' (queue is the default). If set to system, it will add the number of seconds since the call was originally answered (or entered the system) to the caller's base score. Raising the caller's score allows them to receive priority over other calls that might have been in the queue longer but not in the system as long. If set to queue, you get the default behavior, i.e., nobody's score gets increased upon entering the queue (regardless of the total length of their call).
Can be True or False. This defines if we should apply the following tier rules when a caller advances through a queue's tiers. If False, they will use all tiers with no wait.
The time in seconds that a caller is required to wait before advancing to the next tier. This will be multiplied by the tier level if tier-rule-wait-multiply-level is set to True. If tier-rule-wait-multiply-level is set to false, then after tier-rule-wait-second's have passed, all tiers are open for calls in the tier-order and no advancement (in terms of waiting) to another tier is made.
Can be True or False. If False, then once tier-rule-wait-second is passed, the caller is offered to all tiers in order (level/position). If True, the tier-rule-wait-second will be multiplied by the tier level and the caller will have to wait on every tier tier-rule-wait-second's before advancing to the next tier.
Can be True or False. If True, callers will skip tiers that don't have agents available. Otherwise, they are be required to wait before advancing. Agents must be logged off to be considered not available.
The number of seconds before we completely remove an abandoned member from the queue. When used in conjunction with abandoned-resume-allowed, callers can come back into a queue and resume their previous position.
Can be True or False. If True, a caller who has abandoned the queue can re-enter and resume their previous position in that queue. In order to maintain their position in the queue, they must not abandoned it for longer than the number of seconds defined in 'discard-abandoned-after'.
Default to 0 to be disabled. Any value are in seconds, and will define the delay before we quit the callcenter application IF the member haven't been answered by an agent. Can be used for sending call in voicemail if wait time is too long.
Default to 0 to be disabled. The value is in seconds, and it will define the amount of time the queue has to be empty (without logged agents, on a call or not) before we disconnect all members. This principle protects kicking all members waiting if all agents are logged off by accident.
Default to 5. Any value are in seconds, and will define the length of time after the max-wait-time-with-no-agent is reached to reject new caller. This allow for kicking caller if no agent are logged in for over 5 seconds, but new caller after that 5 seconds is reached can have a lower limit.
Default to 10. The value is in seconds, and it will define the delay to wait before starting call to the next agent when using the 'ring-progressively' queue strategy.
leg_timeout callcenter的分机组里边想让分机a多振玲一会再转去b分机
Dialplan Examples
Queues
Queues can only be configured in the XML configuration. They are only loaded once.
String |
Description |
ring-all |
Rings all agents simultaneously. |
longest-idle-agent |
Rings the agent who has been idle the longest taking into account tier level. |
round-robin |
Rings the agent in position but remember last tried agent. |
top-down |
Rings the agent in order position starting from 1 for every member. |
agent-with-least-talk-time |
Rings the agent with least talk time. |
agent-with-fewest-calls |
Rings the agent with fewest calls. |
sequentially-by-agent-order |
Rings agents sequentially by tier & order. |
random |
Rings agents in random order. |
ring-progressively |
Rings agents in the same way as top-down, but keeping the previous members ringing (it basically leads to ring-all in the end). |
When a caller goes into a queue, we can add to their base score the total number of seconds they have been in the system. This enables the caller to get in front of other callers by the amount of time they have already spent waiting elsewhere.
The time-base-score param in a queue can be set as 'queue' (base score counts only the time the caller is in this queue) or 'system' (base score accounts for the total time of the call).
Agents have Status and States. The Status is the general state of the agent. Statuses are not updated by the system automatically, so they must be set or changed as needed. States are the specific state of an agent with regard to the calls in the queue. States are dynamic and are updated by the system based on the progress of a agent in a call. The reason for separating the two is so that an agent can logout (change Status to 'Logged Out') without affecting his current call State (possibly set to 'In a queue call').
If an agent changes his status to Logged Out, any active callback attempts will be halted and the queue will try to place that caller with another agent.
Status only applies to the next call. So for example, if you change user from Available to Available (On Demand) while they are in a call, they will receive one more call when the current one finishes.
Agent Status and States follow:
Agent Status:
String |
Description |
Logged Out |
Cannot receive queue calls. |
Available |
Ready to receive queue calls. |
Available (On Demand) |
State will be set to 'Idle' once the call ends (not automatically set to 'Waiting'). |
On Break |
Still Logged in, but will not receive queue calls. |
Agent State:
String |
Description |
Idle |
Does nothing, no calls are given. |
Waiting |
Ready to receive calls. |
Receiving |
A queue call is currently being offered to the agent. |
In a queue call |
Currently on a queue call. |
While an agent's State is 'Waiting', calls will be directed to them. Whenever an agent completes one of those calls, their State is set back to 'Waiting'.
This is the same as the regular 'Available' Status, except that when the call is terminated, the agent's State is set to 'Idle'. This means the agent won't receive additional calls until his State is changed to 'Waiting'.
This is used when agents call into the system and wait to receive a calls.
No Answer
If you define the max-no-answer for an agent, and that agent fails to answer that many calls, then the agent's Status will changed to 'On Break'.
Rejecting a call does not act as a 'no-answer'.
A delay can be added before calling an agent who has just rejected a call from the queue by setting 'reject_delay_time' on an agent.
An agent who is set to "do not disturb" can have a delay added before he is offered his next call by using the 'busy_delay_time' parameter on the agent.
Adapt the following dialplan to meet your needs. For example, you can change the contact info if you use this to login from different workstations, or from a number on the PSTN.
...
...
Add a new agent into the system
callcenter_config agent add [agent name] [type(Callback)]
Update Agent value
callcenter_config agent set [key(contact|status|state|type|max_no_answer|wrap_up_time|ready_time|reject_delay_time|busy_delay_time)] [agent name] [value]
Delete an agent
callcenter_config agent del [agent name]
List agents
callcenter_config agent list [agent_name]
Get uuid of the agent who is bridged to a member
callcenter_config agent get uuid [agent_name]
Add a new tier mapping an agent to a queue. Note that only Ready should be used in case of an problem.
callcenter_config tier add [queue name] [agent name] [[level]] [[position]]
Update tier value
callcenter_config tier set [key(state|level|position)] [queue name] [agent name] [value]
callcenter_config tier set state support@default 21009@default on break
Delete a tier
callcenter_config tier del [queue name] [agent name]
List tiers
callcenter_config tier list
Load a queue off the in memory xml config file
callcenter_config queue load [queue_name]
Unload a queue settings
callcenter_config queue unload [queue_name]
Reload a queue settings
callcenter_config queue reload [queue_name]
List queues settings
callcenter_config queue list
List agents with a tier associated to the specified queue. If [status] is specified, only list agents with a given status.
callcenter_config queue list agents [queue_name] [status] [state]
List callers present in the queue.
callcenter_config queue list members [queue_name]
List tiers associated to a specific queue.
callcenter_config queue list tiers [queue_name]
Return the total number of queues.
callcenter_config queue count
Return the number agents with a tier associated to the specified queue. If [status] is specified, only count agents with a given status.
callcenter_config queue count agents [queue_name] [status]
callcenter_config queue count agents support@default Available
Return the number of callers present in the queue (no of callers waiting in a queue + number of callers bridged with an agent).
callcenter_config queue count members [queue_name]
Return the number of tiers associated to the specified queue.
callcenter_config queue count tiers [queue_name]
JSON API was added in FS-8799 - Jira project doesn't exist or you don't have permission to view it. and currently it support the following commands:
Listing agents
json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"agent list"}}
json {"command": "callcenter_config", "format": "pretty", "data": {"arguments":"queue list"}}
json {"command": "callcenter_config", "data": {"arguments":"queue list agents", "queue_name":"support@default"}}
json {"command": "callcenter_config", "data": {"arguments":"queue list members", "queue_name":"support@default"}}
json {"command": "callcenter_config","data": {"arguments":"queue list tiers", "queue_name":"support@default"}}
json {"command": "callcenter_config","data": {"arguments":"member list"}}
json {"command": "callcenter_config","data": {"arguments":"tier list"}}
Export variables to the b-leg(s) of call center (the agents)
This is necessary because mod_callcenter originates B-leg calls in its own separate thread, therefore it has no access to variables set in the A-leg (as 'bridge' would).
Example usage:
Overrides the queue's default Music On Hold.
Example usage from mail list:
Adds the specified amount to the caller's base score, potential putting him in front other callers in the queue.
Caller can exit the queue by pressing this key.
Adds prefix to the Caller ID Name of the caller.
Playback specific audio, or an array of audios, to the agent prior to bridging the member.
Playback specific audio to the agent if we can't bridge him because member hangup the call just before the bridge occurs. You can play a busy tone here or a custom audio, for example:
This variable is only valid for agents in 'uuid-standby' mode. It plays the specified tone when a call is sent to the agent.
Contains the filename of the call recording if a record-template was provided in the queue's configuration. (read-only)
Contains the leg side of the call. Can be either member or agent. (read-only)
Contains the unique callcenter member uuid (Different from the member session uuid) (read-only)
Contains the member session uuid. (Different from the member_uuid) (read-only)
Contains the agent who accepted the call from the queue. (read-only)
Contains the epoch time that the agent answered the call. (read only)
Contains the epoch time that the bridge with the agent was terminated. (read-only)
Contains the epoch time that the caller joined the queue and started waiting. (read-only)
Contains the epoch time when a caller leaves the queue and aborts the call. (read-only)
Contains a boolean value indicating if this call was successfully bridged or not. We may call the agent and the member can hangup just before bridging them. (read-only)
Here are a few example events generated by mod_callcenter.
When an agent's Status changes, this event is generated with the agent's new Status.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Agent: 1000@default
CC-Action: agent-status-change
CC-Agent-Status: Available
Every time an agent's State changes, this event is generated.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Agent: 1000@default
CC-Action: agent-state-change
CC-Agent-State: Receiving
Every time a caller is presented to an agent (before he answers), this event is generated.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Agent: AgentNameHere
CC-Action: agent-offering
CC-Agent-System: single_box
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: 600165a4-f748-11df-afdd-b386769690cd
CC-Member-CID-Name: CHOUINARD MO
CC-Member-CID-Number: 4385551212
When an agent is connected, this event is generated. NOTE: the channel variables, including, for example, Event-Date-Timestamp are present as well as the callcenter values.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: bridge-agent-start
CC-Agent: AgentNameHere
CC-Agent-System: single_box
CC-Agent-UUID: 7acfecd3-ab50-470b-8875-d37aba0429ba
CC-Agent-Called-Time: 10000
CC-Agent-Answered-Time: 10009
CC-Member-Joined-Time: 9000
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: c6360976-231c-43c6-bda7-7ac4c7d1c125
CC-Member-CID-Name: Their Name
CC-Member-CID-Number: 555-555-5555
When an agent is disconnected, this event is generated. NOTE: the channel variables, including, for example, Event-Date-Timestamp are present as well as the callcenter values.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: bridge-agent-end
CC-Agent: AgentNameHere
CC-Agent-System: single_box
CC-Agent-UUID: 7acfecd3-ab50-470b-8875-d37aba0429ba
CC-Agent-Called-Time: 10000
CC-Agent-Answered-Time: 10009
CC-Bridge-Terminated-Time: 10500
CC-Member-Joined-Time: 9000
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: c6360976-231c-43c6-bda7-7ac4c7d1c125
CC-Member-CID-Name: Their Name
CC-Member-CID-Number: 555-555-5555
When an agent originate fail, this event is generated. NOTE: the channel variables, including, for example, Event-Date-Timestamp are present as well as the callcenter values.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: bridge-agent-fail
CC-Hangup-Cause: CHECK FS HANGUP CAUSE
CC-Agent: AgentNameHere
CC-Agent-System: single_box
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: c6360976-231c-43c6-bda7-7ac4c7d1c125
CC-Member-CID-Name: Their Name
CC-Member-CID-Number: 555-555-5555
This event is generated every time the queue count api is called and anytime a caller enters or leaves the queue.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: members-count
CC-Count: 1
CC-Selection: Single-Queue
Joining the queue triggers this event, allowing you to track when callers enter the queue.
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: member-queue-start
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: b77c49c2-a732-11df-9438-e7d9456f8886
CC-Member-CID-Name: CHOUINARD MO
CC-Member-CID-Number: 4385551212
This is generated when a caller leaves the queue. Different lengths of queue-specific times are reported in seconds. There are two values for CC-Cause: 'Terminated' and 'Cancel'.
'Terminated' means the call ended after talking to an agent. Here is an example:
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: member-queue-end
CC-Hangup-Cause: CHECK FS HANGUP CAUSE
CC-Cause: Terminated
CC-Agent-Called-Time: 10000
CC-Agent-Answered-Time: 10009
CC-Member-Joined-Time: 9000
CC-Member-Leaving-Time: 10100
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: b77c49c2-a732-11df-9438-e7d9456f8886
CC-Member-CID-Name: CHOUINARD MO
CC-Member-CID-Number: 4385551212
If we get a hangup from the caller before talking to an agent, the CC-Cause will be 'Cancel'. CC-Cause-Reason will contain the reason of the drop between NONE (no specific reason), TIMEOUT (caller has been waiting more than the timeout), NO_AGENT_TIMEOUT (caller has been waiting more than the no_agent_timeout), and BREAK_OUT (caller abandoned). Here's an example:
Event-Subclass: callcenter::info
Event-Name: CUSTOM
CC-Queue: support@default
CC-Action: member-queue-end
CC-Member-Joined-Time: 9000
CC-Member-Leaving-Time: 10050
CC-Cause: Cancel
CC-Cancel-Reason: TIMEOUT
CC-Member-UUID: 453324f8-3424-4322-4242362fd23d
CC-Member-Session-UUID: e260ffd0-a731-11df-9341-e7d9456f8886
CC-Member-CID-Name: Marc O Robinson
CC-Member-CID-Number: 5145551212
This a very simple script that you can use to monitor all callcenter events.
#!/usr/bin/env python
import sys
import ESL
con = ESL.ESLconnection("127.0.0.1", "8021", "ClueCon")
if not con.connected():
print("ERROR: connection failed!")
sys.exit(1)
con.events("PLAIN", "ALL")
while con.connected():
e = con.recvEventTimed(1000)
if e:
name = e.getHeader("Event-Name")
if name == 'CUSTOM':
subclass = e.getHeader("Event-Subclass")
if subclass == 'callcenter::info':
print('->>' + name + '|' + subclass + '\n')
print(e.serialize())
Lua Script to announce members position
This is the script to place in $PREFIX/scripts:
-- callcenter-announce-position.lua
-- Announce queue position to a member in a given mod_callcenter queue.
-- Arguments are, in order: caller uuid, queue_name, interval (in milliseconds).
api = freeswitch.API()
caller_uuid = argv[1]
queue_name = argv[2]
mseconds = argv[3]
if caller_uuid == nil or queue_name == nil or mseconds == nil then
return
end
while (true) do
-- Pause between announcements
freeswitch.msleep(mseconds)
members = api:executeString("callcenter_config queue list members "..queue_name)
pos = 1
exists = false
for line in members:gmatch("[^\r\n]+") do
if (string.find(line, "Trying") ~= nil or string.find(line, "Waiting") ~= nil) then
-- Members have a position when their state is Waiting or Trying
if string.find(line, caller_uuid, 1, true) ~= nil then
-- Member still in queue, so script must continue
exists = true
api:executeString("uuid_broadcast "..caller_uuid.." ivr/ivr-you_are_number.wav aleg")
api:executeString("uuid_broadcast "..caller_uuid.." digits/"..pos..".wav aleg")
end
pos = pos+1
end
end
-- If member was not found in queue, or it's status is Aborted - terminate script
if exists == false then
return
end
end
Name this script for example callcenter-announce-position.lua. This script accepts three arguments:
First, script checks for empty arguments and terminates if one is not defined. However no type checking is done. Then entering an endless loop, at start we wait before first announce. In members we get the output of callcenter_config queue list members.
Next we initialize two variables. As the resulting listing from callcenter_config does not contain any autonumbering, we will count the lines. And this is better, because members will contain members which have Aborted or Bridged state, which must not be counted.
The second variable is for checking if the member with this uuid is present in list and need announces again.
Iterating thru each line from members we first check if the member's call state is in Waiting or Trying state. The Trying state means that this member is the next which will get an agent to bridge. In queue only one member can be in Trying state. As an agent can have a long time to answer (may be more time than period we specified), member must be assured it is first in line.
Next we check that member with given uuid is present in current line and therefore we found it's position and can announce the position. Notice forth parameter of the string.find function. By default this option is false, meaning, the second parameter is interpreted as regex pattern, which in case of uuid will interfere with the "-" symbol. Setting forth parameter two true, we will get a complete character by character compare.
If uuid is found, play the position. Using uuid_broadcast we can pause moh sound and play the audio file after which moh sound will continue to play. uuid_broadcast has a forth parameter which indicate to what leg we want to play the position. Position must hear only member which initiated the call, and therefore is considered as aleg.
As the member with this uuid were found, the exists variable is set to true, for allowing this script to continue saying position.
Then we increase pos variable by one to prepare for next position. After which the exists variable is checked. If exists variable is true, meaning that member is still in queue wating for an agent, the loop will start over. If exists is false, this means that member was bridget to an agent, or disconnected from queue, or is not in queue. In this case the script must end.
We have to call this script before entering callcenter module. For example in the dialplan:
A note about uuid_broadcast. This command was corrected in FreeSWITCH Version 1.2.0-rc2+git~20120807T123541Z~c0626e6801. Before this git version, after playing file, the members call was pulled out from callcenter.
This script is started for each calling party with corresponding uuid of the call. In this case we know to whom which position must be played. So, even moh sound is the same for all members in queue, each one hears its position only.
This script can be easily modified to announce other messages to members with different interval. Using this method is not quite accurate. The first problem occurs when members are being disconnected and come back to its position. In this case other members can hear its queue position increased, but this is rarely. This script does not use score to sort members.