修改拨打电话

This commit is contained in:
lilong@dgg.net 2020-07-02 17:43:37 +08:00
parent 6493f7a13e
commit ecd73ea53f
4 changed files with 247 additions and 20 deletions

View File

@ -103,7 +103,6 @@ class callcenterListen extends Command
if ($cause == 'Cancel') {
$billsec = 0;
}else{
if ($leaving_time && $answered_time){
$billsec = $leaving_time - $answered_time > 0 ? $leaving_time - $answered_time : 0;
}else{

View File

@ -0,0 +1,235 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
class swoole extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'swoole:cdr';
/**
* The console command description.
*
* @var string
*/
protected $description = '监听打电话';
protected $fs_dir = '/usr/local/freeswitch';
public $url = null;
public $machineId = 3;
public $asr_status_key = 'asr_status_key'; //控制是否开启分段录音asr识别的redis key
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->url = config('app.url');
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
while (true){
$data = Redis::lPop(config('freeswitch.fs_dial_key'));
if ($data == null){
sleep(2);
continue;
}
$data = json_decode($data,true);
$process = new \Swoole\Process(function () use ($data) {
$fs = new \Freeswitchesl();
$service = config('freeswitch.esl');
if (!$fs->connect($service['host'], $service['port'], $service['password'])){
Log::error("asr监听ESL未连接");
return false;
}
//监听的事件
$eventarr = [
'CHANNEL_CALLSTATE',
'CHANNEL_ANSWER',
//'RECORD_START',
//'RECORD_STOP',
'CHANNEL_HANGUP_COMPLETE',
];
if (isset($data['aleg_uuid'])){
$fs->filteruuid($data['aleg_uuid']);
}
if (isset($data['bleg_uuid'])){
$fs->filteruuid($data['bleg_uuid']);
}
if (isset($data['dial_str'])){
$fs->bgapi(base64_decode($data['dial_str']));
}
$answer_time = 0;
//录音目录
$filepath = $this->fs_dir . '/recordings/' . date('Y') . '/' . date('m') . '/' . date('d') . '/';
$fullfile = $filepath . 'full_' . md5($data['aleg_uuid'] . $data['bleg_uuid']) . '.wav';
$fs->events('plain', implode(" ",$eventarr));
while (true) {
$received_parameters = $fs->recvEvent();
if (!empty($received_parameters)) {
$serialize = $fs->serialize($received_parameters,'json');
$json = json_decode($serialize,true);
$eventname = Arr::get($json,'Event-Name',null); //事件名称
$uuid = Arr::get($json,'Unique-ID',null);//当前信道leg的uuid
$otherUuid = Arr::get($json,'Other-Leg-Unique-ID',null);
$CallerCallerIDNumber = Arr::get($json,"Caller-Caller-ID-Number"); //主叫
$CallerCalleeIDNumber = Arr::get($json,"Caller-Destination-Number"); //被叫
switch ($eventname) {
//呼叫状态
case 'CHANNEL_CALLSTATE':
//是分机号才记录
if (preg_match('/\d{4,5}/',$CallerCallerIDNumber)){
$state = Arr::get($json,'Channel-Call-State');
$uniqueid = Arr::get($json,'Caller-Unique-ID');
Redis::setex($CallerCallerIDNumber.'_uuid',1200, $uniqueid);
DB::table('sip')->where('username',$CallerCallerIDNumber)->update(['state'=>$state]);
}
break;
case 'CHANNEL_ANSWER':
if ($otherUuid) { //被叫应答后
$answer_time = time();
//开启全程录音
$fs->bgapi("uuid_record {$uuid} start {$fullfile} 7200"); //录音
if (Redis::get($this->asr_status_key)==1) {
//记录A分段录音数据
$halffile_a = $filepath . 'half_' . md5($otherUuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $otherUuid . " start " . $halffile_a . " 18");
Redis::set($otherUuid,json_encode([
'uuid' => $otherUuid,
'leg_uuid' => $otherUuid,
'record_file' => $halffile_a,
'full_record_file' => $fullfile,
'start_at' => date('Y-m-d H:i:s'),
'end_at' => null,
]));
//记录B分段录音数据
$halffile_b = $filepath . 'half_' . md5($uuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $uuid . " start " . $halffile_b . " 18");
Redis::set($uuid,json_encode([
'uuid' => $otherUuid,
'leg_uuid' => $uuid,
'record_file' => $halffile_b,
'full_record_file' => $fullfile,
'start_at' => date('Y-m-d H:i:s'),
'end_at' => null,
]));
unset($halffile_a);
unset($halffile_b);
}
}
break;
case 'RECORD_START':
$channel = Redis::get($uuid);
if ($channel){
$data = array_merge(json_decode($channel,true),[
'start_time' => date('Y-m-d H:i:s'),
]);
Redis::set($uuid,json_encode($data));
}
break;
case 'RECORD_STOP':
if (Redis::get($this->asr_status_key)==1) {
$channel = Redis::get($uuid);
if ($channel){
$data = json_decode($channel,true);
if (isset($data['record_file'])&&file_exists($data['record_file'])){
DB::table('asr')->insert([
'uuid' => $data['uuid'],
'leg_uuid' => $data['leg_uuid'],
'start_at' => $data['start_at'],
'end_at' => date('Y-m-d H:i:s'),
'billsec' => strtotime(date('Y-m-d H:i:s'))-strtotime($data['start_at']),
'record_file' => str_replace($this->fs_dir, $this->url, $data['record_file']),
'created_at' => date('Y-m-d H:i:s'),
]);
}
//结束说话 后接着开启分段录音
$halffile = $filepath . 'half_' . md5($uuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $uuid . " start " . $halffile . " 18");
Redis::set($uuid,json_encode(array_merge($data,[
'record_file' => $halffile,
'start_at' => date('Y-m-d H:i:s'),
'end_at' => null,
])));
unset($data);
unset($halffile);
}
unset($channel);
}
break;
case 'CHANNEL_HANGUP_COMPLETE':
$otherType = array_get($json,'Other-Type',null);
//A的挂机事件到来时线束进程
if (empty($otherType) || $otherType == 'originatee') {
$src = array_get($json,'Caller-Caller-ID-Number',null);
$dst = array_get($json,'Caller-Callee-ID-Number',null);
$customer_caller = array_get($json,'variable_customer_caller',null);
$dst = !empty($customer_caller)?$customer_caller:$dst;
$start = array_get($json,'variable_start_stamp',null);
$user_data = array_get($json,'variable_user_data',null);
$record_file = str_replace($this->fs_dir,$this->url,$fullfile);
$billsec = $answer_time!=0?time()-$answer_time:0;
try{
$user_data = decrypt($user_data);
}catch (\Exception $exception){
$user_data = null;
}
try {
$model = DB::table('sip')
->join('users','sip.id','=','users.sip_id')
->where('sip.username',$src)
->select(['users.id','users.department_id'])
->first();
if ($model == null) break 2;
$cdr = [
'user_id' => $model->id,
'uuid' => $uuid,
'aleg_uuid' => $uuid,
'bleg_uuid' => $uuid,
'direction' => 1,
'src' => $src,
'dst' => $dst,
'duration' => 0,
'billsec' => $billsec,
'record_file' => $record_file,
'user_data' => $user_data,
'created_at' => date('Y-m-d H:i:s'),
];
DB::table('cdr')->insert($cdr);
}catch (\Exception $exception){
Log::error('写入通话记录异常:'.$exception->getMessage(),$cdr);
}
break 2;
}
default:
# code...
break;
}
}
}
$fs->disconnect();
});
$process->start();
}
}
}

