FreeSwitch / FusionPBX support

Uniloader can work with FreeSwitch to act as an adaptor between its 'mod_callcenter' features and QueueMetrics. In particular, when using FusionPBX, events are divided by 'domain' as to be able to feed them to multiple QueueMetrics Live systems, one for each tenant who subscribed the service.

General overview

Uniloader is able to build a synthesized queue_log file out of a stream of events flowing from a FreeSwitch system. This queue_log file can then be uploaded by a regular Uniloader process to a single QueueMetrics instance, or can be split by tenant and pushed to multiple, separate QueueMetrics Live instances.

Agent state is controlled by QueueMetrics by sending ESL commands to implement agent actions (eg. log-on or log-off).

fsw_process.png

In order to enable this feature, you need to run two copies of Uniloader in parallel, as different services:

  • one runs uniloader fsw: it connects to FreeSwitch over ESL, reads events from 'mod_callcenter' and generates a queue_log file

  • another one runs uniloader upload: it reads the generated queue_log file and uploads it to one or more QueueMetrics systems, splitting data as appropriate between different tenants

The system was designed to be run as two different services because log generation is based on events streamed in real-time, so the idea is that the service is run at all times when FreeSwitch is active. Upload also happens in real-time, but it can be restarted with no data loss if you need to change its configuration - e.g. adding or removing a tenant.

You may also want to run a third service with Uniloader in AudioVault mode, setting it in multi-tenant mode, as to make audio recordings created in FusionPBX available from the QueueMetrics GUI - see AudioVault.

Automatic outbound tracking

Uniloader performs automatic outbound tracking, that is, any call between extensions that is not handled on a queue will be tracked as an outbound call. They will be tracked on a queue named q-outbound and agents will be logged on to it before the call starts and logged off after the call ends - so you will see them logged on to that queue only when a call is happening.

It is possible to control which calls are tracked - or, if you prefer, which calls are hidden - by setting one of more of the command line options: --outbound-include-caller, --outbound-exclude-caller, --outbound-include-callee and --outbound-exclude-callee, each of which takes a regular expression.

For example, you might want to track only outbound calls where the dialed number starts with zero, if you knew that on your PBX all numbers beginning with 0 are routed through an outgoing SIP trunk, making them true external calls.

So you would use:

uniloader fsw .... --outbound-include-callee '^0.+'

If instead you do not want any calls to be tracked, you could use:

uniloader fsw .... --outbound-exclude-caller '^.*'

As this regexp matches any possible caller extension, no automatic outbound calls will be logged for this case.

The numbers being matched are the actual extensions or telephone numbers being used - so even if the caller might in the end be logged as Agent/1234, the caller check will try matching only extension 1234.

When developing a regular expression, remember that Uniloader can help you nail it - see Regexp tester. Also remember that in order to be passed as a command-line parameter, the regexp must be properly shell-encoded - usually enclosing it in single-brackets is enough. To avoid mistakes, the actual regexps in use (if any) are printed when uniloader fsw starts. If unset, they will be printed as "All", as all numbers would be valid.

You can specify multiple conditions at once:

  • If you specify conditions for both caller and callee extensions, they both must match for the call to be logged.

  • For any kind of extension, if you specify both 'include' and 'exclude' conditions, first 'include' is applied, and then 'exclude'. So if your 'include' condition was ^1.. and 'exclude' was ^19., extension "132" would be tracked, but "192" would not.

The conditions specified apply to all tenants at once.

QueueMetrics outbound tracking

In QM mode, an outbound call is started though the QueueMetrics GUI. This way the agent can specify not only the extension to be dialed, but also the "outbound queue" on which it should be placed. A call is then sent to the agent and, when they pick it up, the actual outbound call is started.

This gives you two advantages:

  • The agent can be more granular in specifying why they are calling someone - if the outbound queue is called "SalesOut" the reason is different from "Support" - as you would do with incoming queues

  • You can use AMO (Automatic Manual Outbound) that works as a very simple, one-click dialer, with recall rules, scheduling, etc.

When a call happens on Active Outbound, the agent is supposed to be logged in to an existing queue with the correct name. So we suggest creating those queues on Fusion as placeholders and then not sending any calls to them.

The details of how a call should be placed are specified in the QueueMetrics documentation. Uniloader just applies them.

Automatic music-on-hold tracking

On any tracked call, be it inbound or outbound, music on hold events are tracked in real-time and visible on QueueMetrics. You do not have to configure anything - it just happens.

Call actions

