修改通话记录监听

This commit is contained in:
lilong@dgg.net 2020-08-03 16:14:37 +08:00
parent f829990420
commit 674daf444a
4 changed files with 139 additions and 252 deletions

View File

@ -1,83 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
class eslCdr extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'esl:cdr';
/**
* The console command description.
*
* @var string
*/
protected $description = '从redis里取数据写入cdr';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$key = 'esl_cdr_key';
while (true){
$item = Redis::lPop($key);
if ($item){
try{
$data = json_decode($item,true);
if ($data['type']==1){ //通话记录
if ($data['leg_type']=='A'){
if (!$data['update_data']['src']) continue;
$model = DB::table('sip')
->join('users','sip.id','=','users.sip_id')
->where('sip.username',$data['update_data']['src'])
->select(['users.id','users.department_id'])
->first();
DB::table($data['table_name'])->updateOrInsert([
'uuid' => $data['uuid'],
],array_merge($data['update_data'],[
'user_id' => $model->id??null,
'created_at' => date('Y-m-d H:i:s'),
]));
}else{
DB::table($data['table_name'])->updateOrInsert([
'uuid' => $data['uuid'],
],$data['update_data']);
}
}else{ //分段记录
DB::table($data['table_name'])->insert(array_merge($data['update_data'],[
'created_at' => date('Y-m-d H:i:s'),
]));
}
}catch (\Exception $exception){
Log::info('写入通话记录异常:'.$exception->getMessage());
}
usleep(50);
}else{
sleep(10);
}
}
}
}

View File

