Mod callcenter

From FreeSWITCH Wiki
Jump to: navigation, search


Warning

Superseded by https://confluence.freeswitch.org/display/FREESWITCH/mod_callcenter

 


mod_callcenter is a call queuing application that can be used for call center needs.

callcenter <queue name>

Contents

Description

The Callcenter application provides call center functionality by distributing calls to agents using different scenarios and rules. A score-based system is used to distribute those call. 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 tiered system for creating different agent 'priorities' as needed.

An alternative means of handling incoming call queues is with mod_fifo, a first-in, first-out (FIFO) queuing system.

Configuration

Settings

odbc-dsn

The callcenter will use the supplied ODBC database instead of the default behavior, which is to use the internal SQLite database.

dbname

This is to specify a different name or name and path of the SQLite database. Useful to put into a ram disk for better performance.

Agent options

type

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 directly bridge the call using the agent uuid.

contact

A simple dial string can be put in here, like: user/1000@default

status

Define the current status of an agent. Check the Agents Status table for more informations

max-no-answer

If the agent reach this number of consecutive no answer, his status is changed to On Break automatically.

wrap-up-time

Allow an agent to have a delay when finishing a call before receiving another one

reject-delay-time

If the agent press the reject on their phone, we wait this defined time amount.

busy-delay-time

If the agent is on do not disturb, we wait this defined time before trying him again.

no-answer-delay-time

If the agent does not answer the call, we wait this defined time before trying him again.

Queue options

strategy

The strategy defines how calls are distributed in a queue. A table of different strategies can be found below.

moh-sound