A supervisor with the correct grants can use Queuemetrics to:

  • hang-up existing calls, whether they are connected or waiting on a queue

  • manually transfer existing calls

  • start a spy, whisper or barge-in session on any call

These features are controlled by QueueMetrics and require no configuration on Uniloader.

Agent status sync

It is possible at any time to ask Uniloader to query the current status of all agents on Freswitch and write it out. This is helpful if:

  • Your agent status appears to be aout of sync, and

  • You have long-standing agents and they are not visible on QM until they take their first call in the morning

By creating a cron-job that runs this soon after midnight, you can have Uniloader rewrite the current status of all agents.

This is similar to Asterisk queue-replicate.

To have Uniloader replicate all events, it must be running in fsw mode - an then you launch it from a shell like:

$ uniloader fs-queue sync --host 127.0.0.1 --port 8021 --auth ClueCon

2025/05/06 18:06:34 Asking for a queue refresh on 127.0.0.1:8021 with pass '*******'
2025/05/06 18:06:35 Event was sent in 34 ms - all went well

This actually inserts a request on the FreeSwitch event bus, so that all agent status is written to the queue_log file by uniloader fsw.

System diagnostics

It is possible to ask Uniloader to print the current status of all agents and all ongoing calls. This works by inserting an event on the Freeswitch event bus.

$ uniloader fs-queue daemon-status

2025/05/06 18:12:11 Asking Uniloader Fsw on 127.0.0.1:8021 with pass '*******' to log its status
2025/05/06 18:12:11 Event was sent in 58 ms - all went well

And then on the service’s output (usually available via journalctl -u uniloader-fsw or a similar command) you would see an output line for each agent session and open call:

2025/05/06 18:12:11 ========== Γνῶθι σεαυτόν - NOSCE TE IPSVM ===========
2025/05/06 18:12:11 Open Calls: 1 - Open agents: 3 (waiting for tiers: 0)
2025/05/06 18:12:11 Ag:b03660af-0b21-4bb0-833f-3791b8ec021c C:user/997@tenant1.server.my Available,Waiting Qs:NONE
2025/05/06 18:12:11 Ag:c476c911-6280-4e89-8519-67f0db15c854 C:user/998@tenant1.server.my Available,Waiting Qs:401@tenant1.server.my:0-0
2025/05/06 18:12:11 Ag:e5dc2fbc-6526-4f1f-8389-642901876a0e C:user/999@tenant1.server.my Available,Waiting Qs:400@tenant1.server.my:0-0
2025/05/06 18:12:11 C:4f67a(ma)-Q=OUTBOUND@tenant1.server.my-A=Agent/998@tenant1.server.my-r=40915-n:
2025/05/06 18:12:11 -----------------  That's all folks -----------------
  • For each agent you can see their current id, user, status and state, and a list of queues they are known members of.

  • For each call, you can see their current queue and agent

Agent and queue translation

With FusionPBX, uniloader fsw can be given access to the FusionPBX’s Postgres database, in order to decode queue and agent names (that appear as UUIDs) into their own tenant and extension.

For example, a queue with id 75082016-6394-4738-b896-b9121c060612 that belongs to domain (tenant) 'abc.example.com' where it is reachable under extension 200 will be logged as abc.example.com-200. An agent working from extension 300 under the same tenant will appear as Agent/abc.example.com-300.

On current versions of FusionPBX (5.x), some queue events are logged with their UUID and some use the format agent@domain. Starting from Uniloader 21.04.5 onwards, such events are recognized and translated trasparently. If you get a crash that says invalid input syntax for type uuid: "1600@tenant.server.com", you have an older Uniloader that needs to be upgraded.

When uploading data, you can then use the splitter feature to send only data for domain 'abc.example.com' to a QueueMetrics Live instance named (for example) 'my.queuemetrics-live.com/customer-abc', where the data mentioned above appears as queue code '200' and agent code 'Agent/300'.

While the settings work for FusionPBX, it is possible to override the database and the queries used by setting sql-agent and sql-queue. For example, if we run:

uniloader fsw --queuelog my_log.txt --ps-uri 127.0.0.1/fusionpbx
    --shorten-domain 1  --sql-agent 'SELECT 1000 as TENANT, $1 as AGENT'

we will always get back the same agent and a tenant "1000". If your logic is on MySQL, we could get the same result by issuing:

