Passed
Push — develop ( 47a9ce...330ac8 )
by Портнов
04:36
created

InternalContexts   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 11
eloc 110
c 4
b 0
f 0
dl 0
loc 232
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A generateInternalFW() 0 19 1
A getSettings() 0 3 1
A generate() 0 6 1
A generateAdditionalModulesContext() 0 7 1
A makeDialplan() 0 21 1
A generateAllPeers() 0 44 2
A generateInternal() 0 20 1
A generateInternalUsers() 0 39 1
A generateAdditionalModulesInternalUsersContext() 0 3 1
A generateAdditionalModulesInternalContext() 0 6 1
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2021 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\Asterisk\Configs\Generators\Extensions;
21
22
23
use MikoPBX\Core\Asterisk\Configs\ExtensionsConf;
24
use MikoPBX\Core\Asterisk\Configs\SIPConf;
25
use MikoPBX\Core\Asterisk\Configs\CoreConfigClass;
26
27
class InternalContexts extends CoreConfigClass
28
{
29
30
    private string $technology;
31
    private string $extensionPattern = 'X!';
32
33
    /**
34
     * Генератор входящих контекстов. Точка входа.
35
     *
36
     * @return string
37
     */
38
    public static function generate(): string
39
    {
40
        $generator = new self();
41
        $generator->getSettings();
42
43
        return $generator->makeDialplan();
44
    }
45
46
    public function getSettings(): void
47
    {
48
        $this->technology = SIPConf::getTechnology();
49
    }
50
51
    /**
52
     * Генерация dialplan.
53
     *
54
     * @return string
55
     */
56
    public function makeDialplan(): string
57
    {
58
        $conf = $this->generateAdditionalModulesContext();
59
60
        $conf .= "[internal-num-undefined] \n";
61
        $conf .= 'exten => _' . $this->extensionPattern . ',1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\t";
62
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t";
63
        $conf .= "same => n,Playback(pbx-invalid,noanswer) \n\n";
64
65
        $conf .= $this->generateInternalFW();
66
        $conf .= $this->generateAllPeers();
67
68
        $conf .= "[voice_mail_peer] \n";
69
        $conf .= 'exten => voicemail,1,Answer()' . "\n\t";
70
        $conf .= 'same => n,VoiceMail(admin@voicemailcontext)' . "\n\t";
71
        $conf .= 'same => n,Hangup()' . "\n\n";
72
73
        $conf .= $this->generateInternal();
74
        $conf .= $this->generateInternalUsers();
75
76
        return $conf;
77
    }
78
79
    /**
80
     * Генератор dialplan дополнительных модулей.
81
     *
82
     * @return string
83
     */
84
    private function generateAdditionalModulesContext(): string
85
    {
86
        $conf = '';
87
        $conf .= $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_CONTEXTS);
88
        $conf .= "\n";
89
90
        return $conf;
91
    }
92
93
    /**
94
     * Генератор [internal-fw] dialplan.
95
     *
96
     * @return string
97
     */
98
    private function generateInternalFW(): string
99
    {
100
        $conf = "[internal-fw]\n";
101
        $conf .= 'exten => _' . $this->extensionPattern . ',1,NoOp(DIALSTATUS - ${DIALSTATUS})' . "\n\t";
102
        // CANCEL - вызов был отменен, к примеру *0, не нужно дальше искать адресат.
103
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CANCEL"]?Hangup())' . "\n\t";
104
        // BUSY - занято. К примру абонент завершил вызов или DND.
105
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "BUSY"]?Set(dstatus=FW_BUSY))' . "\n\t";
106
        // CHANUNAVAIL - канал не доступен. К примеру телефон не зарегистрирован или не отвечает.
107
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CHANUNAVAIL"]?Set(dstatus=FW_UNAV))' . "\n\t";
108
        // NOANSWER - не ответили по таймауту.
109
        $conf .= 'same => n,ExecIf($["${dstatus}x" == "x"]?Set(dstatus=FW))' . "\n\t";
110
        $conf .= 'same => n,Set(fw=${DB(${dstatus}/${EXTEN})})' . "\n\t";
111
        $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Set(__pt1c_UNIQUEID=${UNDEFINED})' . "\n\t";
112
        $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Goto(internal,${fw},1))' . "\n\t";
113
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t";
114
        $conf .= 'same => n,Hangup() ' . "\n\n";
115
116
        return $conf;
117
    }
118
119
    /**
120
     * Генератор [all_peers] dialplan.
121
     *
122
     * @return string
123
     */
124
    private function generateAllPeers(): string