The system will playback whatever you specify to incoming calls. You can use any type of input here that is supported by the FreeSWITCH playback system:

  1. A direct path to a wav file will play in loop indefinitely.
  2. The local stream, e.g., (ex:local_stream://moh) or use $${hold_music} as defined in the default configuration.
  3. The FreeSWITCH phrase system, e.g., (ex:phrase:my-special-phrase). (I use this to play multiple prompts after each other.)
  4. A tone stream as with ringing, e.g., (ex:tone_stream://${us-ring};loops=-1).

announce-sound

Can be exact path of the file you need to play as a periodic announcement. This parameter allows you to play an announcement in between the music being played. Can be used to play periodic announcements like "Your call is important to us... blah blah." The frequency can be controlled by the next parameter.

announce-frequency

Number to be defined in seconds. The announce-sound will play at intervals defined in this parameter.

record-template

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.

time-base-score

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).

tier-rules-apply

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.

tier-rule-wait-second

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.

tier-rule-wait-multiply-level

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.

tier-rule-no-agent-no-wait

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.

discard-abandoned-after

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.

abandoned-resume-allowed

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'.

max-wait-time

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.

max-wait-time-with-no-agent

Default to 0 to be disabled. Any value are in seconds, and will define the length of time a queue be empty of available (on a call or not) before we disconnect members. This allow to have member leave the callcenter app if no agents are available, but only after a certain time. This principle protect kicking all member waiting if all agents are logged off by accident.

max-wait-time-with-no-agent-time-reached

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.

callcenter.conf.xml example

<configuration name="callcenter.conf" description="CallCenter">
 
  <settings>
      <!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
      <!--<param name="dbname" value="/dev/shm/callcenter.db"/>-->
  </settings>
 
  <queues>
    <queue name="sales@default">
      <param name="strategy" value="agent-with-least-talk-time"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <!--<param name="record-template" value="$${base_dir}/recordings/sales/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
      <param name="time-base-score" value="queue"/>
      <param name="tier-rules-apply" value="false"/>
      <param name="tier-rule-wait-second" value="300"/>
      <param name="tier-rule-wait-multiply-level" value="true"/>
      <param name="tier-rule-no-agent-no-wait" value="false"/>
      <param name="discard-abandoned-after" value="14400"/>
      <param name="max-wait-time" value="0"/>
      <param name="max-wait-time-with-no-agent" value="120"/>
 
    </queue>
    <queue name="support@default">
      <param name="strategy" value="longest-idle-agent"/>
      <param name="moh-sound" value="$${hold_music}"/>
      <!--<param name="record-template" value="$${base_dir}/recordings/support/${strftime(%Y-%m-%d-%H-%M-%S)}.${destination_number}.${caller_id_number}.${uuid}.wav"/>-->
      <param name="time-base-score" value="system"/>
      <param name="tier-rules-apply" value="false"/>
      <param name="tier-rule-wait-second" value="300"/>
      <param name="tier-rule-wait-multiply-level" value="true"/>
      <param name="tier-rule-no-agent-no-wait" value="false"/>
      <param name="discard-abandoned-after" value="60"/>
      <param name="abandoned-resume-allowed" value="false"/>
      <param name="max-wait-time" value="0"/>
      <param name="max-wait-time-with-no-agent" value="120"/>
    </queue>
  </queues>
 
<!-- WARNING: Configuration of XML Agents will be updated into the DB upon restart. -->
<!-- WARNING: Configuration of XML Tiers will reset the level and position if those were supplied. -->
<!-- WARNING: Agents and Tiers XML config shouldn't be used in a multi FS shared DB setup. -->
 
  <agents>
    <agent name="1000@default" type="callback" contact="[call_timeout=10]user/1000@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="10" busy-delay-time="60" />
    <!-- If you would like to set the Caller ID name, for whatever reason notice below. -->
    <agent name="1001@default" type="callback" contact="[origination_caller_id_name='Queue Caller',call_timeout=10]user/1001@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="10" busy-delay-time="60" />
  </agents>
 
  <tiers>
    <!-- If no level or position is provided, they will default to 1.  You should do this to keep db value on restart. -->
    <!-- agent 1000 will be in both the sales and support queues -->
    <tier agent="1000@default" queue="sales@default" level="1" position="1"/>
    <tier agent="1000@default" queue="support@default" level="1" position="1"/>
    <!-- agent 1001 will only be in the support queue -->
    <tier agent="1001@default" queue="support@default" level="1" position="1"/>
  </tiers>
 
</configuration>

Dialplan Examples

Put a caller into a queue

<action application="callcenter" data="support@default"/>

Time of Day / Voicemail timeout Example

<condition field="destination_number" expression="^3000$" break="on-false"/>
<!-- Mon-Fri, 9am-5pm -->
<condition wday="2-6" time-of-day="09:00-17:00" break="on-true">
  <!-- limit 3 calls to this destination number per 1 second, otherwise give congestion message -->
  <action application="limit" data="hash inbound ${destination_number} 3/1 !NORMAL_CIRCUIT_CONGESTION"/>
  <!-- play a message before entering the queue. -->
  <action application="playback" data="$${sounds_dir}/greeting.wav"/>
  <!-- hangup after successful bridge to agent -->
  <action application="set" data="hangup_after_bridge=true"/>
  <!-- queue caller -->
  <action application="callcenter" data="queue@default"/>
  <!-- if no agent was reached and using max-wait-time - send to general voicemail -->
  <action application="playback" data="$${sounds_dir}/queue_voicemail.wav"/>
  <action application="answer"/>
  <action application="set" data="skip_greeting=true"/>
  <action application="set" data="skip_instructions=true"/>
  <action application="voicemail" data="default voicemail 2001"/>
  <action application="hangup"/>
</condition>
<condition>
  <action application="limit" data="hash inbound ${destination_number} 3/1 !NORMAL_CIRCUIT_CONGESTION"/>
  <!-- outside business hours - play message and send to general voicemail -->
  <action application="playback" data="$${sounds_dir}/tod_voicemail.wav"/>
  <action application="answer"/>
  <action application="set" data="skip_greeting=true"/>
  <action application="set" data="skip_instructions=true"/>
  <action application="voicemail" data="default voicemail 2001"/>
  <action application="hangup"/>
</condition>

Queues

Queues can only be configured in the XML configuration. They are only loaded once.

Distribution Strategy

StringDescription
ring-allRings all agents simultaneously.
longest-idle-agentRings the agent who has been idle the longest.
round-robinRings the agent in position but remember last tried agent.
top-downRings the agent in order position starting from 1 for every member.
agent-with-least-talk-timeRings the agent with least talk time.
agent-with-fewest-callsRings the agent with fewest calls.
sequentially-by-agent-orderRings agents sequentially by tier & order.
randomRings agents in random order

Time base score

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.

time-base-score param in a queue can be set as 'queue' or 'system'

Agents

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.

Agent Status and States follow:

Agent Status:

StringDescription
Logged OutCannot receive queue calls.
AvailableReady to receive queue calls.
Available (On Demand)State will be set to 'Idle' once the call ends (not automatically set to 'Waiting').
On BreakStill Logged in, but will not receive queue calls.

Agent State:

StringDescription
IdleDoes nothing, no calls are given.
WaitingReady to receive calls.
ReceivingA queue call is currently being offered to the agent.
In a queue callCurrently on a queue call.

Type

Callback

Available

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'.

Available (On Demand)

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'.

uuid-standby

This is used when agents call into the system and wait to receive a calls.

Sample Dialplan to use this function

<extension>
  <condition field="destination_number" expression="^(4099)$">
    <action application="set" data="transfer_after_bridge=4099"/> <!-- Remove this if you just want to get a single call 
    <action application="sleep" data="300"/> <!-- Small delay for safety needs -->
    <action application="set" data="res=${callcenter_config(agent set uuid ${caller_id_number}@${domain_name} '${uuid}')}" />
    <action application="set" data="res=${callcenter_config(agent set type ${caller_id_number}@${domain_name} 'uuid-standby')}" />
    <action application="set" data="res=${callcenter_config(agent set status ${caller_id_number}@${domain_name} 'Available (On Demand)')}" />
    <action application="set" data="res=${callcenter_config(agent set state ${caller_id_number}@${domain_name} 'Waiting')}" />
    <action application="set" data="cc_warning_tone=tone_stream://%(200,0,500,600,700)"/>
    <action application="answer" />
    <action application="playback" data="$${hold_music}"/>
 
  </condition>
</extension>

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 Calls

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.

Do not disturb

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.

Example Dialplan

Agent Login/Logout Example

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.

<extension name="agent_login">
  <condition field="destination_number" expression="^agent-login$">
    <action application="set" data="res=${callcenter_config(agent set status ${caller_id_number}@${domain_name} 'Available')}" />
    <action application="answer" data=""/>
    <action application="sleep" data="500"/>
    <action application="playback" data="ivr/ivr-you_are_now_logged_in.wav"/>
    <action application="hangup" data=""/>
  </condition>
</extension>
 
<extension name="agent_logoff">
  <condition field="destination_number" expression="^agent-logoff$">
    <action application="set" data="res=${callcenter_config(agent set status ${caller_id_number}@${domain_name} 'Logged Out')}" />
    <action application="answer" data=""/>
    <action application="sleep" data="500"/>
    <action application="playback" data="ivr/ivr-you_are_now_logged_out.wav"/>
    <action application="hangup" data=""/>
  </condition>
</extension>

API Commands

callcenter_config

agent

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]


