Skip to main content
Skip table of contents

Pause/Resume EFCX Call Recording

Introduction

Basic Working

Pause/resume service expects a post request with the extension of an agent. After receiving the request it makes a connection with freeswitch using ESL(Event Socket Lib). It run a command “show calls” and retrieve all the calls active at that time. A command using

CODE
uuid_broadcast uuid  record_session_mask::${record_path}/${recording_filename} both

will be sent to mask the call

The call will be paused in a sense that it would be masked and that portion will be muted. An as soon as the call is unmasked it will be recording once again.

image-20241104-053630.png

Architecture

Inside Apis we have two end point in the controller that handles pause and resume request. With pause and resume service we have an ESL service responsible for the connection making and handling. Inside Pause and resume service we have two major functions one is to mask and other is to umask. These functions are dependent on esl connection for fetching call information and also on another function that extract all the useful information from the response and help masking or unmasking a call.

ESL connection

To Connect to FreeSwitch CLI we use the ESL(Event Socket Layer) libraries, It helps make connections with the free-switch using host, port, and password. Once the connection is established it fetches all the information from free-switch and also it gives commands to free-switch.

CODE
public class EslService {
    private String eslHost = System.getenv("ESL_HOST");
    private int eslPort = Integer.parseInt(System.getenv("ESL_PORT"));
    private String eslPassword = System.getenv("ESL_PASSWORD");
    private Client inboundClient;
    private String recordingPath = System.getenv("REC_PATH_STREAMS");

    public EslService() throws InboundConnectionFailure {
        inboundClient = new Client();
        inboundClient.connect(new InetSocketAddress(eslHost, eslPort), eslPassword, 10);
        log.debug("ESL Connection established");
    }
    
    ...

Configuration

In Pom.xml we need to add the following dependencies.

CODE
  <dependency>
            <groupId>esl-client</groupId>
            <artifactId>esl-client</artifactId>
            <version>1.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.0.56.Final</version>
        </dependency>
        
        ...

We need further env variables

CODE
ESL_HOST = where the freeswitch is up and running
ESL_PORT = ESL makes connection with 8021 
ESL_PASSWORD = Password of the freeswitch

Update the Lua script

  1. Go to the /usr/share/freeswitch/scripts/

  2. Find the file with the name set_recording_name.lua script file

  3. look for a line in the script session:setVariable("recording_filename" , agent_leg_uuid .. "." .. ext) and update it to session:setVariable("recording_filename" , customer_leg_uuid .. "." .. ext)

    Here is the full lua script as a reference. it is very crucial for Pause/Resume of Call recording.

    CODE
    package.cpath ="/usr/lib/x86_64-linux-gnu/lua/5.2/?.so;" .. package.cpath
    package.path = "/usr/share/lua/5.2/?.lua;" .. package.path
    
    local callType = session:getVariable("sip_h_X-CallType")
    local record_path = session:getVariable("record_path")
    local uuid = session:getVariable("uuid")
    local ext = session:getVariable("record_ext")
    local customer_leg_uuid = ''
    local api = freeswitch.API();
    
    -- can be consult, OUT, MONITOR, PROGRESSIVE, 
    
    local function fileExists(file) 
        local f = io.open(record_path .. '/' .. file, 'r')
        if f == nil then
                -- freeswitch.consoleLog("ERROR", "set_recording_name.lua FILE NOT FOUND: " .. file)
                return false;
        end
        io.close(f);
        -- freeswitch.consoleLog("ERROR", "set_recording_name.lua FILE WAS FOUND: " .. file)
        return true;
    end
    
    local destination = tostring(session:getVariable("destination_number"))
    -- freeswitch.consoleLog("NOTICE", " \n set_recording_name.lua DESTINATION \n " .. destination)
    
    
    if (callType == "CONSULT") then 
        freeswitch.consoleLog("NOTICE", " \n set_recording_name.lua RECORDING CONSULT CALL \n")
        session:execute("stop_record_session","all")
        if (string.match(tostring(destination), "99887766")) then
            return
        end
        local filename = uuid .. "." .. ext
        session:setVariable("recording_command" , "nolocal:execute_on_answer=record_session " .. record_path .. "/" .. filename)
        session:setVariable("recording_filename" , filename)
        return
    elseif (callType == "MONITOR") then -- no recording enabled here
        return
        -- callType == "PROGRESSIVE" or 
    elseif (callType == "OUT") then -- for manual outbound use the preset other leg uuid i.e. customer leg uuid
    
        customer_leg_uuid = session:getVariable("customer_leg_uuid")
        freeswitch.consoleLog("NOTICE", " \n set_recording_name.lua UUID \n " .. uuid)
        freeswitch.consoleLog("NOTICE", " \n set_recording_name.lua CUSTOMER \n" .. customer_leg_uuid)
        if (uuid ~= customer_leg_uuid) then
            freeswitch.consoleLog("NOTICE", " \n set_recording_name.lua RECORDING MANUAL OUTBOUND CALL \n")
            session:setVariable("recording_command" , "nolocal:execute_on_answer=record_session " .. record_path .. "/" .. customer_leg_uuid .. "." .. ext)
    	session:setVariable("recording_filename" , customer_leg_uuid .. "." .. ext)
            return
        end
        session:execute("stop_record_session","all")
        local res = api:executeString("bgapi uuid_broadcast " .. uuid .. " stop_record_session::all")
        uuid = customer_leg_uuid
    end
    
    local filename = uuid
    -- inbound ivr case
    freeswitch.consoleLog("NOTICE", "\n set_recording_name.lua RECORDING INBOUND CALL \n")
    local count = 0
    while (fileExists(filename .. '.' .. ext)) do
        count = count + 1
        filename = uuid .. '_' .. count
    end
    
    local suffix = '_' .. count
    if (count == 0) then 
        suffix = ''
    end
    
    filename = uuid .. suffix.. "." .. ext
    session:setVariable("recording_filename" , filename)
    session:setVariable("recording_command" , "nolocal:execute_on_answer=record_session " .. record_path .. "/" .. filename)
    
    

ESL connection on Free-switch

  1. Go to /etc/freeswitch/autoload_configs/event_socket.conf.xml and add the following lines

  2. CODE
    <settings>
        <param name="listen-ip" value="192.168.1.106"/> <!-- Use the server's IP -->
        <param name="listen-port" value="8021"/>       <!-- Default ESL port -->
        <param name="password" value="1234"/>       <!-- Default ESL password -->
        <param name="apply-inbound-acl" value="esl"/>
    </settings>
  3. Go to /etc/freeswitch/autoload_config/acl.conf.xml and add the following lines

CODE
  <list name="esl" default="allow">
      <node type="allow" cidr="0.0.0.0/0"/>
  </list> 
  1. Type ‘fs_cli’ on your terminal it will open free-switch CLI and then run this command ‘reloadxml’ in fs_cli

  2. Type ‘fs_cli’ on your terminal it will open free-switch CLI and then run this command ‘reloadacl’ in fs_cli

  3. Restart the freeswitch using sudo systemctl restart freeswitch

  4. run ./install-efcx script to restart the vrs.

Controller end

We have two different APIs for pause and resume. Both of them awaits a post request containing extension of the customer. Then end point are as follows

CODE
//For masking the call
http://localhost:8080/vrs/recording/{extension}/pause

//For unmasking the call
http://localhost:8080/vrs/recording/{extension}/resume

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.