Passed
Push — develop ( 8d717c...9d129a )
by Портнов
05:17 queued 10s
created

OutgoingContext::generateOutgoingRegexPattern()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 17
rs 9.8333
cc 4
nc 4
nop 1
1
<?php
2
3
4
namespace MikoPBX\Core\Asterisk\Configs\Generators\Extensions;
5
6
7
use MikoPBX\Common\Models\Iax;
8
use MikoPBX\Common\Models\OutgoingRoutingTable;
9
use MikoPBX\Common\Models\Providers;
10
use MikoPBX\Common\Models\Sip;
11
use MikoPBX\Common\Providers\PBXConfModulesProvider;
12
use MikoPBX\Core\Asterisk\Configs\ExtensionsConf;
13
use MikoPBX\Core\Asterisk\Configs\IAXConf;
14
use MikoPBX\Core\Asterisk\Configs\SIPConf;
15
use MikoPBX\Modules\Config\ConfigClass;
16
use Phalcon\Di;
17
18
class OutgoingContext extends ConfigClass {
19
20
21
    private array $additionalModules = [];
22
23
    public function getSettings(): void{
24
        $di = Di::getDefault();
25
        if ($di !== null) {
26
            $this->additionalModules = $di->getShared(PBXConfModulesProvider::SERVICE_NAME);
27
        }
28
    }
29
30
    /**
31
     * Генератор входящих контекстов. Точка входа.
32
     * @return string
33
     */
34
    public static function generate(): string{
35
        $generator = new self();
36
        $generator->getSettings();
37
        return $generator->makeDialplan();
38
    }
39
40
    public function makeDialplan(): string{
41
        $conf = "[outgoing] \n";
42
        $conf .= 'exten => _+.!,1,NoOp(Strip + sign from number and convert it to +)' . " \n\t";
43
        $conf .= 'same => n,Set(ADDPLUS=+);' . " \n\t";
44
        $conf .= 'same => n,Goto(${CONTEXT},${EXTEN:1},1);' . " \n\n";
45
        $conf .= 'exten => _.!,1,ExecIf($[ "${EXTEN}" == "h" ]?Hangup())' . " \n\t";
46
        $conf .= 'same => n,Ringing()' . " \n\t";
47
48
        // Описываем возможность прыжка в пользовательский sub контекст.
49
        $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t";
50
51
        $provider_contexts = $this->generateRegexPatternsContextNames($conf);
52
53
        $conf .= 'same => n,ExecIf($["${peer_mobile}x" != "x"]?Hangup())' . " \n\t";
54
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" != "ANSWER" && "${BLINDTRANSFER}x" != "x" && "${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\t";
55
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . " \n\t";
56
        $conf .= 'same => n,ExecIf($["${ROUTFOUND}x" == "x"]?Gosub(dial,${EXTEN},1))' . "\n\t";
57
58
        $conf .= 'same => n,Playback(silence/2,noanswer)' . " \n\t";
59
        $conf .= 'same => n,ExecIf($["${ROUTFOUND}x" != "x"]?Playback(followme/sorry,noanswer):Playback(cannot-complete-as-dialed,noanswer))' . " \n\t";
60
        $conf .= 'same => n,Hangup()' . " \n\n";
61
        $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\t";
62
63
        foreach ($provider_contexts as $id_dialplan => $rout) {
64
            $this->generateProviderContext($conf, $id_dialplan, $rout);
65
        }
66
        return $conf;
67
    }
68
69
    /**
70
     * Формирует имена конткестов провайдеров и шаблоны Regex для проверки номера.
71
     * @param string $conf
72
     * @return array
73
     */
74
    private function generateRegexPatternsContextNames(string &$conf): array{
75
        $routs = OutgoingRoutingTable::find(['order' => 'priority'])->toArray();
76
        uasort($routs, ExtensionsConf::class . '::sortArrayByPriority');
77
78
        $provider_contexts = [];
79
        /** @var OutgoingRoutingTable $routs */
80
        /** @var OutgoingRoutingTable $rout */
81
        foreach ($routs as $rout) {
82
            $technology = $this->getTechByID($rout['providerid']);
83
            if (empty($technology)) {
84
                continue;
85
            }
86
            $rout_data = $rout;
87
            $rout_data['technology'] = $technology;
88
            $id_dialplan = $rout_data['providerid'] . '-' . $rout_data['id'] . '-outgoing';
89
            $provider_contexts[$id_dialplan] = $rout_data;
90
            $conf .= $this->generateOutgoingRegexPattern($rout_data);
91
        }
92
93
        return $provider_contexts;
94
    }
95
96
    /**
97
     * Генератор extension для контекста outgoing.
98
     *
99
     * @param string $uniqueID
100
     *
101
     * @return null|string
102
     */
103
    public function getTechByID(string $uniqueID): string{
104
        $technology = '';
105
        $provider = Providers::findFirstByUniqid($uniqueID);
106
        if ($provider !== null) {
107
            if ($provider->type === 'SIP') {
108
                $account = Sip::findFirst('disabled="0" AND uniqid = "' . $uniqueID . '"');
109
                $technology = ($account === null) ? '' : SIPConf::getTechnology();
110
            } elseif ($provider->type === 'IAX') {
111
                $account = Iax::findFirst('disabled="0" AND uniqid = "' . $uniqueID . '"');
112
                $technology = ($account === null) ? '' : 'IAX2';
113
            }
114
        }
115
116
        return $technology;
117
    }
118
119
    /**
120
     * Генератор исходящего маршрута.
121
     *
122
     * @param $rout
123
     *
124
     * @return string
125
     */
126
    private function generateOutgoingRegexPattern($rout): string{
127
        $conf = '';
128
        $regexPattern = '';
129
130
        $restNumbers = (int)($rout['restnumbers'] ?? 0);
131
        if ($restNumbers > 0) {
132
            $regexPattern = "[0-9]{" . $rout['restnumbers'] . "}$";
133
        } elseif ($restNumbers === 0) {
134
            $regexPattern = "$";
135
        } elseif ($restNumbers === -1) {
136
            $regexPattern = "";
137
        }
138
        $numberBeginsWith = $rout['numberbeginswith'] ?? '';
139
        $numberBeginsWith = str_replace(array('*', '+'), array('\\\\*', '\\\\+'), $numberBeginsWith);
140
        $conf .= 'same => n,ExecIf($["${REGEX("^' . $numberBeginsWith . $regexPattern . '" ${EXTEN})}" == "1"]?Gosub(' . $rout['providerid'] . '-' . $rout['id'] . '-outgoing,${EXTEN},1))' . " \n\t";
141
142
        return $conf;
143
    }
144
145
    /**
146
     * @param string $conf
147
     * @param        $id_dialplan
148
     * @param        $rout
149
     */
150
    private function generateProviderContext(string &$conf, $id_dialplan, $rout): void{
151
        $conf .= "\n[{$id_dialplan}]\n";
152
        [$extensionVar, $changeExtension] = $this->initTrimVariables($rout);
153
154
        $conf .= 'exten => _.!,1,Set(number=' . $rout['prepend'] . $extensionVar . ')' . "\n\t";
155
        $conf .= 'same => n,Set(number=${FILTER(\*\#\+1234567890,${number})})' . "\n\t";
156
        $conf .= $changeExtension;
157
158
        $conf .= $this->generateProviderContextOutRoutModules($rout);
159
        $conf .= 'same => n,ExecIf($["${number}x" == "x"]?Hangup())' . "\n\t";
160
        $conf .= 'same => n,Set(ROUTFOUND=1)' . "\n\t";
161
        $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t";
162
163
        $conf .= 'same => n,ExecIf($["${EXTERNALPHONE}" == "${EXTEN}"]?Set(DOPTIONS=tk))' . "\n\t";
164
165
        // Описываем возможность прыжка в пользовательский sub контекст.
166
        $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(' . $rout['providerid'] . '-outgoing-custom,${EXTEN},1)}" == "1"]?' . $rout['providerid'] . '-outgoing-custom,${EXTEN},1)' . "\n\t";
167
168
        $conf .= $this->getDialCommand($rout);
169
        $conf .= $this->generateProviderContextAfterDialModules($rout);
170
171
        $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(' . $rout['providerid'] . '-outgoing-after-dial-custom,${EXTEN}),1}" == "1"]?' . $rout['providerid'] . '-outgoing-after-dial-custom,${EXTEN},1)' . "\n\t";
172
173
        $conf .= 'same => n,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\t";
174
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" = "ANSWER"]?Hangup())' . "\n\t";
175
        $conf .= 'same => n,Set(pt1c_UNIQUEID=${EMPTY_VALUE})' . "\n\t";
176
        $conf .= 'same => n,return' . "\n";
177
    }