tier

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]

Delete a tier

callcenter_config tier del [queue name] [agent name]

List tiers

callcenter_config tier list

queue

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]

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]

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]

Variables

cc_export_vars

Export variables to the b-leg(s) of call center (the agents)

Example usage:

<action application="set" data="hold_music=local_stream://example_moh"/>
<action application="set" data="origination_caller_id_name=Call Center"/>
<action application="set" data="origination_caller_id_number=9000"/>
<action application="set" data="cc_export_vars=hold_music,origination_caller_id_name,origination_caller_id_number"/>
<action application="callcenter" data="9000@callcenter"/>

cc_moh_override

Overrides the queue's default Music On Hold.

cc_base_score

Adds the specified amount to the caller's base score, potential putting him in front other callers in the queue.

cc_exit_keys

Caller can exit the queue by pressing this key.

cc_outbound_cid_name_prefix

Adds prefix to the Caller ID Name of the caller.

cc_warning_tone

This variable is only valid for agents in 'uuid-standby' mode. It plays the specified tone when a call is sent to the agent.

cc_record_filename

Contains the filename of the call recording if a record-template was provided in the queue's configuration. (read-only)

cc_side

Contains the leg side of the call. Can be either member or agent. (read-only)

cc_member_uuid

Contains the unique callcenter member uuid (Different from the member session uuid) (read-only)