125
    {
126
        $conf = "[all_peers]\n";
127
        $conf .= 'include => internal-hints' . "\n";
128
        $conf .= 'exten => failed,1,Hangup()' . "\n";
129
130
        $conf .= 'exten => ' . ExtensionsConf::ALL_NUMBER_EXTENSION . ',1,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?Wait(0.2))' . PHP_EOL . "\t";
131
        $conf .= 'same => n,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?ChannelRedirect(${ORIGINATE_SRC_CHANNEL},${CONTEXT},${ORIGINATE_DST_EXTEN},1))' . PHP_EOL . "\t";
132
        $conf .= 'same => n,ExecIf($[ "${ORIGINATE_SRC_CHANNEL}x" != "x" ]?Hangup())' . PHP_EOL . "\t";
133
        // Фильтр спецсимволов. Разершаем только цифры.
134
        $conf .= 'same => n,Set(cleanNumber=${FILTER(\*\#\+1234567890,${EXTEN})})' . "\n\t";
135
        $conf .= 'same => n,ExecIf($["${EXTEN}" != "${cleanNumber}"]?Goto(${CONTEXT},${cleanNumber},$[${PRIORITY} + 1]))' . "\n\t";
136
137
        $conf .= 'same => n,Set(__FROM_CHAN=${CHANNEL})' . "\n\t";
138
        $conf .= 'same => n,ExecIf($["${OLD_LINKEDID}x" == "x"]?Set(__OLD_LINKEDID=${CHANNEL(linkedid)}))' . "\n\t";
139
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" != "Local"]?Gosub(set_from_peer,s,1))' . "\n\t";
140
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . "\n\t";
141
142
        $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(num)=${FROM_PEER}))' . "\n\t";
143
        $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(name)=${FROM_PEER}))' . "\n\t";
144
145
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local" && "${FROM_PEER}x" == "x"]?Set(__FROM_PEER=${CALLERID(num)}))' . "\n\t";
146
        $conf .= 'same => n,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . "\n\t";
147
        $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t";
148
149
        $conf          .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t";
150
        $dialplanNames = ['applications', 'internal', 'outgoing'];
151
        foreach ($dialplanNames as $name) {
152
            $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(' . $name . ',${EXTEN},1)}" == "1"]?' . $name . ',${EXTEN},1)' . " \n\t";
153
        }
154
        $conf .= 'same => n,Hangup()' . " \n";
155
156
        $pickupExtension = $this->generalSettings['PBXFeaturePickupExten'];
157
        $conf            .= 'exten => _' . $pickupExtension . $this->extensionPattern . ',1,Set(PICKUPEER=' . $this->technology . '/${FILTER(0-9,${EXTEN:2})})' . "\n\t";
158
        $conf            .= 'same => n,Set(pt1c_dnid=${EXTEN})' . "\n\t";
159
        $conf            .= 'same => n,PickupChan(${PICKUPEER})' . "\n\t";
160
        $conf            .= 'same => n,Hangup()' . "\n\n";
161
162
        $voicemailExtension = $this->generalSettings['VoicemailExten'];
163
        $conf               .= 'exten => ' . $voicemailExtension . ',1,NoOp(NOTICE, Dialing out from ${CALLERID(all)} to VoiceMail)' . "\n\t";
164
        $conf               .= 'same => n,VoiceMailMain(admin@voicemailcontext,s)' . "\n\t";
165
        $conf               .= 'same => n,Hangup()' . "\n\n";
166
167
        return $conf;
168
    }
169
170
    /**
171
     * Генератор [internal] dialplan.
172
     *
173
     * @return string
174
     */
175
    private function generateInternal(): string
176
    {
177
        // Контекст для внутренних вызовов.
178
        $conf = "[internal] \n";
179
        $conf .= $this->generateAdditionalModulesInternalContext();
180
181
        $conf .= 'exten => i,1,NoOp(-- INVALID NUMBER --)' . "\n\t";
182
        $conf .= 'same => n,Set(DIALSTATUS=INVALID_NUMBER)' . "\n\t";
183
        $conf .= 'same => n,Playback(privacy-incorrect,noanswer)' . "\n\t";
184
        $conf .= 'same => n,Hangup()' . "\n";
185
186
        $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\n";
187
188
        $conf .= "[internal-incoming]\n";
189
        $conf .= 'exten => ' . ExtensionsConf::ALL_NUMBER_EXTENSION . ',1,ExecIf($["${MASTER_CHANNEL(M_TIMEOUT)}x" != "x"]?Set(TIMEOUT(absolute)=${MASTER_CHANNEL(M_TIMEOUT)}))' . " \n\t";
190
        $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT_CHANNEL)=${CHANNEL})' . " \n\t";