@ -29,11 +29,9 @@ class eslListen extends Command
*/
protected $description = 'esl listen events';
protected $fs_dir = '/usr/local/freeswitch';
protected $recording_dir = 'recordings';
public $url = null;
public $machineId = 2;
public $asr_table = null;
public $cdr_table = null;
public $asr_status_key = 'asr_status_key'; //控制是否开启分段录音asr识别的redis key
public $hash_table = 'esl_listen';
/**
* Create a new command instance.
@ -43,7 +41,9 @@ class eslListen extends Command
public function __construct()
{
parent::__construct();
$this->url = config('app.url');
if ($this->url == null){
$this->url = config('app.url');
}
}
/**
@ -90,7 +90,7 @@ class eslListen extends Command
$fs->events('plain', $event);
while (true) {
//录音目录
$filepath = $this->fs_dir . '/recordings/' . date('Y') . '/' . date('m') . '/' . date('d') . '/';
$filepath = $this->fs_dir . '/' .$this->recording_dir. '/' . date('Y') . '/' . date('m') . '/' . date('d') . '/';
$received_parameters = $fs->recvEvent();
if (!empty($received_parameters)) {
$this->setTable();
@ -114,159 +114,125 @@ class eslListen extends Command
break;
//通道应答
case 'CHANNEL_ANSWER':
$otherUuid = Arr::get($info,"Other-Leg-Unique-ID");
$cdr_uuid = md5($uuid.Redis::incr('cdr_uuid_incr_key'));
Redis::setex($uuid,1800,json_encode([
'uuid' => $cdr_uuid,
Redis::hset($this->hash_table,$uuid,json_encode([
'pid' => $uuid,
'unique_id' => $uuid,
'record_file' => null,
'full_record_file' => null,
'start_time' => date('Y-m-d H:i:s'),
'end_time' => null,
]));
$otherUuid = Arr::get($info,"Other-Leg-Unique-ID");
if ($otherUuid) { //被叫应答后
//开启全程录音
$fullfile = $filepath . 'full_' . $cdr_uuid . '.wav';
$fullfile = $filepath . 'full_' . md5($otherUuid . $uuid) . '.wav';
$fs->bgapi("uuid_record {$uuid} start {$fullfile} 7200"); //录音
Redis::setex($otherUuid,1800,json_encode([
'uuid' => $cdr_uuid,
'full_record_file' => $fullfile,
]));
Redis::setex($uuid,1800,json_encode([
'uuid' => $cdr_uuid,
'full_record_file' => $fullfile,
]));
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::setex($otherUuid,1800,json_encode([
'uuid' => $cdr_uuid,
'leg_uuid' => $otherUuid,
'record_file' => $halffile_a,
'full_record_file' => $fullfile,
'start_at' => date('Y-m-d H:i:s'),
'end_at' => null,
]));
//记录A分段录音数据
$halffile_a = $filepath . 'half_' . md5($otherUuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $otherUuid . " start " . $halffile_a . " 18");
$a_data = json_decode(Redis::hget($this->hash_table,$otherUuid),true);
$a_data = array_merge($a_data,[
'record_file' => $halffile_a,
'full_record_file' => str_replace($this->fs_dir,$this->url,$fullfile),
]);
Redis::hset($this->hash_table,$otherUuid,json_encode($a_data));
unset($a_data);
unset($halffile_a);
//记录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' => $cdr_uuid,
'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);
}
//记录B分段录音数据
$halffile_b = $filepath . 'half_' . md5($uuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $uuid . " start " . $halffile_b . " 18");
$b_data = json_decode(Redis::hget($this->hash_table,$uuid),true);
$b_data = array_merge($b_data,[
'pid' => $otherUuid,
'record_file' => $halffile_b,
'full_record_file' => str_replace($this->fs_dir,$this->url,$fullfile),
]);
Redis::hset($this->hash_table,$uuid,json_encode($b_data));
unset($b_data);
unset($halffile_b);
//更新B接听时间
DB::table('cdr')->where('uuid',$otherUuid)->update([
'bleg_answer_at' => date('Y-m-d H:i:s'),
'record_file' => str_replace($this->fs_dir,$this->url,$fullfile),
'updated_at' => date('Y-m-d H:i:s'),
]);
unset($fullfile);
}else{
//更新A接听时间
DB::table('cdr')->where('uuid',$uuid)->update([
'aleg_start_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
unset($otherUuid);
break;
//开始说话
case 'RECORD_START':
$channel = Redis::get($uuid);
if ($channel){
$data = array_merge(json_decode($channel,true),[
'start_at' => date('Y-m-d H:i:s'),
if (Redis::hexists($this->hash_table,$uuid)){
$data = json_decode(Redis::hget($this->hash_table,$uuid),true);
$data = array_merge($data,[
'start_time' => date('Y-m-d H:i:s'),
]);
Redis::set($uuid,json_encode($data));
Redis::hset($this->hash_table,$uuid,json_encode($data));
unset($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'])){
Redis::rPush('esl_cdr_key',json_encode([
'table' => $this->asr_table,
'update_data' => [
'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']),
],
'type' => 2,
]));
}
//结束说话 后接着开启分段录音
$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);
if (Redis::hexists($this->hash_table,$uuid)){
$data = json_decode(Redis::hget($this->hash_table,$uuid),true);
$data['is_prologue'] += 1;
if (isset($data['record_file'])&&file_exists($data['record_file'])){
$table = 'asr';
DB::table($table)->insert([
'uuid' => $data['pid'],
'leg_uuid' => $data['unique_id'],
'start_at' => $data['start_time'],
'end_at' => date('Y-m-d H:i:s'),
'billsec' => strtotime(date('Y-m-d H:i:s'))-strtotime($data['start_time']),
'record_file' => str_replace($this->fs_dir, $this->url, $data['record_file']),
'created_at' => date('Y-m-d H:i:s'),
]);
unset($table);
}
unset($channel);
//结束说话 后接着开启分段录音
$halffile = $filepath . 'half_' . md5($uuid . time() . uniqid()) . '.wav';
$fs->bgapi("uuid_record " . $uuid . " start " . $halffile . " 18");
$data = array_merge($data,[
'record_file' => $halffile,
'start_time' => date('Y-m-d H:i:s'),
'end_time' => null,
]);
Redis::hset($this->hash_table,$uuid,json_encode($data));
unset($halffile);
unset($data);
}
break;
//挂断
case 'CHANNEL_HANGUP_COMPLETE':
$channel = Redis::get($uuid);
if ($channel){
$channelData = json_decode($channel,true);
$record_file = Arr::get($channelData,'full_record_file',null);
$record_file = $record_file ? str_replace($this->fs_dir,$this->url,$record_file) : null;
Redis::del($uuid);
}else{
$record_file = null;
continue 2; //继续外层的while循环
if (Redis::hexists($this->hash_table,$uuid)){
$data = json_decode(Redis::hget($this->hash_table,$uuid),true);
Redis::hdel($this->hash_table, $uuid);
$table = 'cdr';
$cdr = DB::table($table)->where('uuid',$data['pid'])->whereNull('aleg_end_at')->first();
if ($cdr != null){
$callsec = $cdr->bcalltime != null ? time()-strtotime($cdr->bleg_answer_at) : 0;
//更新通话时长
DB::table($table)->where('uuid',$data['pid'])->update([
'aleg_end_at' => date('Y-m-d H:i:s'),
'billsec' => $callsec,
]);
unset($callsec);
}
unset($data);
unset($table);
unset($cdr);
}
$otherType = Arr::get($info,'Other-Type');
$otherUuid = Arr::get($info,'Other-Leg-Unique-ID');
$start = Arr::get($info,'variable_start_stamp');
$answer = Arr::get($info,'variable_answer_stamp');
$end = Arr::get($info,'variable_end_stamp');
$extend_content = Arr::get($info,'variable_user_data',null);
$extend_content = $extend_content ? decrypt($extend_content) : $extend_content;
$duration = (int)Arr::get($info,'variable_duration',0);
$billsec = (int)Arr::get($info,'variable_billsec',0);
$customer_caller = Arr::get($info,'variable_customer_caller',null);
if (empty($otherType) || $otherType == 'originatee') {
Redis::del($CallerCallerIDNumber.'_uuid');
$data = [
'table_name' => $this->cdr_table,
'leg_type' => 'A',
'uuid' => $channelData['uuid'],
'update_data' => [
'aleg_uuid' => $uuid,
'src' => $CallerCallerIDNumber,
'dst' => $customer_caller?$customer_caller:$CallerCalleeIDNumber,
'aleg_start_at' => $start ? urldecode($start) : null,
'aleg_answer_at' => $answer ? urldecode($answer) : null,
'aleg_end_at' => $end ? urldecode($end) : null,
'duration' => $duration,
'record_file' => $record_file,
'user_data' => $extend_content,
],
'type' => 1,
];
}else{
$data = [
'table_name' => $this->cdr_table,
'leg_type' => 'B',
'uuid' => $channelData['uuid'],
'update_data' => [
'bleg_uuid' => $uuid,
'bleg_start_at' => $start ? urldecode($start) : null,
'bleg_answer_at' => $answer ? urldecode($answer) : null,
'bleg_end_at' => $end ? urldecode($end) : null,
'billsec' => $billsec,
],
'type' => 1,
];
}
Redis::rPush('esl_cdr_key',json_encode($data));
Redis::get($uuid);
unset($data);
break;
default:
break;

View File

@ -3,7 +3,9 @@
namespace App\Http\Controllers;
use App\Models\Audio;
use App\Models\Cdr;
use App\Models\Gateway;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Response;
@ -99,10 +101,7 @@ class ApiController extends Controller
$dialStr = "originate {origination_uuid=".$aleg_uuid."}";
$dialStr .= "{origination_caller_id_number=".$sip->username."}";
$dialStr .= "{origination_caller_id_name=".$sip->username."}";
//设置变量
if ($data['user_data']){
$dialStr .= "{user_data=".encrypt($data['user_data'])."}";
}
//验证内部呼叫还是外部呼叫
$res = Sip::where('username',$data['phone'])->first();
@ -142,17 +141,22 @@ class ApiController extends Controller
$dialStr .=" XML default";
try{
/*$fs = new \Freeswitchesl();
$fs = new \Freeswitchesl();
$service = config('freeswitch.esl');
$fs->connect($service['host'],$service['port'],$service['password']);
$fs->bgapi($dialStr);
$fs->disconnect();*/
Redis::rPush(config('freeswitch.fs_dial_key'),json_encode([
$fs->disconnect();
//写入初始记录
$user = User::where('sip_id',$sip->id)->first();
Cdr::create([
'user_id' => $user->id??0,
'uuid' => $aleg_uuid,
'aleg_uuid' => $aleg_uuid,
'bleg_uuid' => $bleg_uuid,
'dial_str' => base64_encode($dialStr),
]));
'src' => $data['exten'],
'dst' => $data['phone'],
'user_data' => $data['user_data'],
]);
//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