178
179
    /**
180
     * Формирование исходящего dialplan доп. модулей;. Переопределение dialplan маршрута.
181
     * @param $rout
182
     * @return string
183
     */
184
    private function generateProviderContextOutRoutModules($rout): string{
185
        $conf = '';
186
        foreach ($this->additionalModules as $appClass) {
187
            $addition = $appClass->generateOutRoutContext($rout);
188
            if (!empty($addition)) {
189
                $conf .= $appClass->confBlockWithComments($addition);
190
            }
191
        }
192
        return $conf;
193
    }
194
195
    /**
196
     * Формирование dialplan доп. модулей после команды Dial.
197
     * @param $rout
198
     * @return string
199
     */
200
    private function generateProviderContextAfterDialModules($rout): string
201
    {
202
        $conf = '';
203
        foreach ($this->additionalModules as $appClass) {
204
            $addition = $appClass->generateOutRoutAfterDialContext($rout);
205
            if (!empty($addition)) {
206
                $conf .= $appClass->confBlockWithComments($addition);
207
            }
208
        }
209
        return $conf;
210
    }
211
212
    /**
213
     * Формирует Dial команду для технологии PJSIP / IAX2
214
     * @param        $rout
215
     * @return string
216
     */
217
    private function getDialCommand($rout): string{
218
        $conf = '';
219
        if ($rout['technology'] === IAXConf::TYPE_IAX2) {
220
            $conf .= 'same => n,Dial(' . $rout['technology'] . '/' . $rout['providerid'] . '/${number},600,${DOPTIONS}TKU(${ISTRANSFER}dial_answer)b(dial_create_chan,s,1))' . "\n\t";
221
        } else {
222
            $conf .= 'same => n,Dial(' . $rout['technology'] . '/${number}@' . $rout['providerid'] . ',600,${DOPTIONS}TKU(${ISTRANSFER}dial_answer)b(dial_create_chan,s,1))' . "\n\t";
223
        }
224
        return $conf;
225
    }
226
227
    /**
228
     * Проверка на необходимость обрезать номер телефона перед набором.
229
     * @param     $rout
230
     * @return string[]
231
     */
232
    private function initTrimVariables($rout): array{
233
        $trimFromBegin = (int)($rout['trimfrombegin'] ?? 0);
234
        if ($trimFromBegin > 0) {
235
            $extensionVar = '${ADDPLUS}${EXTEN:' . $rout['trimfrombegin'] . '}';
236
            $changeExtension = 'same => n,ExecIf($["${EXTEN}" != "${number}"]?Goto(${CONTEXT},${number},$[${PRIORITY} + 1]))' . "\n\t";
237
        } else {
238
            $extensionVar = '${ADDPLUS}${EXTEN}';
239
            $changeExtension = '';
240
        }
241
        return array($extensionVar, $changeExtension);
242
    }
243
}