
本教程详细介绍了如何使用Twilio构建一个具备呼叫转发功能的系统,并在此基础上实现未接来电自动转接语音留言。文章将涵盖Twilio TwiML中的`Dial`和`Record`动词的使用,包括设置呼叫超时、录制语音留言,以及通过Webhook处理录音回调和转录,最终实现将语音留言通过邮件通知用户。
在现代通信应用中,为用户提供一个隐私保护的虚拟号码(即掩码号码)并将其呼叫转发至真实号码是一项常见需求。然而,当用户真实号码无法接通、占线或未及时应答时,如何确保客户能够留下语音留言,并及时通知用户,是提升用户体验的关键。本教程将详细阐述如何利用Twilio的强大功能,构建一个完整的呼叫转发与未接来电语音留言系统。
我们的目标是实现以下功能:
Twilio通过TwiML(Twilio Markup Language)来控制呼叫流程。实现上述功能主要依赖于Dial和Record这两个核心动词。
Dial动词用于将当前呼叫连接到另一个电话号码或Twilio客户端。为了处理未接来电,我们需要利用Dial动词的timeout和action属性。
示例代码:主呼叫Webhook (/webhook/voice)
const twilio = require("twilio");
const express = require("express");
const router = express.Router();
// 假设 getNumberWithoutUser 和 updateQuota 是你的数据库操作函数
// const { getNumberWithoutUser, updateQuota } = require("../db/dbOperations");
router.post("/webhook/voice", async (req, res) => {
const { To, From, CallStatus } = req.body;
// 假设根据 'To' 掩码号码获取用户信息和真实号码
// const [numbers] = await getNumberWithoutUser(To);
// if (!numbers) return res.status(400).send("User does not own this number");
// 假设这些信息从数据库中获取
const primaryPhoneNumber = "+8613800138000"; // 示例:用户的真实号码
const isToPrimaryPhone = true; // 示例:是否开启了呼叫转发
if (isToPrimaryPhone) {
const twiml = new twilio.twiml.VoiceResponse();
// 可选:在转接前播放欢迎语
twiml.say({ voice: 'alice', language: 'zh-CN' }, "请稍候,我们正在为您转接。");
// 使用Dial动词将呼叫转发到用户的真实号码
// 设置15秒超时,如果未接听,则将控制权转交给 /webhook/handle_dial_outcome
twiml.dial({
timeout: 15, // 等待被叫方接听的秒数
action: '/webhook/handle_dial_outcome', // 如果未接听,Twilio将请求此URL
hangupOnStar: true // 允许呼叫者按 * 键跳过语音留言
}, primaryPhoneNumber);
// 假设更新呼叫配额
// await updateQuota(numbers._id, To, "callForwarding", type);
res.type("text/xml");
return res.send(twiml.toString());
}
// 如果呼叫转发未启用或套餐已过期
res.send("Call Forwarding is disabled or package has finished");
});
module.exports = router;当Dial动词因超时而结束时,Twilio会调用action指定的URL。在这个新的Webhook中,我们将使用Record动词来提示客户录制语音留言。
Record动词的关键属性:
示例代码:处理Dial动词结果的Webhook (/webhook/handle_dial_outcome)
// ... (之前的require和router定义)
router.post("/webhook/handle_dial_outcome", async (req, res) => {
const { DialCallStatus, To, From } = req.body; // DialCallStatus 表示Dial动词的执行结果
const twiml = new twilio.twiml.VoiceResponse();
// 检查Dial动词的执行状态
if (DialCallStatus === 'no-answer' || DialCallStatus === 'busy' || DialCallStatus === 'failed') {
// 如果呼叫未接听、占线或失败,则提示客户留言
twiml.say({ voice: 'alice', language: 'zh-CN' }, "您拨打的用户暂时无法接听,请在哔声后留言。");
twiml.record({
maxLength: 60, // 最大录音时长60秒
finishOnKey: '#', // 呼叫者按 # 键结束录音
action: '/webhook/recording_status', // 录音完成后,Twilio将请求此URL
transcribe: true, // 启用语音转文本
transcribeCallback: '/webhook/transcription_status' // 转录完成后,Twilio将请求此URL
});
twiml.say({ voice: 'alice', language: 'zh-CN' }, "留言结束,感谢您的来电。");
} else {
// 其他状态,例如呼叫已完成(通常不会进入此分支,因为如果接听,Dial动词会直接连接,不会触发action)
// 或者可以播放一个结束语
twiml.say({ voice: 'alice', language: 'zh-CN' }, "感谢您的来电。");
}
res.type("text/xml");
return res.send(twiml.toString());
});
// module.exports = router; // 如果是单独的文件,需要导出当录音完成或转录完成时,Twilio会分别向action和transcribeCallback指定的URL发送POST请求,其中包含录音或转录的详细信息。
这个Webhook会接收到录音的URL、时长等信息。你可以在这里将这些信息存储到数据库,并触发邮件通知。
// ... (之前的require和router定义)
router.post("/webhook/recording_status", async (req, res) => {
const { RecordingUrl, RecordingDuration, CallSid, From, To } = req.body;
console.log(`收到新的语音留言:${RecordingUrl},时长:${RecordingDuration}秒`);
// 1. 将录音信息存储到数据库
// const [numbers] = await getNumberWithoutUser(To); // 根据'To'掩码号码获取用户数据
// if (numbers) {
// await appendMessage(numbers._id, To, From, {
// type: 'voicemail',
// url: RecordingUrl,
// duration: RecordingDuration,
// callSid: CallSid
// });
// // 2. 如果不需要等待转录结果,可以直接发送邮件通知
// // await sendMessageNotificationEmail(numbers.userEmail, "您有新的语音留言", `请访问:${RecordingUrl}`);
// }
// Twilio期望收到一个200 OK响应
res.send("Voicemail recording processed.");
});如果启用了transcribe: true,当Twilio完成语音转文本后,会调用此Webhook。这里可以获取转录文本,并将其与录音链接一同通过邮件发送给用户。
// ... (之前的require和router定义)
router.post("/webhook/transcription_status", async (req, res) => {
const { TranscriptionStatus, TranscriptionText, RecordingUrl, CallSid, From, To } = req.body;
if (TranscriptionStatus === 'completed') {
console.log(`语音留言转录完成。录音:${RecordingUrl},转录文本:${TranscriptionText}`);
// 1. 更新数据库中对应的语音留言记录,添加转录文本
// const [numbers] = await getNumberWithoutUser(To);
// if (numbers) {
// // 假设有一个函数可以更新留言记录
// // await updateVoicemailTranscription(CallSid, TranscriptionText);
// // 2. 发送包含语音链接和转录文本的邮件通知
// // const emailBody = `您有一个新的语音留言:\n语音链接:${RecordingUrl}\n转录内容:${TranscriptionText}`;
// // await sendMessageNotificationEmail(numbers.userEmail, "您有新的语音留言及转录", emailBody);
// }
} else {
console.log(`语音转录失败或进行中,状态:${TranscriptionStatus}`);
}
res.send("Voicemail transcription processed.");
});通过本教程,我们学习了如何利用Twilio的Dial和Record TwiML动词,结合多个Webhook回调,构建一个功能完善的呼叫转发和未接来电语音留言系统。这不仅提升了用户在无法接听电话时的服务连续性,也为客户提供了便捷的沟通渠道。理解Twilio的Webhook机制和TwiML动词的灵活运用是实现此类复杂通信逻辑的关键。
以上就是Twilio实现呼叫转发与未接来电语音留言功能详解的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号