Twilio状态回调返回的TwiML未执行,通话中断求助
问题分析与解决方案
核心问题
你当前的实现存在两个关键错误,导致返回的TwiML无法生效:
- 错误依赖
statusCallback控制通话流程:statusCallback仅用于接收通话状态的通知,当监听completed事件时,通话已经彻底终止,此时返回TwiML无法改变通话状态。 - 回调端点重复发送响应:代码中先调用
res.send(twimlObject.toString()),又执行res.status(200).send(),会导致Twilio收到重复响应,无法正确解析TwiML。
另外,无人接听属于通话未完成的中间状态,你需要在通话终止前捕获该事件并处理,而非等通话结束后再触发回调。
修复步骤
1. 修改呼叫发起代码,添加action属性控制后续流程
Dial的action属性才是Twilio在拨号操作完成(包括无人接听、忙线等场景)后,用来获取后续TwiML的关键,此时通话尚未终止,可以继续执行播放语音等操作。同时保留statusCallback用于状态记录(如果需要):
await dial.client( { // 监听需要的状态事件,包括无人接听和通话完成 statusCallbackEvent: "completed,no-answer", statusCallback: `handleFinishedCall?customerId=${customerId}&widgetId=${nextWidget || ""}&caller=${caller}`, statusCallbackMethod: "POST", // 指定Dial完成后Twilio请求的URL,用于返回后续TwiML action: `handleDialResult?customerId=${customerId}&widgetId=${nextWidget || ""}&caller=${caller}`, actionMethod: "POST" }, idToCall );
2. 创建handleDialResult端点处理拨号结果
通过DialStatus参数判断拨号状态,针对无人接听场景返回播放语音的TwiML:
router.post("/handleDialResult", async (req, res) => { const dialStatus = req.body.DialStatus; const twiml = new VoiceResponse(); if (dialStatus === "no-answer") { // 无人接听时播放提示语音 twiml.say('无人接听,正在为您播放提示信息'); } else if (dialStatus === "busy") { twiml.say('对方正忙,请稍后再拨'); } else if (dialStatus === "completed") { // 通话正常完成,直接挂断 twiml.hangup(); } res.type("text/xml"); res.status(200).send(twiml.toString()); });
3. 修复原handleFinishedCall端点的重复响应问题
如果仍需要保留该端点用于状态记录,确保只发送一次响应:
router.post("/handleFinishedCall", async (req, res) => { // 这里仅处理状态通知,比如记录通话状态到数据库 res.status(200).send("OK"); });
关键说明
DialStatus是Twilio返回的拨号结果参数,常见值包括no-answer(无人接听)、completed(通话完成)、busy(忙线)、failed(呼叫失败)。action属性是控制通话后续流程的核心,而statusCallback仅用于被动接收状态通知,无法干预通话。
内容的提问来源于stack exchange,提问作者Supplementing
相关产品推荐
相关产品推荐

