You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Freeswitch:呼叫结束执行Python脚本获billsec发API遇返回None问题咨询

解决Hangup Hook中无法获取billsec变量的问题

我之前也碰到过一模一样的情况——用session.setHangupHook绑定的脚本里拿billsec总是返回None。核心原因大多是hangup hook触发时机太早,会话的CDR类变量(比如通话时长billsec)还没被系统计算写入,或者会话资源已经开始释放。下面分享几个实测可行的解决办法:

方法1:改用Hangup事件监听替代Hangup Hook

如果你的呼叫平台支持事件监听(比如FreeSWITCH的ESL、Asterisk的ARI/AGI事件机制),直接监听CHANNEL_HANGUP这类挂断事件会更可靠。以Python结合FreeSWITCH ESL为例:

import esl

def handle_hangup_event(event, args):
    # 从事件头中直接获取已计算好的billsec
    billsec = event.getHeader('variable_billsec')
    if billsec:
        # 这里写发送到自有API的逻辑
        send_to_your_api(billsec)

# 连接到呼叫平台的ESL服务器
conn = esl.Connection('localhost', 8021, 'cluecon')
# 订阅挂断事件
conn.events('plain', 'CHANNEL_HANGUP')
# 绑定事件回调
conn.execute('event', 'CHANNEL_HANGUP', handle_hangup_event)
# 启动事件循环
conn.mainloop()

这种方式下,挂断事件触发时,系统已经完成了通话时长的计算,billsec会被完整附加在事件参数里,不会出现None的情况。

方法2:在Hangup Hook中通过呼叫ID查询CDR数据库

如果必须使用setHangupHook,可以在钩子脚本里用呼叫的唯一标识(比如uuid)去CDR数据库里查询billsec:

import psycopg2  # 假设用PostgreSQL存储CDR,其他数据库同理

def hangup_hook(session):
    # 获取当前呼叫的唯一ID
    call_uuid = session.getVariable('uuid')
    if not call_uuid:
        return
    
    # 连接CDR数据库查询通话时长
    db_conn = psycopg2.connect("dbname=cdr_db user=admin password=your_pwd")
    cursor = db_conn.cursor()
    cursor.execute("SELECT billsec FROM cdr_records WHERE call_uuid = %s", (call_uuid,))
    billsec_result = cursor.fetchone()
    
    if billsec_result:
        billsec = billsec_result[0]
        send_to_your_api(billsec)
    
    cursor.close()
    db_conn.close()

注意:如果CDR写入有延迟,可以加个短暂的重试逻辑(比如隔1秒查一次,最多查3次),避免hook执行时CDR还没入库。

方法3:手动计算通话时长(适合精度要求不高的场景)

如果你的呼叫流程是完全可控的,可以在通话接通时记录开始时间,在hangup hook里计算时间差得到近似的billsec:

import time

def on_call_answer(session):
    # 通话接通时记录时间戳,存在会话变量里
    session.setVariable('call_start_time', str(time.time()))

def hangup_hook(session):
    start_time_str = session.getVariable('call_start_time')
    if not start_time_str:
        return
    
    start_time = float(start_time_str)
    # 计算通话时长(取整)
    billsec = int(time.time() - start_time)
    send_to_your_api(billsec)

这种方式的缺点是和系统原生计算的billsec可能有几秒误差,但胜在简单直接,适合对精度要求不高的场景。

额外排查点

  • 先确认变量名是否正确:有些平台可能用duration或call_duration代替billsec,可以先调用session.getVariables()打印所有会话变量,核对变量名称
  • 检查脚本权限:确保hangup hook脚本有足够权限读取会话变量或访问数据库

内容的提问来源于stack exchange,提问作者Marik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:45:56