修改拨打电话
This commit is contained in:
parent
6493f7a13e
commit
ecd73ea53f
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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')]]);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ return [
|
|||
'password' => 'dgg@1234.',
|
||||
'port' => 8022,
|
||||
],
|
||||
|
||||
//拨打电话队列
|
||||
'fs_dial_key' => 'fs_dial_list',
|
||||
//队列响铃模式
|
||||
'strategy' => [
|
||||
'top-down' => '顺序振铃',
|
||||
|
|
|
|||
Loading…
Reference in New Issue