uniloader fsw --queuelog my_log.txt --ps-uri "10.10.5.10/sugarcrm"
        --ps-database "mysql" --ps-login queuemetrics --ps-pwd javadude
        --shorten-domain 1
        --sql-agent 'SELECT "abcd" as TENANT, ? as AGENT'

This will return the same agent with a tenant called "abcd".

Any query that receives a single input value and returns two columns will work. The first column is the tenant, while the second one is the decoded queue or agent id.

Disabling database translation

Database translation is needed from FusionPBX 4.4 onwards. On earlier versions, you may want to switch it off by explicitly setting the ps-uri option to blank or a dash, as in --ps-uri "-".

Writing custom queries

MySQL and Postgres use a different format for allowed SQL strings:

  • The placeholder used in Postgres is $1 while Mysql uses ?.

  • The placeholder in Postgres can be used multiple times. On MySQL you have only one, so you need to write a Common Table Expression instead, as shown in the examples below.

  • Literal strings must be quoted with a single quote in Postgres ' while MySQL also allows a double-quote ".

The names of fileds don’t really matter, as Uniloder will take the first one as the tenant and the second one as the queue or agent.

Examples

Split an entry like 1600@tenant.server.com into queue and tenant (Postgres):

SELECT split_part($1, '@', 2) as TENANT, split_part($1, '@', 1) as QUEUE

Split an entry like 1600@tenant.server.com into queue and tenant (MySQL). As we cannot reuse the placeholder, we sport the Commont Table Expression syntax, where we create a literal table cte with a single columns named v and only one row of data that contain our value:

WITH cte (v) as (values ( ? ))
SELECT substring_index(v, '@', -1) as TENANT, substring_index(v, '@', 1) as QUEUE
FROM cte

Given an agent code like 1000, add a fixed tenant "ABCD" (MySQL):

SELECT "abcd" as TENANT, ? as AGENT
Deprecated: ADDMEMBER mode
With Uniloader 25.05, agent login is always logged on a queue-by-queue basis. Queue-by-queue login is availabe from the QUieeiMetrics GUI, while the systems remains compatible with any change you make on the FusionPBX GUI. So this option should not be used.

FusionPBX does not allow the selection of which queues an agent is supposed to work on; an agent becomes available (or unavailable) on all of the queues they are configured on.

In terms of reporting, this means that QueueMetrics will display an agent as available on a fake queue called * ALL *, as we have no information on which queues an agent is actually working on.

If you enable ADDMEMBER mode by passing --use-addmember "1" to uniloader fsw (requires Uniloader 21.04.6), then each time an agent logs on or off, their current set of queues is queried on the FusionPBX database, and separate log-on records are emitted for each queue. This gives QueueMetrics the information needed to display the current set of queues an agent is working on.

This causes a database access on each agent log-on or log-off; the query run can be overridden though sql-qs-for-agent, that given the agent’s UUID, should return the UUIDs of all queues this agent is supposed to be working on. The UUIDs for agent and queues will then be translated through the normal mechanism described above.

This does not change the fact that logging on, logging off or pausing happen at once on all queues, and never separately. A possible workaround is explained in our FusionPBX Integration Guide.

Setting up

You can install Uniloader normally; make sure you enable both services uniloader and uniloader-fsw. Both will be active at once: uniloader-fsw wil create a "fake" queue_log file and uniloader, as always, will upload it to your QM server(s).

You can also run it manually to test it.

$ ./uniloader fsw -?

NAME:
   uniloader fsw - Parses FreeSwitch mod_callcenter events

USAGE:
   uniloader fsw [command options] [arguments...]

DESCRIPTION:
   This command listens on FreeSwitch's Event Socket.

It reacts to mod_callcenter events and attempts to coerce them
to queue_log format, to make them compatible
with QueueMetrics.
It only generates a queue_log file; it should then be
uploaded by a separate instance of 'uniloader upload'.

