Passed
Push — develop ( a1000e...820560 )
by Портнов
05:57
created

WorkerCallEvents::otherEvents()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 12
rs 10
cc 4
nc 8
nop 2
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Workers;
21
require_once 'Globals.php';
22
23
use MikoPBX\Core\System\{BeanstalkClient, MikoPBXConfig, Storage, Util};
24
use MikoPBX\Core\Asterisk\Configs\CelConf;
25
use MikoPBX\Core\Workers\Libs\WorkerCallEvents\SelectCDR;
26
use MikoPBX\Core\Workers\Libs\WorkerCallEvents\UpdateDataInDB;
27
use Phalcon\Text;
28
29
class WorkerCallEvents extends WorkerBase
30
{
31
    public    array $mixMonitorChannels = [];
32
    protected bool  $record_calls       = true;
33
    protected bool  $split_audio_thread = false;
34
    public    array $checkChanHangupTransfer = [];
35
    public const TIMOUT_CHANNEL_TUBE = 'CleanChannelTimout';
36
37
38
    /**
39
     * Инициирует запись разговора на канале.
40
     *
41
     * @param string    $channel
42
     * @param ?string   $file_name
43
     * @param ?string   $sub_dir
44
     * @param ?string   $full_name
45
     *
46
     * @return string
47
     */
48
    public function MixMonitor($channel, $file_name = null, $sub_dir = null, $full_name = null): string
49
    {
50
        $resFile = $this->mixMonitorChannels[$channel]??'';
51
        if($resFile !== ''){
52
            return $resFile;
53
        }
54
        $resFile           = '';
55
        $file_name = str_replace('/', '_', $file_name);
56
        if ($this->record_calls) {
57
            [$f, $options] = $this->setMonitorFilenameOptions($full_name, $sub_dir, $file_name);
58
            $arr = $this->am->GetChannels(false);
59
            if(!in_array($channel, $arr, true)){
60
                return '';
61
            }
62
            $srcFile = "{$f}.wav";
63
            $resFile = "{$f}.mp3";
64
            $this->am->MixMonitor($channel, $srcFile, $options);
65
            $this->mixMonitorChannels[$channel] = $resFile;
66
            $this->am->UserEvent('StartRecording', ['recordingfile' => $resFile, 'recchan' => $channel]);
67
        }
68
        return $resFile;
69
    }
70
71
    /**
72
     * @param string|null $full_name
73
     * @param string|null $sub_dir
74
     * @param string|null $file_name
75
     * @return array
76
     */
77
    public function setMonitorFilenameOptions(?string $full_name, ?string $sub_dir, ?string $file_name): array{
78
        if (!file_exists((string)$full_name)) {
79
            $monitor_dir = Storage::getMonitorDir();
80
            if ($sub_dir === null) {
81
                $sub_dir = date('Y/m/d/H/');
82
            }
83
            $f = "{$monitor_dir}/{$sub_dir}{$file_name}";
84
        } else {
85
            $f = Util::trimExtensionForFile($full_name);
86
        }
87
        if ($this->split_audio_thread) {
88
            $options = "abSr({$f}_in.wav)t({$f}_out.wav)";
89
        } else {
90
            $options = 'ab';
91
        }
92
        return array($f, $options);
93
    }
94
95
    /**
96
     * Останавливает запись разговора на канале.
97
     * @param string $channel
98
     */
99
    public function StopMixMonitor($channel): void
100
    {
101
        if(isset($this->mixMonitorChannels[$channel])){
102
            unset($this->mixMonitorChannels[$channel]);
103
        }else{
104
            return;
105
        }
106
        if ($this->record_calls) {
107
            $this->am->StopMixMonitor($channel);
108
        }
109
    }
110
111
    /**
112
     *
113
     * @param $params
114
     */
115
    public function start($params): void
116
    {
117
        $this->mixMonitorChannels       = [];
118
        $this->checkChanHangupTransfer  = [];
119
        $mikoPBXConfig            = new MikoPBXConfig();
120
        $this->record_calls       = $mikoPBXConfig->getGeneralSettings('PBXRecordCalls') === '1';
121
        $this->split_audio_thread = $mikoPBXConfig->getGeneralSettings('PBXSplitAudioThread') === '1';
122
        $this->am                 = Util::getAstManager('off');
123
124
        // PID сохраняем при начале работы Worker.
125
        $client = new BeanstalkClient(self::class);
126
        $client->subscribe(CelConf::BEANSTALK_TUBE,    [$this, 'callEventsWorker']);
127
        $client->subscribe(self::class,                [$this, 'otherEvents']);
128
        $client->subscribe(WorkerCdr::SELECT_CDR_TUBE, [$this, 'selectCDRWorker']);
129
        $client->subscribe(WorkerCdr::UPDATE_CDR_TUBE, [$this, 'updateCDRWorker']);
130
        $client->subscribe(self::TIMOUT_CHANNEL_TUBE,  [$this, 'cleanTimeOutChannel']);
131
        $client->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
132
        $client->setErrorHandler([$this, 'errorHandler']);
133
134
        while ($this->needRestart === false) {
135
            $client->wait();
136
        }
137
    }
138
139
    /**
140
     * @param $tube
141
     * @param $data
142
     * @return void
143
     */
144
    public function otherEvents($tube, array $data=[]): void
145
    {
146
        if(empty($data)){
147
            $data = json_decode($tube->getBody(), true);
148
        }
149
        $funcName = "Action_".$data['action']??'';
150
        if ( method_exists($this, $funcName) ) {
151
            $this->$funcName($data);
152
        }
153
        $className = __NAMESPACE__.'\Libs\WorkerCallEvents\\'.Text::camelize($funcName, '_');
154
        if( method_exists($className, 'execute') ){
155
            $className::execute($this, $data);
156
        }
157
    }
158
159
    /**
160
     * Обработчик событий изменения состояния звонка.
161
     *
162
     * @param array | BeanstalkClient $tube
163
     */
164
    public function callEventsWorker($tube): void
165
    {
166
        $data      = json_decode($tube->getBody(), true);
167
        if('USER_DEFINED' !== ($data['EventName']??'')){
168
            return;
169
        }
170
        try {
171
            $data = json_decode(
172
                base64_decode($data['AppData']??''),
173
                true,
174
                512,
175
                JSON_THROW_ON_ERROR
176
            );
177
        }catch (\Throwable $e){
178
            $data = [];
179
        }
180
        $this->otherEvents($tube, $data);
181
    }
182
183
    /**
184
     * Получения CDR к обработке.
185
     *
186
     * @param array | BeanstalkClient $tube
187
     */
188
    public function updateCDRWorker($tube): void
189
    {
190
        $task    = $tube->getBody();
191
        $data = json_decode($task, true);
192
        UpdateDataInDB::execute($data);
193
        $tube->reply(json_encode(true));
194
    }
195
196
    /**
197
     * Получения CDR к обработке.
198
     *
199
     * @param array | BeanstalkClient $tube
200
     */
201
    public function cleanTimeOutChannel($tube): void
202
    {
203
        $task        = $tube->getBody();
204
        $taskData    = json_decode($task, true);
205
        $srcChannel  = $taskData['srcChannel']??'';
206
        $this->am->SetVar($srcChannel, "MASTER_CHANNEL(M_DIALSTATUS)", 'ANSWER');
207
        $tube->reply(json_encode(true));
208
    }
209
    /**
210
     * @param array | BeanstalkClient $tube
211
     */
212
    public function selectCDRWorker($tube): void
213
    {
214
        $filter   = json_decode($tube->getBody(), true);
215
        $res_data = SelectCDR::execute($filter);
216
        $tube->reply($res_data);
217
    }
218
219
    public function errorHandler($m): void
220
    {
221
        Util::sysLogMsg(self::class . '_ERROR', $m, LOG_ERR);
222
    }
223
}
224
225
226
// Start worker process
227
WorkerCallEvents::startWorker($argv??null);