Passed
Push — develop ( 06fb87...d66898 )
by Nikolay
23:43
created

OutgoingContext::makeDialplan()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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