View File

@ -71,7 +71,10 @@ class ApiController extends Controller
if ($data['exten'] == null || $data['phone'] == null) {
return Response::json(['code'=>1,'msg'=>'号码不能为空']);
}
//验证手机号码
if (!preg_match('/\d{4,12}/', $data['phone'])) {
return Response::json(['code'=>1,'msg'=>'客户电话号码格式不正确']);
}
//检测10秒重复请求
if(Redis::get($data['exten'].'_check')!=null){
return Response::json(['code'=>1,'msg'=>'重复请求,请稍后再试']);
@ -90,20 +93,6 @@ class ApiController extends Controller
return Response::json(['code'=>1,'msg'=>'当前外呼号未登录']);
}
$fs = new \Freeswitchesl();
$service = config('freeswitch.esl');
try{
$fs->connect($service['host'],$service['port'],$service['password']);
}catch (\Exception $exception){
Log::info('拨打电话连接esl异常'.$exception->getMessage());
return Response::json(['code'=>1,'msg'=>'无法连接外呼服务']);
}
//验证手机号码
if (!preg_match('/\d{4,12}/', $data['phone'])) {
return Response::json(['code'=>1,'msg'=>'客户电话号码格式不正确']);
}
//呼叫字符串
$aleg_uuid = md5(\Snowflake::nextId(1).$data['exten'].$data['phone'].Redis::incr('fs_id'));
$bleg_uuid = md5(\Snowflake::nextId(2).$data['phone'].$data['exten'].Redis::incr('fs_id'));
@ -148,13 +137,16 @@ class ApiController extends Controller
}
$dialStr .= $data['phone']."_".$bleg_uuid;
}else{ //内部呼叫
$dialStr .="user/".$sip->username." ".$data["phone"];
$dialStr .="user/".$sip->username." ".$data["phone"]."_".$bleg_uuid;
}
$dialStr .=" XML default";
try{
$fs->bgapi($dialStr);
$fs->disconnect();
Redis::rPush(config('freeswitch.fs_dial_key'),json_encode([
'aleg_uuid' => $aleg_uuid,
'bleg_uuid' => $bleg_uuid,
'dial_str' => base64_encode($dialStr),
]));
//20分钟过期
Redis::setex($data['exten'].'_uuid',1200, $aleg_uuid);
return Response::json(['code'=>0,'msg'=>'呼叫成功','data'=>['uuid'=>$aleg_uuid,'time'=>date('Y-m-d H:i:s')]]);

View File

@ -40,7 +40,8 @@ return [
'password' => 'dgg@1234.',
'port' => 8022,
],
//拨打电话队列
'fs_dial_key' => 'fs_dial_list',
//队列响铃模式
'strategy' => [
'top-down' => '顺序振铃',