191
        $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT)=${EMPTY_VAR})' . " \n\t";
192
        $conf .= 'same => n,Goto(internal,${EXTEN},1)' . " \n\n";
193
194
        return $conf;
195
    }
196
197
    /**
198
     * Переопределение dialplan [internal] из дополнительных модулей.
199
     *
200
     * @return string
201
     */
202
    private function generateAdditionalModulesInternalContext(): string
203
    {
204
        $conf = $this->hookModulesMethod(CoreConfigClass::GET_INCLUDE_INTERNAL);
205
        $conf .= $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_INTERNAL);
206
207
        return $conf;
208
    }
209
210
    private function generateAdditionalModulesInternalUsersContext():string
211
    {
212
        return $this->hookModulesMethod(CoreConfigClass::EXTENSION_GEN_INTERNAL_USERS_PRE_DIAL);
213
    }
214
215
    /**
216
     * Генератор [internal-users] dialplan.
217
     *
218
     * @return string
219
     */
220
    private function generateInternalUsers(): string
221
    {
222
        $conf = "[internal-users] \n";
223
        $conf .= 'exten => _' . $this->extensionPattern . ',1,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . " \n\t";
224
        $conf .= 'same => n,ExecIf($["${ISTRANSFER}x" != "x"]?Set(SIPADDHEADER01=${EMPTY_VAR})' . " \n\t";
225
        $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . " \n\t";
226
227
        $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t";
228
        // Проверим, существует ли такой пир.
229
        $conf .= 'same => n,ExecIf($["${PJSIP_ENDPOINT(${EXTEN},auth)}x" == "x"]?Goto(internal-num-undefined,${EXTEN},1))' . " \n\t";
230
231
        $conf .= $this->generateAdditionalModulesInternalUsersContext();
232
233
        $conf .= 'same => n,ExecIf($["${DEVICE_STATE(' . $this->technology . '/${EXTEN})}" == "BUSY"]?Set(DIALSTATUS=BUSY))' . " \n\t";
234
        $conf .= 'same => n,GotoIf($["${DEVICE_STATE(' . $this->technology . '/${EXTEN})}" == "BUSY"]?fw_start)' . " \n\t";
235
236
        // Как долго звонить пиру.
237
        $conf .= 'same => n,Set(ringlength=${DB(FW_TIME/${EXTEN})})' . " \n\t";
238
        $conf .= 'same => n,ExecIf($["${ringlength}x" == "x"]?Set(ringlength=600))' . " \n\t";
239
        $conf .= 'same => n,ExecIf($["${QUEUE_SRC_CHAN}x" != "x" && "${ISTRANSFER}x" == "x"]?Set(ringlength=600))' . " \n\t";
240
241
        $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1) ' . " \n\t";
242
        // Совершаем вызов пира.
243
        $conf .= 'same => n,Set(DST_CONTACT=${PJSIP_DIAL_CONTACTS(${EXTEN})})' . " \n\t";
244
        $conf .= 'same => n,ExecIf($["${FIELDQTY(DST_CONTACT,&)}" != "1"]?Set(__PT1C_SIP_HEADER=${EMPTY_VAR}))' . " \n\t";
245
        $conf .= 'same => n,ExecIf($["${TRANSFER_OPTIONS}x" == "x" || "${ISTRANSFER}x" != "x"]?Set(TRANSFER_OPTIONS=Tt))' . " \n\t";
246
        $conf .= 'same => n,ExecIf($["${DST_CONTACT}x" != "x"]?Dial(${DST_CONTACT},${ringlength},${TRANSFER_OPTIONS}ekKHhU(${ISTRANSFER}dial_answer)b(dial_create_chan,s,1)):Set(DIALSTATUS=CHANUNAVAIL))' . " \n\t";
247
        $conf .= 'same => n(fw_start),NoOp(dial_hangup)' . " \n\t";
248
249
        // QUEUE_SRC_CHAN - установлена, если вызов сервершен агенту очереди.
250
        // Проверяем нужна ли переадресация
251
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" != "ANSWER" && "${ISTRANSFER}x" != "x"]?Goto(internal-fw,${EXTEN},1))' . " \n\t";
252
        $conf .= 'same => n,ExecIf($["${DIALSTATUS}" != "ANSWER" && "${QUEUE_SRC_CHAN}x" == "x"]?Goto(internal-fw,${EXTEN},1))' . " \n\t";
253
        $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . " \n\t";
254
        $conf .= 'same => n,Hangup()' . "\n\n";
255
256
        $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\n";
257
258
        return $conf;
259
    }
260
261
}