Total Complexity | 108 |
Total Lines | 758 |
Duplicated Lines | 0 % |
Changes | 4 | ||
Bugs | 0 | Features | 0 |
Complex classes like ExtensionsConf 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 ExtensionsConf, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class ExtensionsConf extends ConfigClass |
||
17 | { |
||
18 | protected string $description = 'extensions.conf'; |
||
19 | |||
20 | /** |
||
21 | * Основной генератор extensions.conf |
||
22 | */ |
||
23 | protected function generateConfigProtected(): void |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * Генератор прочих контекстов. |
||
60 | * |
||
61 | * @param $conf |
||
62 | */ |
||
63 | private function generateOtherExten(&$conf): void |
||
64 | { |
||
65 | $extension = 'X!'; |
||
66 | // Контекст для AMI originate. Без него отображается не корректный CallerID. |
||
67 | $conf .= '[sipregistrations]' . "\n\n"; |
||
68 | |||
69 | $conf .= '[messages]' . "\n" . |
||
70 | 'exten => _' . $extension . ',1,MessageSend(sip:${EXTEN},"${CALLERID(name)}"${MESSAGE(from)})' . "\n\n"; |
||
71 | |||
72 | $conf .= '[internal-originate]' . " \n"; |
||
73 | $conf .= 'exten => _' . $extension . ',1,NoOP(Hint ${HINT} exten ${EXTEN} )' . " \n"; |
||
74 | $conf .= '; Если это originate, то скроем один CDR.' . " \n\t"; |
||
75 | $conf .= 'same => n,ExecIf($["${pt1c_cid}x" != "x"]?Set(CALLERID(num)=${pt1c_cid}))' . " \n\t"; |
||
76 | |||
77 | $conf .= 'same => n,ExecIf($["${CUT(CHANNEL,\;,2)}" == "2"]?Set(__PT1C_SIP_HEADER=${SIPADDHEADER}))' . " \n\t"; |
||
78 | $conf .= 'same => n,ExecIf($["${peer_mobile}x" != "x"]?Set(ADDITIONAL_PEER=&Local/${peer_mobile}@outgoing/n))' . " \n\t"; |
||
79 | |||
80 | // Описываем возможность прыжка в пользовательский sub контекст. |
||
81 | $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t"; |
||
82 | $conf .= 'same => n,Dial(Local/${EXTEN}@internal-users/n${ADDITIONAL_PEER},60,cTteKkHhb(originate_create_chan,s,1))' . " \n\n"; |
||
83 | |||
84 | $conf .= '[originate_create_chan]' . " \n"; |
||
85 | $conf .= 'exten => s,1,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . "\n\t"; |
||
86 | $conf .= 'same => n,return' . " \n\n"; |
||
87 | |||
88 | $conf .= '[dial_create_chan]' . " \n"; |
||
89 | $conf .= 'exten => s,1,Gosub(lua_${ISTRANSFER}dial_create_chan,${EXTEN},1)' . "\n\t"; |
||
90 | $conf .= 'same => n,Set(pt1c_is_dst=1)' . " \n\t"; |
||
91 | $conf .= 'same => n,ExecIf($["${PT1C_SIP_HEADER}x" != "x"]?Set(PJSIP_HEADER(add,${CUT(PT1C_SIP_HEADER,:,1)})=${CUT(PT1C_SIP_HEADER,:,2)}))' . " \n\t"; |
||
92 | $conf .= 'same => n,Set(__PT1C_SIP_HEADER=${UNDEFINED})' . " \n\t"; |
||
93 | $conf .= 'same => n,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . " \n\t"; |
||
94 | $conf .= 'same => n,return' . " \n\n"; |
||
95 | |||
96 | $conf .= '[hangup_handler]' . "\n"; |
||
97 | $conf .= 'exten => s,1,NoOp(--- hangup - ${CHANNEL} ---)' . "\n\t"; |
||
98 | $conf .= 'same => n,Gosub(hangup_chan,${EXTEN},1)' . "\n\t"; |
||
99 | |||
100 | $conf .= 'same => n,return' . "\n\n"; |
||
101 | |||
102 | $conf .= '[set_orign_chan]' . "\n"; |
||
103 | $conf .= 'exten => s,1,Wait(0.2)' . "\n\t"; |
||
104 | $conf .= 'same => n,Set(pl=${IF($["${CHANNEL:-1}" == "1"]?2:1)})' . "\n\t"; |
||
105 | $conf .= 'same => n,Set(orign_chan=${IMPORT(${CUT(CHANNEL,\;,1)}\;${pl},BRIDGEPEER)})' . "\n\t"; |
||
106 | $conf .= 'same => n,ExecIf($[ "${orign_chan}x" == "x" ]?Set(orign_chan=${IMPORT(${CUT(CHANNEL,\;,1)}\;${pl},FROM_CHAN)}))' . "\n\t"; |
||
107 | $conf .= 'same => n,ExecIf($[ "${QUEUE_SRC_CHAN}x" != "x" ]?Set(__QUEUE_SRC_CHAN=${orign_chan}))' . "\n\t"; |
||
108 | $conf .= 'same => n,ExecIf($[ "${QUEUE_SRC_CHAN:0:5}" == "Local" ]?Set(__QUEUE_SRC_CHAN=${FROM_CHAN}))' . "\n\t"; |
||
109 | $conf .= 'same => n,ExecIf($[ "${FROM_CHAN}x" == "x" ]?Set(__FROM_CHAN=${IMPORT(${CUT(CHANNEL,\;,1)}\;${pl},BRIDGEPEER)}))' . "\n\t"; |
||
110 | $conf .= 'same => n,return' . "\n\n"; |
||
111 | |||
112 | $conf .= '[playback]' . "\n"; |
||
113 | $conf .= 'exten => s,1,Playback(hello_demo,noanswer)' . "\n\t"; |
||
114 | $conf .= 'same => n,ExecIf($["${SRC_BRIDGE_CHAN}x" == "x"]?Wait(30))' . "\n\t"; |
||
115 | $conf .= 'same => n,Wait(0.3)' . "\n\t"; |
||
116 | $conf .= 'same => n,Bridge(${SRC_BRIDGE_CHAN},kKTthH)' . "\n\n"; |
||
117 | |||
118 | $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\n"; |
||
119 | |||
120 | // TODO / Добавление / удаление префиксов на входящий callerid. |
||
121 | $conf .= '[add-trim-prefix-clid]' . "\n"; |
||
122 | $conf .= 'exten => _.!,1,NoOp(--- Incoming call from ${CALLERID(num)} ---)' . "\n\t"; |
||
123 | $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t"; |
||
124 | // Отсекаем "+". |
||
125 | // $conf.= 'same => n,ExecIf( $["${CALLERID(num):0:1}" == "+"]?Set(CALLERID(num)=${CALLERID(num):1}))'."\n\t"; |
||
126 | // Отсекаем "7" и добавляем "8". |
||
127 | // $conf.= 'same => n,ExecIf( $["${REGEX("^7[0-9]+" ${CALLERID(num)})}" == "1"]?Set(CALLERID(num)=8${CALLERID(num):1}))'."\n\t"; |
||
128 | $conf .= 'same => n,return' . "\n\n"; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Генератор контекста для внутренних вызовов. |
||
133 | * |
||
134 | * @param $conf |
||
135 | */ |
||
136 | private function generateInternal(&$conf): void |
||
137 | { |
||
138 | $extension = 'X!'; |
||
139 | $technology = SIPConf::getTechnology(); |
||
140 | |||
141 | $additionalModules = $this->di->getShared('pbxConfModules'); |
||
142 | foreach ($additionalModules as $appClass) { |
||
143 | $addition = $appClass->extensionGenContexts(); |
||
144 | if (!empty($addition)){ |
||
145 | $conf .=$appClass->confBlockWithComments($addition); |
||
146 | } |
||
147 | } |
||
148 | $conf .= "\n"; |
||
149 | $conf .= "[internal-num-undefined] \n"; |
||
150 | $conf .= 'exten => _' . $extension . ',1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\t"; |
||
151 | $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t"; |
||
152 | $conf .= "same => n,Playback(pbx-invalid,noanswer) \n\n"; |
||
153 | |||
154 | $conf .= "[internal-fw]\n"; |
||
155 | $conf .= 'exten => _' . $extension . ',1,NoOp(DIALSTATUS - ${DIALSTATUS})' . "\n\t"; |
||
156 | // CANCEL - вызов был отменен, к примеру *0, не нужно дальше искать адресат. |
||
157 | $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CANCEL"]?Hangup())' . "\n\t"; |
||
158 | // BUSY - занято. К примру абонент завершил вызов или DND. |
||
159 | $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "BUSY"]?Set(dstatus=FW_BUSY))' . "\n\t"; |
||
160 | // CHANUNAVAIL - канал не доступен. К примеру телефон не зарегистрирован или не отвечает. |
||
161 | $conf .= 'same => n,ExecIf($["${DIALSTATUS}" == "CHANUNAVAIL"]?Set(dstatus=FW_UNAV))' . "\n\t"; |
||
162 | // NOANSWER - не ответили по таймауту. |
||
163 | $conf .= 'same => n,ExecIf($["${dstatus}x" == "x"]?Set(dstatus=FW))' . "\n\t"; |
||
164 | $conf .= 'same => n,Set(fw=${DB(${dstatus}/${EXTEN})})' . "\n\t"; |
||
165 | $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Set(__pt1c_UNIQUEID=${UNDEFINED})' . "\n\t"; |
||
166 | $conf .= 'same => n,ExecIf($["${fw}x" != "x"]?Goto(internal,${fw},1))' . "\n\t"; |
||
167 | $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . "\n\t"; |
||
168 | $conf .= 'same => n,Hangup() ' . "\n\n"; |
||
169 | |||
170 | $conf .= "[all_peers]\n"; |
||
171 | $conf .= 'include => internal-hints' . "\n"; |
||
172 | $conf .= 'exten => failed,1,Hangup()' . "\n"; |
||
173 | |||
174 | $conf .= 'exten => _.!,1,ExecIf($[ "${EXTEN}" == "h" ]?Hangup())' . "\n\t"; |
||
175 | // Фильтр спецсимволов. Разершаем только цифры. |
||
176 | $conf .= 'same => n,Set(cleanNumber=${FILTER(\*\#1234567890,${EXTEN})})' . "\n\t"; |
||
177 | $conf .= 'same => n,ExecIf($["${EXTEN}" != "${cleanNumber}"]?Goto(${CONTEXT},${cleanNumber},$[${PRIORITY} + 1]))' . "\n\t"; |
||
178 | |||
179 | $conf .= 'same => n,Set(__FROM_CHAN=${CHANNEL})' . "\n\t"; |
||
180 | $conf .= 'same => n,ExecIf($["${OLD_LINKEDID}x" == "x"]?Set(__OLD_LINKEDID=${CDR(linkedid)}))' . "\n\t"; |
||
181 | $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" != "Local"]?Gosub(set_from_peer,s,1))' . "\n\t"; |
||
182 | $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . "\n\t"; |
||
183 | |||
184 | $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(num)=${FROM_PEER}))' . "\n\t"; |
||
185 | $conf .= 'same => n,ExecIf($["${CALLERID(num)}x" == "x"]?Set(CALLERID(name)=${FROM_PEER}))' . "\n\t"; |
||
186 | |||
187 | $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local" && "${FROM_PEER}x" == "x"]?Set(__FROM_PEER=${CALLERID(num)}))' . "\n\t"; |
||
188 | $conf .= 'same => n,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . "\n\t"; |
||
189 | $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t"; |
||
190 | |||
191 | // Описываем возможность прыжка в пользовательский sub контекст. |
||
192 | $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)' . "\n\t"; |
||
193 | |||
194 | $conf .= 'same => n,Goto(peer_${FROM_PEER},${EXTEN},1)' . "\n\n"; |
||
195 | |||
196 | $pickupexten = $this->generalSettings['PickupExten']; |
||
197 | $conf .= 'exten => _' . $pickupexten . $extension . ',1,Set(PICKUPEER=' . $technology . '/${FILTER(0-9,${EXTEN:2})})' . "\n\t"; |
||
198 | $conf .= 'same => n,Set(pt1c_dnid=${EXTEN})' . "\n\t"; |
||
199 | $conf .= 'same => n,PickupChan(${PICKUPEER})' . "\n\t"; |
||
200 | $conf .= 'same => n,Hangup()' . "\n\n"; |
||
201 | |||
202 | $voicemail_exten = $this->generalSettings['VoicemailExten']; |
||
203 | $conf .= 'exten => ' . $voicemail_exten . ',1,NoOp(NOTICE, Dialing out from ${CALLERID(all)} to VoiceMail)' . "\n\t"; |
||
204 | $conf .= 'same => n,VoiceMailMain(admin@voicemailcontext,s)' . "\n\t"; |
||
205 | $conf .= 'same => n,Hangup()' . "\n\n"; |
||
206 | |||
207 | $conf .= "[voice_mail_peer] \n"; |
||
208 | $conf .= 'exten => voicemail,1,Answer()' . "\n\t"; |
||
209 | $conf .= 'same => n,VoiceMail(admin@voicemailcontext)' . "\n\t"; |
||
210 | $conf .= 'same => n,Hangup()' . "\n\n"; |
||
211 | |||
212 | // Контекст для внутренних вызовов. |
||
213 | $conf .= "[internal] \n"; |
||
214 | |||
215 | foreach ($additionalModules as $appClass) { |
||
216 | $addition = $appClass->getIncludeInternal(); |
||
217 | if (!empty($addition)){ |
||
218 | $conf .=$appClass->confBlockWithComments($addition); |
||
219 | } |
||
220 | } |
||
221 | |||
222 | foreach ($additionalModules as $appClass) { |
||
223 | $addition = $appClass->extensionGenInternal(); |
||
224 | if (!empty($addition)){ |
||
225 | $conf .=$appClass->confBlockWithComments($addition); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | $conf .= 'exten => i,1,NoOp(-- INVALID NUMBER --)' . "\n\t"; |
||
230 | $conf .= 'same => n,Set(DIALSTATUS=INVALID_NUMBER)' . "\n\t"; |
||
231 | $conf .= 'same => n,Playback(privacy-incorrect,noanswer)' . "\n\t"; |
||
232 | $conf .= 'same => n,Hangup()' . "\n"; |
||
233 | |||
234 | $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\n"; |
||
235 | |||
236 | $conf .= "[internal-incoming]\n"; |
||
237 | $conf .= 'exten => _.!,1,ExecIf($["${MASTER_CHANNEL(M_TIMEOUT)}x" != "x"]?Set(TIMEOUT(absolute)=${MASTER_CHANNEL(M_TIMEOUT)}))' . " \n\t"; |
||
238 | $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT_CHANNEL)=${CHANNEL})' . " \n\t"; |
||
239 | $conf .= 'same => n,Set(MASTER_CHANNEL(M_TIMEOUT)=${EMPTY_VAR})' . " \n\t"; |
||
240 | $conf .= 'same => n,Goto(internal,${EXTEN},1)' . " \n\n"; |
||
241 | |||
242 | $conf .= "[internal-users] \n"; |
||
243 | $conf .= 'exten => _' . $extension . ',1,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . " \n\t"; |
||
244 | $conf .= 'same => n,ExecIf($["${ISTRANSFER}x" != "x"]?Set(SIPADDHEADER01=${EMPTY_VAR})' . " \n\t"; |
||
245 | $conf .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Gosub(set_orign_chan,s,1))' . " \n\t"; |
||
246 | |||
247 | $conf .= 'same => n,Gosub(${ISTRANSFER}dial,${EXTEN},1)' . "\n\t"; |
||
248 | // Проверим, существует ли такой пир. |
||
249 | |||
250 | $conf .= 'same => n,ExecIf($["${PJSIP_ENDPOINT(${EXTEN},auth)}x" == "x"]?Goto(internal-num-undefined,${EXTEN},1))' . " \n\t"; |
||
251 | $conf .= 'same => n,ExecIf($["${DEVICE_STATE(' . $technology . '/${EXTEN})}" == "BUSY"]?Set(DIALSTATUS=BUSY))' . " \n\t"; |
||
252 | $conf .= 'same => n,GotoIf($["${DEVICE_STATE(' . $technology . '/${EXTEN})}" == "BUSY"]?fw_start)' . " \n\t"; |
||
253 | |||
254 | // Как долго звонить пиру. |
||
255 | $conf .= 'same => n,Set(ringlength=${DB(FW_TIME/${EXTEN})})' . " \n\t"; |
||
256 | $conf .= 'same => n,ExecIf($["${ringlength}x" == "x" || "${QUEUE_SRC_CHAN}x" != "x"]?Set(ringlength=600))' . " \n\t"; |
||
257 | |||
258 | $conf .= 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1) ' . " \n\t"; |
||
259 | // Совершаем вызов пира. |
||
260 | $conf .= 'same => n,Set(DST_CONTACT=${PJSIP_DIAL_CONTACTS(${EXTEN})})' . " \n\t"; |
||
261 | $conf .= 'same => n,ExecIf($["${FIELDQTY(DST_CONTACT,&)}" != "1"]?Set(__PT1C_SIP_HEADER=${EMPTY_VAR}))' . " \n\t"; |
||
262 | $conf .= 'same => n,ExecIf($["${DST_CONTACT}x" != "x"]?Dial(${DST_CONTACT},${ringlength},cTtekKHhU(${ISTRANSFER}dial_answer)b(dial_create_chan,s,1)):Set(DIALSTATUS=CHANUNAVAIL))' . " \n\t"; |
||
263 | $conf .= 'same => n(fw_start),NoOp(dial_hangup)' . " \n\t"; |
||
264 | |||
265 | // QUEUE_SRC_CHAN - установлена, если вызов сервершен агенту очереди. |
||
266 | // Проверяем нужна ли переадресация |
||
267 | $expression = '$["${DIALSTATUS}" != "ANSWER" && "${QUEUE_SRC_CHAN}x" == "x"]'; |
||
268 | $conf .= 'same => n,ExecIf(' . $expression . '?Goto(internal-fw,${EXTEN},1))' . " \n\t"; |
||
269 | $conf .= 'same => n,ExecIf($["${BLINDTRANSFER}x" != "x"]?AGI(check_redirect.php,${BLINDTRANSFER}))' . " \n\t"; |
||
270 | $conf .= 'same => n,Hangup()' . "\n\n"; |
||
271 | |||
272 | $conf .= 'exten => h,1,ExecIf($["${ISTRANSFER}x" != "x"]?Gosub(${ISTRANSFER}dial_hangup,${EXTEN},1))' . "\n\n"; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Генератор контекста для переадресаций. |
||
277 | * |
||
278 | * @param $conf |
||
279 | */ |
||
280 | private function generateInternalTransfer(&$conf): void |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Генератор хинтов SIP. |
||
303 | * |
||
304 | * @param $conf |
||
305 | */ |
||
306 | private function generateSipHints(&$conf): void |
||
307 | { |
||
308 | $additionalModules = $this->di->getShared('pbxConfModules'); |
||
309 | $conf .= "[internal-hints] \n"; |
||
310 | foreach ($additionalModules as $appClass) { |
||
311 | $addition = $appClass->extensionGenHints(); |
||
312 | if (!empty($addition)){ |
||
313 | $conf .=$appClass->confBlockWithComments($addition); |
||
314 | } |
||
315 | } |
||
316 | $conf .= "\n\n"; |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * Генератор исходящих контекстов. |
||
321 | * |
||
322 | * @param $conf |
||
323 | */ |
||
324 | private function generateOutContextPeers(&$conf): void |
||
408 | } |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Генератор исходящего маршрута. |
||
413 | * |
||
414 | * @param $rout |
||
415 | * |
||
416 | * @return string |
||
417 | */ |
||
418 | private function generateOutgoingRegexPattern($rout): string |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * Контекст для входящих внешних звонков без авторизации. |
||
437 | * |
||
438 | * @param $conf |
||
439 | */ |
||
440 | public function generatePublicContext(&$conf): void |
||
460 | } |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Генератор входящих контекстов. |
||
465 | * |
||
466 | * @param string | array $provider |
||
467 | * @param string | array $login |
||
468 | * @param string $uniqid |
||
469 | * |
||
470 | * @return string |
||
471 | */ |
||
472 | public static function generateIncomingContextPeers($provider, $login = '', $uniqid = ''): string |
||
473 | { |
||
474 | $conf = ''; |
||
475 | $dialplan = []; |
||
476 | $di = Di::getDefault(); |
||
477 | if ($di===null){ |
||
478 | return ''; |
||
479 | } |
||
480 | $additionalModules = $di->getShared('pbxConfModules'); |
||
481 | |||
482 | if ('none' === $provider) { |
||
483 | // Звонки по sip uri. |
||
484 | $filter = [ |
||
485 | 'provider IS NULL AND priority<>9999', |
||
486 | 'order' => 'provider,priority,extension', |
||
487 | ]; |
||
488 | } elseif (is_array($provider)) { |
||
489 | $filter = [ |
||
490 | 'provider IN ({provider:array})', |
||
491 | 'bind' => [ |
||
492 | 'provider' => array_keys($provider), |
||
493 | ], |
||
494 | 'order' => 'provider,priority,extension', |
||
495 | ]; |
||
496 | } else { |
||
497 | // Звонки через провайдера. |
||
498 | $filter = [ |
||
499 | "provider = '$provider'", |
||
500 | 'order' => 'provider,priority,extension', |
||
501 | ]; |
||
502 | } |
||
503 | /** @var \MikoPBX\Common\Models\IncomingRoutingTable $default_action */ |
||
504 | $default_action = IncomingRoutingTable::findFirst('priority = 9999'); |
||
505 | /** @var \MikoPBX\Common\Models\IncomingRoutingTable $m_data */ |
||
506 | $m_data = IncomingRoutingTable::find($filter); |
||
507 | $data = $m_data->toArray(); |
||
508 | |||
509 | $need_def_rout = true; |
||
510 | foreach ($data as $rout) { |
||
511 | $number = trim($rout['number']); |
||
512 | if ($number === 'X!' || $number === '') { |
||
513 | $need_def_rout = false; |
||
514 | break; |
||
515 | } |
||
516 | } |
||
517 | if ($need_def_rout === true && 'none' !== $provider) { |
||
518 | $data[] = ['number' => '', 'extension' => '', 'timeout' => '']; |
||
519 | } |
||
520 | $config = new MikoPBXConfig(); |
||
521 | $lang = str_replace('_', '-', $config->getGeneralSettings('PBXLanguage')); |
||
522 | |||
523 | $rout_data_dial = []; |
||
524 | foreach ($data as $rout) { |
||
525 | $number = trim($rout['number']); |
||
526 | $timeout = trim($rout['timeout']); |
||
527 | $rout_number = ($number === '') ? 'X!' : $number; |
||
528 | $rout_data = &$dialplan[$rout_number]; |
||
529 | if (empty($rout_data)) { |
||
530 | $ext_prefix = ('none' === $provider) ? '' : '_'; |
||
531 | $rout_data .= "exten => {$ext_prefix}{$rout_number},1,NoOp(--- Incoming call ---)\n\t"; |
||
532 | $rout_data .= 'same => n,Set(CHANNEL(language)=' . $lang . ')' . "\n\t"; |
||
533 | $rout_data .= 'same => n,Set(CHANNEL(hangup_handler_wipe)=hangup_handler,s,1)' . "\n\t"; |
||
534 | $rout_data .= 'same => n,Set(__FROM_DID=${EXTEN})' . "\n\t"; |
||
535 | $rout_data .= 'same => n,Set(__FROM_CHAN=${CHANNEL})' . "\n\t"; |
||
536 | |||
537 | // Установка имени пира. |
||
538 | $rout_data .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" != "Local"]?Gosub(set_from_peer,s,1))' . "\n\t"; |
||
539 | $rout_data .= 'same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local"]?Set(__FROM_PEER=${CALLERID(num)}))' . "\n\t"; |
||
540 | |||
541 | // TODO / Подмена входящего callerid. |
||
542 | $rout_data .= 'same => n,Gosub(add-trim-prefix-clid,${EXTEN},1)' . "\n\t"; |
||
543 | |||
544 | foreach ($additionalModules as $appClass) { |
||
545 | $addition = $appClass->generateIncomingRoutBeforeDial($rout_number); |
||
546 | if (!empty($addition)){ |
||
547 | $rout_data .=$appClass->confBlockWithComments($addition); |
||
548 | } |
||
549 | } |
||
550 | |||
551 | // Перехват на ответственного. |
||
552 | $rout_data .= 'same => n,UserEvent(Interception,CALLERID: ${CALLERID(num)},chan1c: ${CHANNEL},FROM_DID: ${FROM_DID})' . "\n\t"; |
||
553 | // Проверим распискние для входящих внешних звонков. |
||
554 | $rout_data .= 'same => n,Gosub(check-out-work-time,${EXTEN},1)'; |
||
555 | // Описываем возможность прыжка в пользовательский sub контекст. |
||
556 | $rout_data .= " \n\t" . 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-custom,${EXTEN},1)'; |
||
557 | } |
||
558 | |||
559 | if ( ! empty($rout['extension'])) { |
||
560 | $rout_data = rtrim($rout_data); |
||
561 | // Обязательно проверяем "DIALSTATUS", в случае с парковой через AMI вызова это необходимо. |
||
562 | // При ответе может отработать следующий приоритет. |
||
563 | if ( ! isset($rout_data_dial[$rout_number])) { |
||
564 | $rout_data_dial[$rout_number] = ''; |
||
565 | } |
||
566 | |||
567 | $dial_command = " \n\t" . 'same => n,' . 'ExecIf($["${M_DIALSTATUS}" != "ANSWER"]?' . "Dial(Local/{$rout['extension']}@internal-incoming/n,{$timeout},cTKg));"; |
||
568 | $rout_data_dial[$rout_number] .= " \n\t" . "same => n,Set(M_TIMEOUT={$timeout})"; |
||
569 | $rout_data_dial[$rout_number] .= $dial_command; |
||
570 | |||
571 | if (is_array($provider)) { |
||
572 | $key = $provider[$rout['provider']] ?? ''; |
||
573 | if ( ! isset($rout_data_dial[$key])) { |
||
574 | $rout_data_dial[$key] = ''; |
||
575 | } |
||
576 | if (empty($number)) { |
||
577 | $rout_data_dial[$key] .= $dial_command; |
||
578 | } |
||
579 | } |
||
580 | } |
||
581 | } |
||
582 | |||
583 | if (is_string($login)) { |
||
584 | $add_login_pattern = ! empty($login); |
||
585 | foreach ($data as $rout) { |
||
586 | if ( ! $add_login_pattern) { |
||
587 | break; |
||
588 | } // Логин не заполнен, обработка не требуется. |
||
589 | $is_num = preg_match_all('/^\d+$/m', $login, $matches, PREG_SET_ORDER, 0); |
||
590 | if ($is_num === 1) { |
||
591 | // Это числовой номер, потому, не требуется дополнительно описывать exten. |
||
592 | $add_login_pattern = false; |
||
593 | break; |
||
594 | } |
||
595 | if (trim($rout['number']) !== $login) { |
||
596 | // Совпадение exten не найдено. Идем дальше. |
||
597 | continue; |
||
598 | } |
||
599 | // Совпадение найдено, не требуется дополнительно описывать exten. |
||
600 | $add_login_pattern = false; |
||
601 | break; |
||
602 | } |
||
603 | if ($add_login_pattern && array_key_exists('X!', $rout_data_dial) && isset($dialplan['X!'])) { |
||
604 | $dialplan[$login] = str_replace('_X!,1', "{$login},1", $dialplan['X!']); |
||
605 | $rout_data_dial[$login] = $rout_data_dial['X!']; |
||
606 | }elseif($add_login_pattern === true && $need_def_rout === true && count($data) === 1){ |
||
607 | // Только маршрут "По умолчанию". |
||
608 | $dialplan[$login] = str_replace('_X!,1', "{$login},1", $dialplan['X!']); |
||
609 | } |
||
610 | } elseif (is_array($provider)) { |
||
611 | foreach (array_values($provider) as $_login) { |
||
612 | $dialplan[$_login] = str_replace('_X!,1', "{$_login},1", $dialplan['X!']); |
||
613 | } |
||
614 | } |
||
615 | |||
616 | foreach ($dialplan as $key => &$dpln) { |
||
617 | if ( ! array_key_exists($key, $rout_data_dial)) { |
||
618 | continue; |
||
619 | } |
||
620 | $dpln = rtrim($dpln); |
||
621 | $dpln .= $rout_data_dial[$key]; |
||
622 | } |
||
623 | unset($dpln); |
||
624 | |||
625 | $uniqid = is_string($provider) ? $provider : $uniqid; |
||
626 | $conf .= "\n" . "[{$uniqid}-incoming]\n"; |
||
627 | foreach ($dialplan as $dpln) { |
||
628 | $conf .= $dpln . "\n"; |
||
629 | if (null == $default_action && 'none' !== $provider) { |
||
630 | continue; |
||
631 | } |
||
632 | if ('extension' === $default_action->action) { |
||
633 | // Обязательно проверяем "DIALSTATUS", в случае с парковой через AMI вызова это необходимо. |
||
634 | // При ответе может отработать следующий приоритет. |
||
635 | $conf .= "\t" . 'same => n,Set(M_TIMEOUT=0)' . "\n"; |
||
636 | $conf .= "\t" . "same => n," . 'ExecIf($["${M_DIALSTATUS}" != "ANSWER"]?' . "Dial(Local/{$default_action->extension}@internal/n,,cTKg)); default action" . "\n"; |
||
637 | |||
638 | foreach ($additionalModules as $appClass) { |
||
639 | $addition = $appClass->generateIncomingRoutAfterDialContext($uniqid); |
||
640 | if (!empty($addition)){ |
||
641 | $conf .=$appClass->confBlockWithComments($addition); |
||
642 | } |
||
643 | } |
||
644 | $conf .= " \t" . 'same => n,GosubIf($["${DIALPLAN_EXISTS(${CONTEXT}-after-dial-custom,${EXTEN},1)}" == "1"]?${CONTEXT}-after-dial-custom,${EXTEN},1)' . "\n"; |
||
645 | } elseif ('busy' === $default_action->action) { |
||
646 | $conf .= "\t" . "same => n,Busy()" . "\n"; |
||
647 | } |
||
648 | $conf .= "\t" . "same => n,Hangup()" . "\n"; |
||
649 | } |
||
650 | |||
651 | return $conf; |
||
652 | } |
||
653 | |||
654 | /** |
||
655 | * Описываем нерабочее время. |
||
656 | * |
||
657 | * @param $conf |
||
658 | * |
||
659 | * @return string |
||
660 | */ |
||
661 | private function generateOutWorkTimes(&$conf): string |
||
662 | { |
||
663 | $conf .= "\n\n[playback-exit]\n"; |
||
664 | $conf .= 'exten => _.!,1,NoOp(check time)' . "\n\t"; |
||
665 | $conf .= 'same => n,Gosub(dial_outworktimes,${EXTEN},1)' . "\n\t"; |
||
666 | $conf .= 'same => n,Playback(${filename})' . "\n\t"; |
||
667 | $conf .= 'same => n,Hangup()' . "\n\n"; |
||
668 | |||
669 | $conf .= "[check-out-work-time]\n"; |
||
670 | $conf .= "exten => _.!,1,NoOp(check time)\n\t"; |
||
671 | |||
672 | $data = OutWorkTimes::find(['order' => 'date_from']); |
||
673 | |||
674 | $conf_out_set_var = ''; |
||
675 | |||
676 | $now_year = 1 * date('Y', time()); |
||
677 | foreach ($data as $out_data) { |
||
678 | /** @var \MikoPBX\Common\Models\OutWorkTimes $out_data */ |
||
679 | if ( ! empty($out_data->date_from) && ! empty($out_data->date_to)) { |
||
680 | $year_from = 1 * date('Y', (int)$out_data->date_from); |
||
681 | $year_to = 1 * date('Y', (int)$out_data->date_to); |
||
682 | if ($now_year < $year_from || $now_year > $year_to) { |
||
683 | // Правило не актуально для текущего года. |
||
684 | continue; |
||
685 | } |
||
686 | } |
||
687 | $time_from = $out_data->time_from; |
||
688 | $time_to = $out_data->time_to; |
||
689 | if (empty($time_from) && empty($time_to)) { |
||
690 | $times = '*'; |
||
691 | } else { |
||
692 | $time_to = (empty($time_to)) ? '23:59' : $time_to; |
||
693 | $time_to = (strlen($time_to) === 4) ? "0{$time_to}" : $time_to; |
||
694 | |||
695 | $time_from = (empty($time_from)) ? '00:00' : $time_from; |
||
696 | $time_from = (strlen($time_from) === 4) ? "0{$time_from}" : $time_from; |
||
697 | |||
698 | $times = "{$time_from}-{$time_to}"; |
||
699 | } |
||
700 | |||
701 | $weekday_from = (string) $out_data->weekday_from; |
||
702 | $weekday_to = (string) $out_data->weekday_to; |
||
703 | $arr_weekday = [null, "mon", "tue", "wed", "thu", "fri", "sat", "sun"]; |
||
704 | if (empty($weekday_from) && empty($weekday_to)) { |
||
705 | $weekdays = '*'; |
||
706 | } else { |
||
707 | $weekday_from = (empty($weekday_from)) ? '1' : $weekday_from; |
||
708 | $weekday_to = (empty($weekday_to)) ? '7' : $weekday_to; |
||
709 | $weekdays = "{$arr_weekday[$weekday_from]}-{$arr_weekday[$weekday_to]}"; |
||
710 | } |
||
711 | |||
712 | $date_from = $out_data->date_from; |
||
713 | $date_to = $out_data->date_to; |
||
714 | if (empty($date_from)) { |
||
715 | $mdays = '*'; |
||
716 | $months = '*'; |
||
717 | } else { |
||
718 | $mdays = strtolower(date("j", (int)$date_from)); |
||
719 | $months = strtolower(date("M", (int)$date_from)); |
||
720 | if ( ! empty($date_to)) { |
||
721 | $mdays .= "-" . strtolower(date("j", (int)$date_to)); |
||
722 | $months .= "-" . strtolower(date("M", (int)$date_to)); |
||
723 | } |
||
724 | } |
||
725 | |||
726 | |||
727 | if ('extension' === $out_data->action) { |
||
728 | $appname = 'GotoIfTime'; |
||
729 | $appdata = "internal,{$out_data->extension},1"; |
||
730 | } else { |
||
731 | /** @var \MikoPBX\Common\Models\SoundFiles $res */ |
||
732 | $res = SoundFiles::findFirst($out_data->audio_message_id); |
||
733 | $audio_message = ($res === null) ? '' : Util::trimExtensionForFile($res->path); |
||
734 | |||
735 | $conf_out_set_var .= "[work-time-set-var-{$out_data->id}]\n" . |
||
736 | 'exten => _.!,1,Set(filename=' . $audio_message . ')' . "\n\t" . |
||
737 | 'same => n,Goto(playback-exit,${EXTEN},1)' . "\n\n"; |
||
738 | |||
739 | $appname = 'ExecIfTime'; |
||
740 | $appdata = 'Goto(work-time-set-var-' . $out_data->id . ',${EXTEN},1)'; |
||
741 | } |
||
742 | $conf .= "same => n,{$appname}($times,$weekdays,$mdays,$months?{$appdata})\n\t"; |
||
743 | } |
||
744 | |||
745 | $conf .= "same => n,return\n\n"; |
||
746 | |||
747 | $conf .= $conf_out_set_var; |
||
748 | |||
749 | return $conf; |
||
750 | } |
||
751 | |||
752 | /** |
||
753 | * Генератор extension для контекста outgoing. |
||
754 | * |
||
755 | * @param string $uniqueID |
||
756 | * |
||
757 | * @return null|string |
||
758 | */ |
||
759 | public function getTechByID(string $uniqueID): string |
||
774 | } |
||
775 | } |