OPTIONS:
   --host value                     Your FreeSwitch server (default: "127.0.0.1")
   --port value                     The ESL port on FreeSwitch (default: 8021)
   --auth value                     The ESL auth secret (default: "ClueCon") [$AUTH]
   --queuelog value                 The queue_log file to write
   --events value                   A debug file to dump mod_callcenter events to
   --allevents                      Read (and possibly log) all events, not just the ones we need
   --ps-database value              The kind of database we  can connect to. (default: "postgres")
   --ps-uri value                   A FusionPbx database to connect to (default: "localhost/fusionpbx")
   --ps-login value                 A FusionPbx database user (default: "fusionpbx")
   --ps-pwd value                   A FusionPbx database password [$FUSIONPWD]
   --sql-agent value                The query used to extract (tenant,agent) from agentId. Blank for default.
   --sql-queue value                The query used to extract (tenant,queue) from queueId.  Blank for default.
   --sql-qs-for-agent value         The query to extract queue UUIDs that agentId works on.  Blank for default.
   --outbound-include-caller value  A regexp to be applied on the caller. Only numbers matching it will be included.
   --outbound-exclude-caller value  A regexp to be applied on the caller. Any numbers matching it will be rejected.
   --outbound-include-callee value  A regexp to be applied on the callee. Only numbers matching it will be included.
   --outbound-exclude-callee value  A regexp to be applied on the callee. Any numbers matching it will be rejected.
   --shorten-domain value           If 1, the domain will be shortened (default: 0)
   --use-addmember value            If 1, agent log-ons will be logged queue-by-queue. (default: 0)
   --pid value                      The PID file to write. If already present, won't start.

When setting up:

  • The --queuelog option should create a queue log file. It can be put anywhere - you must make sure that it is the same location that will be read by the uniloader service

  • The file --events is optional, but we suggest creating it so anomalies can be tracked. If you add --allevents it will process and log all events being created on the PBX and not only the ones it needs.

  • If you use FusionPBX, credentials to the database can be entered in --ps-uri, --ps-login and --ps-pwd. If you don’t want to use it, set 'ps-uri' to a single dash. The option 'ps-database' can be set to postgres or mysql.

  • You can specify custom agent resolution queries in --sql-agent, --sql-queue and --sql-qs-for-agent. See Custom agent and queue translation.

  • The options --outbound-include-caller, --outbound-exclude-caller, --outbound-include-callee and --outbound-exclude-callee let you pick which outbound calls you want trackked automatically. See Automatic outbound tracking.

  • The --shorten-domain option will try shortening the domain name to the first element in it, e.g. 'abc.example.com' will be shortened to 'abc'.

  • The --use-addmember option will generate ADDMEMBER records instead of AGENTLOGIN - see ADDMEMBER. Currently deprecated.

  • The --pid is a file to write the current PID to.

The database connection and the ESL connection can be checked using uniloader test postgres and uniloader test fsw-esl.
If you see that calls generated by uniloader-fsw are incomplete - that is, they seem to be made only of an ENTERQUEUE verb and nothing else, it’s likely that there is an issue on your database when trying to decode unique-ids to agent codes. As the service restarts automatically after each crash, you will always see the uniloader-fsw service as "up", but its logs will show a lot of restarts.

Setting up mult-tenant systems with QueueMetrics Live

When using QueueMetrics Live with multiple instances on a multi-tenat system, you need to run the uniloader service as:

./uniloader -s qlog.txt  u  -x splitter_rules.json

Where 'splitter_rules.json' is a file that contains multiple tenants, defined as:

[
    {
	    "clientname": "Acme Company Ltd",
	    "uri": "https://my.queuemetrics-live.com/acmeco",
	    "login": "webqloader",
	    "pass": "itsasecret",
	    "token": "",
	    "matcher": ["acmeco-"],
	    "match": "any",
	    "removematch": true,
	    "disabled": false,
	    "noactions": false
    }
]

Note that:

  • 'uri', 'login' and 'pass' are the ones that you are given for your QueueMetrics Live instance

  • 'matcher' contains the domain (tenant) and a trailing slash.

  • 'clientname' is not needed in this scenario, but we suggest setting it for readability

You can safely restart the service when you make changes to the rules, as data is queued on the 'queue_log' file.

You do not need to have all tenants configured; only the ones that match will be fed, and other data will be ignored. If you create a new tenant, and there is existing data for it on the log file, it will be uploaded on the first run.

A complete explanation of the splitter logic is available at Splitter.

Enabling user actions

If you want, your QueueMetrics system can send login/logoff actions back to your FreeSwitch server. An explanation of how this works at AMI Feedback.

In QueueMetrics you need to set (at least) the following properties:

callfile.dir=fsw:ClueCon@127.0.0.1
default.webloaderpbx=true
platform.pbx=FREESWITCH

You do not need to include any dial-plan, as actions work directly.

In QueueMetrics, you also need to enter the "External Reference ID" identifier in the Agent (and possibly Queue) page, as this code will be used to generate ESL login/logoff commands. The external reference for queues and agents is easily found and can be uploaded automatically to QM by running uniloader pbxinfo fusionpbx, as explained in PbxInfo for FusionPBX.