cc_member_session_uuid

Contains the member session uuid. (Different from the member_uuid) (read-only)

cc_agent

Contains the agent who accepted the call from the queue. (read-only)

cc_queue_answered_epoch

Contains the epoch time that the agent answered the call. (read only)

cc_queue_terminated_epoch

Contains the epoch time that the bridge with the agent was terminated. (read-only)

cc_queue_joined_epoch

Contains the epoch time that the caller joined the queue and started waiting. (read-only)

cc_queue_canceled_epoch

Contains the epoch time when a caller leaves the queue and aborts the call. (read-only)

Events

NOTE: these events might change. We are in very early development.

Actions

Here are a few example events generated by mod_callcenter.

agent-status-change

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

agent-state-change

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

agent-offering

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

bridge-agent-start

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

bridge-agent-end

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

bridge-agent-fail

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


members-count

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

member-queue-start

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

member-queue-end

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

Sample Script

Python

Simple script I use to monitor all callcenter events.

#!/usr/bin/env python
 
import string
from ESL import *
 
con = ESLconnection("localhost","8021","ClueCon")
 
if con.connected:
    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:

-- Initialization of variables
api = freeswitch.API()
-- First argument is caller uuid.
caller_uuid = argv[1]
if caller_uuid==nil then return end
-- Second argument is callcenter name.
queue_name = argv[2]
if queue_name==nil then return end
-- Third argument is repeating interval in miliseconds.
mseconds = argv[3]
if mseconds==nil then return end
 
while (true) do
	freeswitch.msleep(mseconds) -- Pause before announcing position
	members = api:executeString("callcenter_config queue list members "..queue_name)
	pos=1 -- Variable to count position 
	exists=false -- Variable to allow script termination when member leaves queue 
	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
				exists=true -- Member still in queue so script must continue
				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 exists==false then return end -- If member was not found in queue, or it's status is Aborted - terminate script
end

Name this script for example callcenter-announce-position.lua. This script accepts three arguments:

  1. UUID of the call.
  2. Callcenter's queue name.
  3. How periodically to announce in miliseconds.

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:

<extension name="callcenter-example">
	<condition field="destination_number" expression="^callcenter$" break="on-false">
		<!-- limit 3 calls to this destination number per 1 second, otherwise give congestion message -->
		<action application="limit" data="hash inbound ${destination_number} 3/1 !NORMAL_CIRCUIT_CONGESTION"/>
	</condition>
	<condition>
		<!-- play a message before entering the queue. -->
		<action application="playback" data="ivr/ivr-welcome.wav"/>
		<action application="playback" data="ivr/ivr-one_moment_please.wav"/>
		<!-- hangup after successful bridge to agent -->
		<action application="set" data="hangup_after_bridge=true"/>
		<!-- queue caller -->
		<action application="set" data="result=${luarun(callcenter-announce-position.lua ${uuid} example@default 10000)}"/>
		<action application="callcenter" data="example@default"/>
		<!-- if no agent was reached and using max-wait-time - send to general voicemail -->
		<action application="playback" data="ivr/ivr-please_state_your_name_and_reason_for_calling2.wav"/>
		<action application="answer"/>
		<action application="set" data="skip_greeting=true"/>
		<action application="set" data="skip_instructions=true"/>
		<action application="voicemail" data="default ${domain_name} callcenter"/>
		<action application="hangup"/>
	</condition>
</extension>

A note about uuid_broadcast. This command was corrected in FreeSWITCH Version 1.2.0-rc2+git~20120807T123541Z~c0626e6801 (1.2.0-rc2. 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.

See Also