当前位置: 首页 > 工具软件 > ESL > 使用案例 >

呼叫中心——ESL接入

秦权
2023-12-01

ESL作为java客户端通信组件,底层主要通过socket跟freeSwitch建立通信。具体使用步骤是:

1.spring项目中引入pom依赖:

        <dependency>
            <groupId>thirdparty.org.freeswitch.esl.client</groupId>
            <artifactId>esl-client</artifactId>
            <version>1.0</version>
        </dependency>        

2.ESL的client应用有两种模式:inbound和outbound。本项目采用前者。

a.Client 的声明:

Client eslClient = new Client();

b.初始化连接信息:

public static void connection(Client eslClient) throws InboundConnectionFailure {
        String host = HippoUtil.getValue(HippoUtil.FS_INSIDE_HOST);
        Integer port = Integer.parseInt(HippoUtil.getValue(HippoUtil.FS_INSIDE_PORT));
        String password = HippoUtil.getValue(HippoUtil.FS_INSIDE_PASSWORD);
        Integer timeoutSeconds = Integer.parseInt(HippoUtil.getValue(HippoUtil.FS_INSIDE_TIMEOUTSECONDS));
        eslClient.connect(new InetSocketAddress(host, port), password, timeoutSeconds);
        eslClient.cancelEventSubscriptions();
        eslClient.setEventSubscriptions(IModEslApi.EventFormat.PLAIN, CommonContants.PLAIN);
}

c.为client加入监听

import org.freeswitch.esl.client.inbound.IEslEventListener;
import org.freeswitch.esl.client.internal.Context;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.freeswitch.esl.client.transport.message.EslHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;


/**
 * 创建esl监听
 */
@Component
public class EslEventListener implements IEslEventListener {

    private final Logger log = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    EslEventHandler eslEventHandler;

    /***
     * 事件监听
     * @param event
     */
    @Override
    public void onEslEvent(Context ctx, EslEvent event) {
        if (InjectServiceUtil.getInstance().getEslEventHandler() == null){
            return;
        }
        Map<String, String> headers = event.getEventHeaders();
        //过滤器***

        //初始信息cache化 比如redis存储fs的IP信息等
        String callUuid = headers.get(CommonContants.EventHeader.callUUID);
        String ip = headers.get(CommonContants.EventHeader.IPv4);
        //调用具体的event handler
        eslEventHandler.execute(event);
    }
}

在此注意:由于部署时候会存在多个java客户端,而生产上FS服务器也会部署多个,因为我们需要对每个应用(client)和具体的FS进行映射处理(可以持久化到db)。且同一时刻,一个会话只会在一个esl的连接里处理,因此在一通电话里,client和FS保持的会话是不可间断。且我们需要进行心跳机制保持Client和FS的连接不能被中断。这里需要对client的status进行监控。

另外,listener 里可以加入具体的业务逻辑 ,比如session落库、status记录(callUUID之类的)。而handler可以根据不同的通话命令引导到不同的handler***处理。

其中event header里主要的key如下:

public class EventHeader {
        public final static String direction = "variable_call_direction";
        public final static String callState = "Channel-Call-State";
        public final static String hangupCause = "Hangup-Cause";
        public final static String sipFromUser = "variable_sip_from_user";
        public final static String sipToUser = "variable_sip_to_user";
        public final static String uuid = "variable_uuid";
        public final static String callUUID = "Channel-Call-UUID";
        public final static String fileName = "fileName";
        public final static String callerIdNumber = "Caller-Caller-ID-Number";
        public final static String calleeIdNumber = "Caller-Callee-ID-Number";
        public final static String createdTime = "Caller-Channel-Created-Time";
        public final static String answeredTime = "Caller-Channel-Answered-Time";
        public final static String hangupTime = "Caller-Channel-Hangup-Time";
        public final static String executeOnAnswer = "variable_execute_on_answer";
        public final static String dtmfDigit = "DTMF-Digit";
        public final static String application = "Application";
        public final static String exeAppName = "execute-app-name";
        public final static String dtmfArgs = "variable_dtmf_args";
        public final static String readResult = "variable_read_result";
        public final static String exeRead = "read";
        public final static String speak = "speak";
        public final static String bridge = "bridge";
        public final static String success = "success";
        public final static String failure = "failure";

        public final static String asrResult = "variable_detect_speech_result";
        public final static String currentApplication = "variable_current_application";
        public final static String applicationResponse = "Application-Response";
        public final static String fileUrl = "variable_current_application_data";
        public final static String dialedUser = "variable_dialed_user";
        public final static String disposition = "variable_bridge_disposition";

        public final static String customCallType = "variable_custom_call_type";

        public final static String IPv4 = "FreeSWITCH-IPv4";

        public final static String billmsec = "variable_billmsec";

        public final static String mduration = "variable_mduration";


        // 筛号变量
        public final static String selectTelDa2Tone = "da2_tone";
        public final static String selectTelDa2SampleUniqueid = "da2_sample_uniqueid";

        public final static String selectTelDa2SampleBrief = "da2_sample_brief";
        //号码样本分类
        public final static String selectTelDa2SampleCategory = "da2_sample_category";
        public final static String selectTelDa2SampleName = "da2_sample_name";
    }

ESLEventHandler的核心实现如下:

@Service("eslEventHandler")
public class EslEventHandler {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AnswerHandler answerHandler;

    @Autowired
    private BridgeHandler bridgeHandler;

    @Autowired
    private DestroyHandler destroyHandler;

    @Autowired
    private HangComHandler hangupCompHandler;

    @Autowired
    private HangupHandler hangupHandler;

    @Autowired
    private HeartbeatHandler heartbeatHandler;

    @Autowired
    private PlaybackStopHandler playbackStopHandler;

    @Autowired
    private RobotPlaybackStopHandler robotPlaybackStopHandler;

    @Autowired
    private ExecuteComHandler executeComHandler;

    @Autowired
    private RobotExecuteComHandler robotExecuteComHandler;

    @Autowired
    private RobotBridgeHandler robotBridgeHandler;

    @Autowired
    private ParkHandler parkHandler;

    @Autowired
    private StateHandler stateHandler;

    @Autowired
    ExceptionHandler exceptionHandler;

    @Autowired
    private SelectTelHandler selectTelHandler;

    /**
     * 统一事件处理方法
     * @param event
     */
    public void eventHandler(EslEvent event){
        //业务侧自定义的当前会话任务DTO
        CurrentTaskDto currentTaskDto = null;
        Map<String, String> headers = event.getEventHeaders();
        String uuid = headers.get(CommonContants.EventHeader.callUUID);
        //根据不同类型调用具体的handler 比如通过switch 语法。。
    }
}

可以看到,上述代码块,有不同类型的handler,其实这里可以根据策略模式进行适配,或者责任链的模式,将不同handler串联在一起应用。当然,初期为了快速落地,我们也可以使用简单的if/else即可。

另外,每个Session都会有一个初始事件,而为了进行拥塞控制或者限流,我们可以考虑将不同session在发起时利用生产者-消费者的方式进行事件分发和消费和隔离。比如在EventListener里只引入一个eventStorage或eventProducer。然后通过多线程方式不断进行consume,消费者主要逻辑就是调用唯一的hander入口。

 类似资料: