Announce Sound and Announce Position block the bridging of the caller and the agent

francois

New Member
Oct 3, 2019
22
8
3
55
Dear All,

I am using the callcenter-announce-position.lua (documented in https://freeswitch.org/confluence/display/FREESWITCH/mod_callcenter) and announce-sound (documented in https://docs.fusionpbx.com/en/latest/applications/call_center.html#announce-sound).

Those two settings work like a charm with mod_callcenter. However, when the agent takes the call, the two legs are not bridged until announce-sound is completed and/or api:executeString uuid_broadcast is completed in callcenter-announce-position.lua.

This issue is well documented in https://github.com/signalwire/freeswitch/issues/1148.

As of signalwire github, the bug fix is still not officially released.

Questions; Any one of you already faced that issue? Have you been able to find a workaround?

Note that callcenter-announce-position.lua execution is usually quite fast so the limitation is acceptable for the agents, however, when announce-sound is configured with a very long voice prompt, this issue is more than annoying...
Note v2; I am thinking of merging the announce-sound prompt with the MoH but that's not very scalable.

Looking forward for your comments.

Thanks!
 

gflow

Member
Aug 25, 2019
239
24
18
I had the same issue so don't offer the "announce position" to clients. I ask clients get a MoH file recorded and every 60 seconds have the voice over artist say "press 1 if you'd like to leave a message and receive a callback" if somebody press's 1 they are asked to leave a message and return phone number. Agents then check that group voicemail box every 15-20 minutes and return all the calls.
 

francois

New Member
Oct 3, 2019
22
8
3
55
I managed to find a fix for the limitation in callcenter-announce-position.lua.

1- Use freeswitch.EventConsumer() and bind the "PLAYBACK_STOP" and "CUSTOM"/"callcenter::info" events
2- After the last call of uuid_broadcast, loop with the events:pop function until the PLAYBACK_STOP event related to the last .wav file is received
or
After the CUSTOM / callcenter::info with Answer-State=answered event related to the queue is received
3- When the call is answered (if the CUSTOM / callcenter::info event is recceived), call api:executeString("uuid_break "..caller_uuid.." all") to stop every pending uuid_broadcast

It works like perfectly now and the caller is transferred to the agent with no delay.
 

DigitalDaz

Administrator
Staff member
Sep 29, 2016
2,904
516
113
@francois Could you perhaps detail this solution a little more, I'm sure many people would find this very useful.
 

francois

New Member
Oct 3, 2019
22
8
3
55
@DigitalDaz, here we go:

1-

Code:
--subscribe to the playback and callcenter events
events = freeswitch.EventConsumer()
events:bind("PLAYBACK_STOP"); --usefull to detect the completion of uuid_broadcast event
events:bind("CUSTOM", "callcenter::info"); --usefull to detect the Answer-State=answered event

2- & 3-

Code:
-- this function loop for the events. the second parameter must hold the string of the path of the file that was passed to api:executeString("uuid_broadcast "
function wait_call_answered_or_play_completed(events, file_path)
    local play_completed = false
    local call_answered = false
    while (play_completed == false and call_answered == false)
    do
        ---get the next event
        received_event = events:pop(1, 10000)

        if (received_event ~= '') then
            local event_type = received_event:getType()

            if debug["info"] then
                --freeswitch.consoleLog("notice", "[callcenter-announce-position] event received xml:"..received_event:serialize("xml").."\n")
            end
            if (event_type == "PLAYBACK_STOP") then
                local playback_file_path = received_event:getHeader('Playback-File-Path')
                local playback_status = received_event:getHeader('Playback-Status')
                local variable_call_uuid =  received_event:getHeader('variable_call_uuid')

                if debug["info"] then
                    freeswitch.consoleLog("notice", "[callcenter-announce-position] event received, Playback-File-Path: "..playback_file_path.." Playback-Status: "..playback_status.."\n")
                end

                if (variable_call_uuid == caller_uuid and
                    playback_file_path == file_path and
                    playback_status == 'done') then
                    play_completed = true
                end
            elseif (event_type == "CUSTOM") then
                --assume Event-Subclass is callcenter::info
                local answer_state = received_event:getHeader('Answer-State')
                local cc_queue = received_event:getHeader('CC-Queue');
                local cc_member_session_uuid = received_event:getHeader('CC-Member-Session-UUID');

                if debug["info"] then
                    local event_subclass = received_event:getHeader('Event-Subclass')
                    local cc_action = received_event:getHeader('CC-Action');
                    local cc_agent_status = received_event:getHeader('CC-Agent-Status');
                    freeswitch.consoleLog("notice", "[callcenter-announce-position] event received, Event-Subclass: "..event_subclass.." Answer-State: "..string_protect(answer_state).." CC-Action: "..string_protect(cc_action).." CC-queue: "..string_protect(cc_queue).." CC-Member-Session-UUID: "..string_protect(cc_member_session_uuid).." CC-Agent-Status: "..string_protect(cc_agent_status).."\n")
                end
                if (answer_state == 'answered' and cc_queue == queue_name and cc_member_session_uuid == caller_uuid) then
                    call_answered = true
                    --Stop every pending uuid_broadcast to allow the bridge between the caller and the agent before the uuid_broadcast playback completion
                    local result = api:executeString("uuid_break "..caller_uuid.." all")
                    if debug["info"] then
                        freeswitch.consoleLog("notice", "[callcenter-announce-position] uuid_break result: "..result.."\n")
                    end
                end
            else
                if debug["info"] then
                    freeswitch.consoleLog("notice", "[callcenter-announce-position] event received xml:"..received_event:serialize("xml").."\n")
                end
            end
        else
            if debug["info"] then
                freeswitch.consoleLog("notice", "[callcenter-announce-position] event NOT received\n")
            end
            play_completed = true; -- expired
        end
    end

    return call_answered;
end

-- call the above function after the last call of api:executeString("uuid_broadcast "..
                if wait_call_answered_or_play_completed(events, sounds_dir..[insert the file path here]) then
                    exists = false;
                end

--cleanup the event registration before existing the lua script
    if exists == false then
        --unbind events
        events:cleanup()
        events = nil

        return
    end

Enjoy!

P.S. Fell free to clean the code as per your standard add my name into the copyright section of this script ;)