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

如何在Asterisk拨号计划中同步播放提示音与执行CURL请求?

在Asterisk 13.18.3实现同步播放提示音+后台CURL请求

咱先理清楚核心需求:要让5秒的欢迎提示音完整播完,同时后台默默跑那个最多3秒的CURL请求,之前用MOH的问题在于CURL返回后MOH就停了,没法保证提示音播完;搞API包装又要维护请求响应的存储,太麻烦。好在Asterisk 13.18.3完全支持异步处理,给你两个靠谱的方案:

方案一:用System命令后台执行脚本(简单易上手)

这个方案利用Shell脚本后台执行CURL,把结果存到临时文件,等提示音播完再读取结果,非常适合快速实现。

步骤1:写一个后台执行的Shell脚本

创建fetch_caller_info.sh(记得给执行权限chmod +x):

#!/bin/bash
# 接收主叫号码参数
CALLER_ID=$1
# 后台执行CURL,把结果写到唯一命名的临时文件(用主叫号码+进程ID避免并发冲突)
curl -s --max-time 3 "http://192.168.0.38/test.php?aparty=$CALLER_ID" > /tmp/caller_${CALLER_ID}_$$.result &

注:--max-time 3强制CURL最多等3秒,避免脚本挂着;$$是脚本进程ID,进一步降低并发冲突概率。

步骤2:修改拨号计划

exten => 1002,1,Progress()
; 后台启动脚本执行CURL,不阻塞后续播放
exten => 1002,n,System(/path/to/fetch_caller_info.sh ${CALLERID(num)})
; 完整播放欢迎提示音(这里假设welcome刚好是5秒)
exten => 1002,n,Playback(welcome)
; 最多等3秒确保CURL完成(因为你的CURL最长延迟3秒,这时候提示音已经播完了)
exten => 1002,n,Wait(3)
; 读取临时文件的结果到变量
exten => 1002,n,Set(CURL_RESULT=${FILE(/tmp/caller_${CALLERID(num)}_*.result)})
; 删除临时文件,避免磁盘占用
exten => 1002,n,System(rm -f /tmp/caller_${CALLERID(num)}_*.result)
exten => 1002,n,NoOp(isSubscriber:${CURL_RESULT})
exten => 1002,n,GotoIf($["${CURL_RESULT}"!="0"]?lbl_call_hangup:)
exten => 1002,n,Read(DTMF,,0,n,1,10)
exten => 1002,n,Hangup
exten => 1002,n(lbl_call_hangup),Hangup

方案二:用Async AGI(更优雅,无临时文件)

如果不想用临时文件,可以用Async AGI在后台启动线程处理CURL,直接把结果写入Asterisk变量,适合更复杂的业务逻辑。

步骤1:写一个Async AGI脚本(Python示例)

先安装Asterisk AGI模块:pip install asterisk-agi,然后创建async_curl.py(给执行权限):

#!/usr/bin/env python
import agi
import subprocess
import threading

def fetch_curl_result(agi_instance, caller_id):
    try:
        # 执行CURL请求,设置3秒超时
        result = subprocess.check_output(
            ['curl', '-s', '--max-time', '3', f'http://192.168.0.38/test.php?aparty={caller_id}'],
            stderr=subprocess.STDOUT
        )
        # 把结果设置到Asterisk全局变量
        agi_instance.set_variable('CURL_RESULT', result.decode().strip())
    except subprocess.CalledProcessError as e:
        # 处理CURL请求失败的情况,比如返回默认值0
        agi_instance.set_variable('CURL_RESULT', '0')
    agi_instance.verbose(f"CURL completed for caller {caller_id}, result: {agi_instance.get_variable('CURL_RESULT')}")

# 初始化AGI
agi_obj = agi.AGI()
caller_id = agi_obj.env['agi_callerid']

# 启动后台线程执行CURL,立即返回不阻塞拨号计划
thread = threading.Thread(target=fetch_curl_result, args=(agi_obj, caller_id))
thread.start()

agi_obj.verbose(f"Started async CURL for caller {caller_id}")

步骤2:修改拨号计划

exten => 1002,1,Progress()
; 启动Async AGI,后台处理CURL
exten => 1002,n,AGI(/path/to/async_curl.py)
; 完整播放欢迎提示音
exten => 1002,n,Playback(welcome)
; 等待CURL完成(最多3秒)
exten => 1002,n,Wait(3)
exten => 1002,n,NoOp(isSubscriber:${CURL_RESULT})
exten => 1002,n,GotoIf($["${CURL_RESULT}"!="0"]?lbl_call_hangup:)
exten => 1002,n,Read(DTMF,,0,n,1,10)
exten => 1002,n,Hangup
exten => 1002,n(lbl_call_hangup),Hangup

为什么之前的MOH方案不行?

你之前用StartMusicOnHold的问题在于,CURL命令是同步阻塞的——Asterisk会先执行CURL,等它返回后才会继续后续步骤,而MOH会一直播放到StopMusicOnHold被调用。这就导致如果CURL提前返回,MOH会被立即停止,没法保证提示音完整播放。而Playback是固定播放指定音频文件,完成后才继续,刚好匹配你“完整播放提示音”的需求。

注意事项

  1. 给脚本设置正确的权限,确保Asterisk用户(通常是asterisk)能执行脚本和读写临时文件;
  2. CURL请求一定要加超时参数,避免脚本无限等待;
  3. 并发场景下,临时文件名要足够唯一(方案一里用了主叫号码+进程ID),防止不同呼叫的结果互相覆盖;
  4. 如果提示音时长小于CURL的最长延迟,Wait命令可以调整时间,确保CURL完成后再处理结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:57:58