twilio/voice-sdk拨打Twilio号码时无法触发incoming来电监听问题
当前使用技术栈及版本
- Laravel (9.11)
- vue.js (2.x)
- php (8.1.0)
- twilio/voice-sdk (2.1.1)
- twilio/sdk (6.37)
应用业务流程
基于TaskRouter搭建语音呼入联络中心,客户通过手机拨打联络中心接入号码(+1 873 --- 0331)发起呼叫。
实现步骤
步骤1:IVR入口Webhook
用户拨打接入号码时触发语音webhook,执行如下IVR逻辑代码:
public function webhookForContactCenterBaseNumber(Request $request) { $response = new VoiceResponse(); $params = array(); $params['action'] = secure_url('/api/webhook-for-contact-center-ivr'); $params['numDigits'] = 1; $params['timeout'] = 10; $params['method'] = "POST"; $gather = $response->gather($params); $gather->say('For Spanish, please press one.', ['language' => 'es']); $gather->say('For Enghlish,please press two.', ['language' => 'en']); return $response; }
步骤2:按键处理与任务创建
用户按下数字键(1/2)后,通过TaskRouter结合工作流创建任务,对应代码如下:
public function webhookForContactCenterIvr(Request $request) { $response = new VoiceResponse(); $digits = $request['Digits']; $language = $digits == 1 ? 'es' : 'en'; switch ($digits) { case 1 || 2: $response->enqueue(null, [ 'waitUrl' => 'http://twimlets.com/holdmusic?Bucket=com.twilio.music.classical', 'workflowSid' => 'WW456fb07f4fdc4f55779dcb6bd90f9273' ]) ->task(json_encode([ 'selected_language' => $language, ])); break; default: $response->say("Sorry, Caller. You can only press 1 for spanish, or 2 for english."); break; } return $response; }
步骤3:坐席分配回调
任务创建完成后,在控制台手动将目标坐席状态设置为标签为idle的可用状态,随后触发如下分配回调webhook。根据官方文档说明,此时应通过Twilio caller id桥接主叫用户与坐席的Twilio号码建立通话:
public function assigment(Request $request) { $assignment_instruction = [ 'instruction' => 'dequeue', 'post_work_activity_sid' => 'WA92871fe67075e6556c02e92de6---924', 'from' => '+1647---4676' // Twilio账户下的已验证号码 ]; return $this->respond($assignment_instruction, ['Content-Type', 'application/json']); }
通话日志截图

步骤4:后端令牌生成逻辑
后端Twilio服务相关控制器代码如下:
namespace App\Http\Controllers\Api; use Twilio\Jwt\AccessToken; use Twilio\Jwt\Grants\VoiceGrant; use Illuminate\Http\Request; use Twilio\Rest\Client; class TwilioController extends ApiController { // Twilio访问令牌必填配置 private $twilioAccountSid; private $twilioAccountAuthToken; private $twilioApiKey; private $twilioApiSecret; private $identity; public function __construct() { $this->twilioAccountSid = config('general.twilio_account_sid'); $this->twilioAccountAuthToken = config('general.twilio_auth_token'); $this->twilioApiKey = 'SK45e57c57f923e5c3c0903f48b70ba9de'; $this->twilioApiSecret = 'uqDNnlnDZbWZCKBwlmMdlMIIonhh3X3K'; // 为接入用户设置固定身份标识 $this->identity = 'daffdfwerweds'; } public function getCallAccessToken() { $token = new AccessToken( $this->twilioAccountSid, $this->twilioApiKey, $this->twilioApiSecret, 3600, $this->identity ); // 创建语音权限授权 $voiceGrant = new VoiceGrant(); // 开启来电接收权限 $voiceGrant->setIncomingAllow(true); // 将授权写入令牌 $token->addGrant($voiceGrant); return $this->respond([ 'status' => true, 'message' => '', 'data' => [ 'accessToken' => $token->toJWT() ] ]); } public function getTwilioKey($frindlyName) { $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken); return $twilio->newKeys->create(["friendlyName" => $frindlyName]); } public function getKeys() { $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken); $keys = $twilio->keys ->read(20); foreach ($keys as $record) { $twilio->keys($record->sid) ->delete(); } } public function getAllCalls(Request $request) { $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken); $calls = $twilio->calls ->read([], 20); foreach ($calls as $record) { $twilio->calls($record->sid) ->delete(); } } }
步骤5:前端SDK注册逻辑
在Vue项目中安装twilio/voice-sdk,使用如下代码注册Device实例:
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImN0eSI6InR3aWxpby1mcGE7dj0xIn0.eyJqdGkiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlLTE2NTU3MzgxNjMiLCJpc3MiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlIiwic3ViIjoiQUMwMWExYTRmMDdjMGMwMDlhMmIyZTEyYmJkZWVhYjQ2NSIsImV4cCI6MTY1NTc0MTc2MywiZ3JhbnRzIjp7ImlkZW50aXR5IjoiZGFmZmRmd2Vyd2VkcyIsInZvaWNlIjp7ImluY29taW5nIjp7ImFsbG93Ijp0cnVlfX19fQ.4COIn-EQMQnD6alKUSOZPGIWG3jB5k17K418xCsSiZs" const device = new Device(accessToken, { logLevel: 1, // 优先设置Opus编码,该编码带宽占用更低、网络受限场景下音质表现更好 codecPreferences: ["opus", "pcmu"] }); const handleSuccessfulRegistration = () => { console.log('The device is ready to receive incoming calls.') } device.register(); device.on('registered', handleSuccessfulRegistration); device.on('error', (twilioError, call) => { console.log('An error has occurred: ', twilioError); }); device.on('incoming', call => { console.log('call received-----------------') });
验证结果
- JWT令牌校验结果:

- 控制台设备注册测试结果:

当前问题
设备已成功注册、Token已开启来电允许权限,但实际拨打Twilio号码测试时,Device实例的incoming事件始终无法触发,无法接收到来电回调。
内容的提问来源于stack exchange,提问作者shafeeque ahmad
相关产品推荐
相关产品推荐

