C#使用AsterNET开发时如何取消已发起的外呼通话
问题描述
基于AsterNET组件开发管理端程序时,已实现员工点击按钮一键呼叫客户的功能,当前需要补充取消外呼、主动断开与客户呼叫连接的能力,但未找到对应的实现方法。
当前呼叫业务流程如下:
- 点击呼叫按钮后,系统首先呼叫对应员工分机
- 员工摘机后,系统开始拨打客户号码,同时弹出窗口向员工展示呼叫进度
- 已额外添加取消呼叫按钮,但未找到可终止向客户拨号的相关事件或函数
核心实现代码如下:
manager = new ManagerConnection(host, port, username, password); manager.DialBegin += Manager_DialBegin; manager.DialEnd += Manager_DialEnd; manager.Hangup += Manager_Hangup; manager.PingInterval = 0; try { manager.Login(); OriginateAction oc = new OriginateAction(); oc.Context = "from-internal"; oc.Priority = "1"; oc.Channel = "SIP/100"; oc.CallerId = "100"; oc.Exten = lblPhoneNumber.Text; oc.Timeout = 30000; oc.Async = true; manager.SendAction(oc, null); } catch (ManagerException ex) { var sendMail = Task.Run(async () => await Email.SendMail(Properties.Settings.Default.SmtpEmailLogAddress, "", "", "Error connecting to Asterisk", "Could not connect to the Asterisk-server for employee: " + Functions.GetEmployeeByID(Manager.Employee_ID), true, "", System.Net.Mail.MailPriority.Normal)); }
实现方案
你当前使用异步OriginateAction发起外呼,整个外呼过程分两个阶段,取消逻辑需要对应阶段分别处理:
阶段1:系统正在呼叫员工分机,员工尚未摘机
这个阶段还未启动客户号码呼叫,直接发送CancelOriginateAction即可终止外呼任务。需要注意提前保存发起外呼时的ActionId,用于匹配要取消的外呼任务。
首先修改原外呼发起逻辑,新增全局状态变量存储外呼相关标识:
// 全局状态变量 private string _currentOriginateActionId; private string _employeeChannel; private string _customerChannel; // 外呼发起部分代码修改 OriginateAction oc = new OriginateAction(); oc.Context = "from-internal"; oc.Priority = "1"; oc.Channel = "SIP/100"; oc.CallerId = "100"; oc.Exten = lblPhoneNumber.Text; oc.Timeout = 30000; oc.Async = true; // 生成唯一ActionId并赋值,用于后续取消匹配 _currentOriginateActionId = Guid.NewGuid().ToString("N"); oc.ActionId = _currentOriginateActionId; manager.SendAction(oc, null);
该阶段的取消按钮逻辑如下:
if (!string.IsNullOrEmpty(_currentOriginateActionId) && string.IsNullOrEmpty(_customerChannel)) { CancelOriginateAction cancelOriginate = new CancelOriginateAction(); cancelOriginate.ActionId = _currentOriginateActionId; manager.SendAction(cancelOriginate); ResetCallState(); }
阶段2:员工已摘机,系统正在振铃客户/已和客户接通
这个阶段Originate任务已经执行完成,CancelOriginateAction不再生效,需要通过HangupAction挂断对应通道实现取消/断开:
- 客户未接通时,挂断客户侧振铃通道即可,员工分机通道会被Asterisk自动回收
- 客户已接通时,可根据业务需求选择仅挂断客户通道、或同时挂断员工分机通道
首先配合已注册的事件回调,实时更新通道状态:
private void Manager_DialBegin(object sender, DialBeginEvent e) { // 匹配当前员工分机,记录主被叫通道 if (e.Channel.StartsWith("SIP/100")) { _employeeChannel = e.Channel; _customerChannel = e.Destination; } } private void Manager_Hangup(object sender, HangupEvent e) { // 通道释放后清除对应标记 if (e.Channel == _employeeChannel) _employeeChannel = null; if (e.Channel == _customerChannel) _customerChannel = null; // 所有通道释放完成后重置状态 if (string.IsNullOrEmpty(_employeeChannel) && string.IsNullOrEmpty(_customerChannel)) { ResetCallState(); } } // 状态重置公共方法 private void ResetCallState() { _currentOriginateActionId = null; _employeeChannel = null; _customerChannel = null; }
该阶段的取消/断开逻辑如下:
if (!string.IsNullOrEmpty(_customerChannel)) { // 挂断客户侧通道 HangupAction hangupCustomer = new HangupAction(); hangupCustomer.Channel = _customerChannel; hangupCustomer.Cause = 16; // 16为正常呼叫清除原因码 manager.SendAction(hangupCustomer); // 如需取消后直接把员工分机也挂断,取消下方注释即可 // if (!string.IsNullOrEmpty(_employeeChannel)) // { // HangupAction hangupEmployee = new HangupAction(_employeeChannel, 16); // manager.SendAction(hangupEmployee); // } }
兼容说明:如果使用13以前的老旧Asterisk版本,
CancelOriginateAction可能不被支持,此时可以直接挂断员工分机通道终止整个外呼流程,仅会在员工已摘机的场景下直接挂断分机,体验略差,新版本优先使用分阶段处理逻辑。
内容的提问来源于stack exchange,提问作者Paul Albers
相关产品推荐
相关产品推荐

