Passed
Push — develop ( dcb728...9b177f )
by Портнов
05:21
created

ActionHangupChan   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 211
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 45
eloc 121
dl 0
loc 211
rs 8.8
c 5
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A regMissedCall() 0 29 6
C hangupChanCheckSipTrtansfer() 0 56 15
F hangupChanEndCalls() 0 81 21
A execute() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like ActionHangupChan often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ActionHangupChan, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
4
namespace MikoPBX\Core\Workers\Libs\WorkerCallEvents;
5
6
7
use MikoPBX\Common\Models\CallDetailRecordsTmp;
8
use MikoPBX\Core\Asterisk\Configs\VoiceMailConf;
9
use MikoPBX\Core\System\Util;
10
use MikoPBX\Core\Workers\WorkerCallEvents;
11
12
class ActionHangupChan {
13
14
    public static function execute(WorkerCallEvents $worker, $data):void
15
    {
16
        $worker->removeActiveChan($data['agi_channel']);
17
18
        $channels       = [];
19
        $transfer_calls = [];
20
21
        self::hangupChanEndCalls($worker, $data, $transfer_calls, $channels);
22
        // Проверим, возможно это обычный трансфер.
23
        CreateRowTransfer::execute($worker, 'hangup_chan', $data, $transfer_calls);
24
        self::hangupChanCheckSipTrtansfer($worker, $data, $channels);
25
26
        // Очистим память.
27
        if(isset($worker->checkChanHangupTransfer[$data['agi_channel']])){
28
            unset($worker->checkChanHangupTransfer[$data['agi_channel']]);
29
        }
30
        if(isset($worker->mixMonitorChannels[$data['agi_channel']])){
31
            unset($worker->mixMonitorChannels[$data['agi_channel']]);
32
        }
33
    }
34
35
    /**
36
     * Обработка события уничтожения канала.
37
     * @param       $worker
38
     * @param array $data
39
     * @param array $transfer_calls
40
     * @param array $channels
41
     */
42
    private static function hangupChanEndCalls($worker, array $data, array &$transfer_calls, array &$channels):void{
43
        $filter         = [
44
            'linkedid=:linkedid: AND endtime = ""',
45
            'bind' => [
46
                'linkedid' => $data['linkedid'],
47
            ],
48
        ];
49
        /** @var CallDetailRecordsTmp $m_data */
50
        /** @var CallDetailRecordsTmp $row */
51
        $m_data    = CallDetailRecordsTmp::find($filter);
52
        $countRows = 0;
53
        $transferCdrAnswered = true;
54
        foreach ($m_data as $row) {
55
            if($row->dst_chan !== $data['agi_channel'] && $data['agi_channel'] !== $row->src_chan){
56
                continue;
57
            }
58
            $countRows++;
59
            $vmState = $data['VMSTATUS']??'';
60
            if($row->dst_num === VoiceMailConf::VOICE_MAIL_EXT && $vmState !== 'FAILED'){
61
                // Этот вызов будет заверщен событием voicemail_end
62
                continue;
63
            }
64
            if ($row->transfer === '1' && !empty($row->dst_chan)) {
65
                // Обязательно канал назначения не должен быть пустым.
66
                // Иначе это не переадресация.
67
                $transfer_calls[] = $row->toArray();
68
                $transferCdrAnswered = min($transferCdrAnswered, empty($row->answer));
69
            }
70
        }
71
        foreach ($m_data as $row) {
72
            if($row->dst_chan !== $data['agi_channel'] && $data['agi_channel'] !== $row->src_chan){
73
                continue;
74
            }
75
            $vmState = $data['VMSTATUS']??'';
76
            if($row->dst_num === VoiceMailConf::VOICE_MAIL_EXT && $vmState !== 'FAILED'){
77
                // Этот вызов будет заверщен событием voicemail_end
78
                continue;
79
            }
80
            if ($row->dialstatus === 'ORIGINATE') {
81
                $row->writeAttribute('dialstatus', '');
82
                if($row->answer === ''){
83
                    $newId = $row->linkedid.'_'.$row->src_num.'_'.substr($row->src_chan, strpos($row->src_chan,'-') +1);
84
                    $row->writeAttribute('UNIQUEID', $newId);
85
                }
86
            }
87
            $row->writeAttribute('endtime', $data['end']);
88
            $row->writeAttribute('transfer', 0);
89
            if($transferCdrAnswered === false && count($transfer_calls) === 2){
90
                // Завершение вызова при консультативной переадресации. Инициатор положил трубку до ответа dst.
91
                $row->writeAttribute('a_transfer', 1);
92
            }
93
            if ($data['dialstatus'] !== '') {
94
                if ($data['dialstatus'] === 'ORIGINATE') {
95
                    $row->writeAttribute('dst_chan', '');
96
                }
97
                $row->writeAttribute('dialstatus', $data['dialstatus']);
98
            }
99
            $res = $row->update();
100
            if ( ! $res) {
101
                Util::sysLogMsg('Action_hangup_chan', implode(' ', $row->getMessages()), LOG_DEBUG);
102
            }
103
104
            if ($row->src_chan !== $data['agi_channel']) {
105
                $channels[] = [
106
                    'chan' => $row->src_chan,
107
                    'did'  => $row->did,
108
                    'num'  => $row->src_num,
109
                    'out'  => true,
110
                ];
111
            } else {
112
                $worker->StopMixMonitor($row->dst_chan, 'hangupChanEndCalls');
113
                $channels[] = [
114
                    'chan' => $row->dst_chan,
115
                    'did'  => $row->did,
116
                    'num'  => $row->dst_num,
117
                    'out'  => false,
118
                ];
119
            }
120
        }
121
122
        self::regMissedCall($data, $countRows);
123
    }
124
125
    /**
126
     * Если hangup_chan единственный event.
127
     * @param array $data
128
     * @param int   $tmpCdrCount
129
     */
130
    private static function regMissedCall(array $data, int $tmpCdrCount):void
131
    {
132
        if($tmpCdrCount > 0 || $data['did'] === ''){
133
            return;
134
        }
135
        if(stripos($data['agi_channel'], 'local/') !== false){
136
            // Локальные каналы не логируем как пропущенные.
137
            return;
138
        }
139
        $filter         = [
140
            'linkedid=:linkedid: AND (src_chan=:src_chan: OR dst_chan=:dst_chan:)',
141
            'bind' => [
142
                'linkedid' => $data['linkedid'],
143
                'src_chan' => $data['agi_channel'],
144
                'dst_chan' => $data['agi_channel'],
145
            ],
146
        ];
147
        $m_data = CallDetailRecordsTmp::findFirst($filter);
148
        if($m_data !== null){
149
            return;
150
        }
151
        if(empty($data['UNIQUEID'])){
152
            $data['UNIQUEID'] = $data['agi_threadid'];
153
        }
154
        $time = (float)str_replace('mikopbx-', '', $data['linkedid']);
155
        $data['start']   = date("Y-m-d H:i:s.v", $time);
0 ignored issues
show
Bug introduced by
$time of type double is incompatible with the type integer|null expected by parameter $timestamp of date(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

155
        $data['start']   = date("Y-m-d H:i:s.v", /** @scrutinizer ignore-type */ $time);
Loading history...
156
        $data['endtime'] = $data['end'];
157
158
        InsertDataToDB::execute($data);
159
    }
160
161
    /**
162
     * Проверяем на SIP трансфер.
163
     * @param WorkerCallEvents $worker
164
     * @param $data
165
     * @param $channels
166
     */
167
    private static function hangupChanCheckSipTrtansfer(WorkerCallEvents $worker, $data, $channels):void{
168
        $not_local = (stripos($data['agi_channel'], 'local/') === false);
169
        if($not_local === false || $data['OLD_LINKEDID'] !== $data['linkedid']) {
170
            return;
171
        }
172
        $am = Util::getAstManager('off');
173
        $active_chans = $am->GetChannels(false);
174
        // Намек на SIP трансфер.
175
        foreach ($channels as $data_chan) {
176
            if ( ! in_array($data_chan['chan'], $active_chans, true)) {
177
                // Вызов уже завершен. Не интересно.
178
                continue;
179
            }
180
            $BRIDGEPEER = $am->GetVar($data_chan['chan'], 'BRIDGEPEER', null, false);
181
            if ( !is_string($BRIDGEPEER) || ! in_array($BRIDGEPEER, $active_chans, true)) {
182
                // Вызов уже завершен. Не интересно.
183
                continue;
184
            }
185
186
            $linkedid = $am->GetVar($data_chan['chan'], 'CHANNEL(linkedid)', null, false);
187
            if ( empty($linkedid) || $linkedid === $data['linkedid']) {
188
                continue;
189
            }
190
191
            $CALLERID = $am->GetVar($BRIDGEPEER, 'CALLERID(num)', null, false);
192
            $n_data['action']        = 'sip_transfer';
193
            $n_data['src_chan']      = $data_chan['out'] ? $data_chan['chan'] : $BRIDGEPEER;
194
            $n_data['src_num']       = $data_chan['out'] ? $data_chan['num'] : $CALLERID;
195
            $n_data['dst_chan']      = $data_chan['out'] ? $BRIDGEPEER : $data_chan['chan'];
196
            $n_data['dst_num']       = $data_chan['out'] ? $CALLERID : $data_chan['num'];
197
            $n_data['start']         = date('Y-m-d H:i:s');
198
            $n_data['answer']        = date('Y-m-d H:i:s');
199
            $n_data['linkedid']      = $linkedid;
200
            $n_data['UNIQUEID']      = $data['linkedid'] . Util::generateRandomString();
201
            $n_data['transfer']      = '0';
202
            if($worker->enableMonitor($n_data['src_num']??'', $n_data['dst_num']??'')){
203
                $n_data['recordingfile'] = $worker->MixMonitor($n_data['dst_chan'], $n_data['UNIQUEID'], null, null, 'hangupChanCheckSipTrtansfer');
204
            }
205
            $n_data['did']           = $data_chan['did'];
206
207
            InsertDataToDB::execute($n_data);
208
            $filter = [
209
                'linkedid=:linkedid:',
210
                'bind' => ['linkedid' => $data['linkedid']],
211
            ];
212
            $m_data = CallDetailRecordsTmp::find($filter);
213
            foreach ($m_data as $row) {
214
                $row->writeAttribute('linkedid', $linkedid);
215
                $row->save();
216
            }
217
218
            /**
219
             * Отправка UserEvent
220
             */
221
            $AgiData = base64_encode(json_encode($n_data));
222
            $am->UserEvent('CdrConnector', ['AgiData' => $AgiData]);
223
        } // Обход текущих каналов.
224
    }
225
}