@ -59,32 +59,32 @@ redirect_stderr=true
[group:esl-custom]
programs=esl-custom-sofia_register,esl-custom-sofia_unregister
#[program:esl-listen-channel_answer]
#process_name=%(program_name)s_%(process_num)02d
#command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan esl:listen CHANNEL_ANSWER
#autostart=true
#autorestart=true
#user=www
#numprocs=1
#redirect_stderr=true
#[program:esl-listen-channel_hangup_complete]
#process_name=%(program_name)s_%(process_num)02d
#command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan esl:listen CHANNEL_HANGUP_COMPLETE
#autostart=true
#autorestart=true
#user=www
#numprocs=1
#redirect_stderr=true
#[group:esl-listen]
#programs=esl-listen-channel_answer,esl-listen-channel_hangup_complete
[program:swoole-cdr]
[program:esl-listen-channel_answer]
process_name=%(program_name)s_%(process_num)02d
command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan swoole:cdr
command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan esl:listen CHANNEL_ANSWER
autostart=true
autorestart=true
user=www
numprocs=1
redirect_stderr=true
[program:esl-listen-channel_hangup_complete]
process_name=%(program_name)s_%(process_num)02d
command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan esl:listen CHANNEL_HANGUP_COMPLETE
autostart=true
autorestart=true
user=www
numprocs=1
redirect_stderr=true
[group:esl-listen]
programs=esl-listen-channel_answer,esl-listen-channel_hangup_complete
#[program:swoole-cdr]
#process_name=%(program_name)s_%(process_num)02d
#command=/www/server/php/73/bin/php /www/wwwroot/wh.com/artisan swoole:cdr
#autostart=true
#autorestart=true
#user=www
#numprocs=1
#redirect_stderr=true