1 | <?php |
||||
2 | |||||
3 | namespace Inok\phpagi; |
||||
4 | |||||
5 | /** |
||||
6 | * phpagi.php : PHP AGI Functions for Asterisk |
||||
7 | * @see https://github.com/welltime/phpagi |
||||
8 | * @filesource http://phpagi.sourceforge.net/ |
||||
9 | * |
||||
10 | * $Id: phpagi.php,v 2.20 2010/09/30 02:21:00 masham Exp $ |
||||
11 | * |
||||
12 | * Copyright (c) 2003 - 2010 Matthew Asham <[email protected]>, David Eder <[email protected]> and others |
||||
13 | * All Rights Reserved. |
||||
14 | * |
||||
15 | * This software is released under the terms of the GNU Lesser General Public License v2.1 |
||||
16 | * A copy of which is available from http://www.gnu.org/copyleft/lesser.html |
||||
17 | * |
||||
18 | * We would be happy to list your phpagi based application on the phpagi |
||||
19 | * website. Drop me an Email if you'd like us to list your program. |
||||
20 | * |
||||
21 | * |
||||
22 | * Written for PHP 4.3.4, should work with older PHP 4.x versions. |
||||
23 | * |
||||
24 | * Please submit bug reports, patches, etc to https://github.com/welltime/phpagi |
||||
25 | * |
||||
26 | * |
||||
27 | * @package phpAGI |
||||
28 | * @version 2.20 |
||||
29 | */ |
||||
30 | |||||
31 | /** |
||||
32 | * AGI class |
||||
33 | * |
||||
34 | * @package phpAGI |
||||
35 | * @link http://www.voip-info.org/wiki-Asterisk+agi |
||||
36 | * @example examples/dtmf.php Get DTMF tones from the user and say the digits |
||||
37 | * @example examples/input.php Get text input from the user and say it back |
||||
38 | * @example examples/ping.php Ping an IP address |
||||
39 | */ |
||||
40 | class AGI |
||||
41 | { |
||||
42 | /** |
||||
43 | * Request variables read in on initialization. |
||||
44 | * |
||||
45 | * Often contains any/all of the following: |
||||
46 | * agi_request - name of agi script |
||||
47 | * agi_channel - current channel |
||||
48 | * agi_language - current language |
||||
49 | * agi_type - channel type (SIP, ZAP, IAX, ...) |
||||
50 | * agi_uniqueid - unique id based on unix time |
||||
51 | * agi_callerid - callerID string |
||||
52 | * agi_dnid - dialed number id |
||||
53 | * agi_rdnis - referring DNIS number |
||||
54 | * agi_context - current context |
||||
55 | * agi_extension - extension dialed |
||||
56 | * agi_priority - current priority |
||||
57 | * agi_enhanced - value is 1.0 if started as an EAGI script |
||||
58 | * agi_accountcode - set by SetAccount in the dialplan |
||||
59 | * agi_network - value is yes if this is a fastagi |
||||
60 | * agi_network_script - name of the script to execute |
||||
61 | * |
||||
62 | * NOTE: program arguments are still in $_SERVER['argv']. |
||||
63 | * |
||||
64 | * @var array |
||||
65 | * @access public |
||||
66 | */ |
||||
67 | public $request; |
||||
68 | |||||
69 | /** |
||||
70 | * Config variables |
||||
71 | * |
||||
72 | * @var array |
||||
73 | * @access public |
||||
74 | */ |
||||
75 | public $config; |
||||
76 | |||||
77 | /** |
||||
78 | * Asterisk Manager |
||||
79 | * |
||||
80 | * @var AGI_AsteriskManager |
||||
81 | * @access public |
||||
82 | */ |
||||
83 | public $asm; |
||||
84 | |||||
85 | /** |
||||
86 | * Input Stream |
||||
87 | * |
||||
88 | * @access private |
||||
89 | */ |
||||
90 | private $in; |
||||
91 | |||||
92 | /** |
||||
93 | * Output Stream |
||||
94 | * |
||||
95 | * @access private |
||||
96 | */ |
||||
97 | private $out; |
||||
98 | |||||
99 | /** |
||||
100 | * Audio Stream |
||||
101 | * |
||||
102 | * @access public |
||||
103 | */ |
||||
104 | public $audio = null; |
||||
105 | |||||
106 | |||||
107 | /** |
||||
108 | * Application option delimiter |
||||
109 | * |
||||
110 | * @access public |
||||
111 | */ |
||||
112 | public $option_delim = ","; |
||||
113 | |||||
114 | private $defaultConfig = ["error_handler" => true, |
||||
115 | "debug" => false, |
||||
116 | "admin" => null, |
||||
117 | "tempdir" => AGI_Others::AST_TMP_DIR]; |
||||
118 | |||||
119 | /** |
||||
120 | * Constructor |
||||
121 | * |
||||
122 | * @param string $config is the name of the config file to parse |
||||
123 | * @param array $optconfig is an array of configuration vars and values, stuffed into $this->config['phpagi'] |
||||
124 | */ |
||||
125 | public function __construct($config = null, array $optconfig = []) { |
||||
126 | // load config |
||||
127 | if (!is_null($config) && file_exists($config)) { |
||||
128 | $this->config = parse_ini_file($config, true); |
||||
129 | } elseif (file_exists(AGI_Others::DEFAULT_PHPAGI_CONFIG)) { |
||||
130 | $this->config = parse_ini_file(AGI_Others::DEFAULT_PHPAGI_CONFIG, true); |
||||
131 | } |
||||
132 | |||||
133 | // If optconfig is specified, stuff values and vars into 'phpagi' config array. |
||||
134 | foreach ($optconfig as $var => $val) { |
||||
135 | $this->config['phpagi'][$var] = $val; |
||||
136 | } |
||||
137 | |||||
138 | // add default values to config for uninitialized values |
||||
139 | foreach ($this->defaultConfig as $name => $value) { |
||||
140 | $this->config["phpagi"][$name] = $this->config["phpagi"][$name] ?? $value; |
||||
141 | } |
||||
142 | |||||
143 | // festival TTS config |
||||
144 | $this->config['festival']['text2wave'] = $this->config['festival']['text2wave'] ?? $this->which('text2wave'); |
||||
145 | |||||
146 | // swift TTS config |
||||
147 | $this->config['cepstral']['swift'] = $this->config['cepstral']['swift'] ?? $this->which('swift'); |
||||
148 | |||||
149 | ob_implicit_flush(); |
||||
150 | |||||
151 | // open stdin & stdout |
||||
152 | $this->in = defined('STDIN') ? STDIN : fopen('php://stdin', 'r'); |
||||
153 | $this->out = defined('STDOUT') ? STDOUT : fopen('php://stdout', 'w'); |
||||
154 | |||||
155 | // initialize error handler |
||||
156 | if ($this->config['phpagi']['error_handler']) { |
||||
157 | set_error_handler('\\Inok\\phpagi\\AGI_Others::phpagi_error_handler'); |
||||
158 | AGI_Others::$phpagi_error_handler_email = $this->config['phpagi']['admin']; |
||||
159 | error_reporting(E_ALL); |
||||
160 | } |
||||
161 | |||||
162 | // make sure temp folder exists |
||||
163 | $this->make_folder($this->config['phpagi']['tempdir']); |
||||
164 | |||||
165 | // read the request |
||||
166 | $str = fgets($this->in); |
||||
167 | while ($str != "\n") { |
||||
168 | $this->request[substr($str, 0, strpos($str, ':'))] = trim(substr($str, strpos($str, ':') + 1)); |
||||
169 | $str = fgets($this->in); |
||||
170 | } |
||||
171 | |||||
172 | // open audio if agi detected |
||||
173 | if ($this->request['agi_enhanced'] == '1.0') { |
||||
174 | if (file_exists('/proc/' . getmypid() . '/fd/3')) { |
||||
175 | $this->audio = fopen('/proc/' . getmypid() . '/fd/3', 'r'); |
||||
176 | } elseif (file_exists('/dev/fd/3')) { |
||||
177 | // may need to mount fdescfs |
||||
178 | $this->audio = fopen('/dev/fd/3', 'r'); |
||||
179 | } else { |
||||
180 | $this->conlog('Unable to open audio stream'); |
||||
181 | } |
||||
182 | |||||
183 | if ($this->audio) { |
||||
184 | stream_set_blocking($this->audio, 0); |
||||
185 | } |
||||
186 | } |
||||
187 | |||||
188 | $this->conlog('AGI Request:'); |
||||
189 | $this->conlog(/** @scrutinizer ignore-type */ print_r($this->request, true)); |
||||
190 | $this->conlog('PHPAGI internal configuration:'); |
||||
191 | $this->conlog(/** @scrutinizer ignore-type */ print_r($this->config, true)); |
||||
192 | } |
||||
193 | |||||
194 | // ********************************************************************************************************* |
||||
195 | // ** COMMANDS ** |
||||
196 | // ********************************************************************************************************* |
||||
197 | |||||
198 | /** |
||||
199 | * Answer channel if not already in answer state. |
||||
200 | * |
||||
201 | * @link http://www.voip-info.org/wiki-answer |
||||
202 | * @example examples/dtmf.php Get DTMF tones from the user and say the digits |
||||
203 | * @example examples/input.php Get text input from the user and say it back |
||||
204 | * @example examples/ping.php Ping an IP address |
||||
205 | * |
||||
206 | * @return array, see evaluate for return information. ['result'] is 0 on success, -1 on failure. |
||||
207 | */ |
||||
208 | function answer(): array { |
||||
209 | return $this->evaluate('ANSWER'); |
||||
210 | } |
||||
211 | |||||
212 | /** |
||||
213 | * Get the status of the specified channel. If no channel name is specified, return the status of the current channel. |
||||
214 | * |
||||
215 | * @link http://www.voip-info.org/wiki-channel+status |
||||
216 | * @param string $channel |
||||
217 | * @return array, see evaluate for return information. ['data'] contains description. |
||||
218 | */ |
||||
219 | function channel_status(string $channel = ''): array { |
||||
220 | $ret = $this->evaluate("CHANNEL STATUS $channel"); |
||||
221 | switch ($ret['result']) { |
||||
222 | case -1: |
||||
223 | $ret['data'] = trim("There is no channel that matches $channel"); |
||||
224 | break; |
||||
225 | case AGI_Others::AST_STATE_DOWN: |
||||
226 | $ret['data'] = 'Channel is down and available'; |
||||
227 | break; |
||||
228 | case AGI_Others::AST_STATE_RESERVED: |
||||
229 | $ret['data'] = 'Channel is down, but reserved'; |
||||
230 | break; |
||||
231 | case AGI_Others::AST_STATE_OFFHOOK: |
||||
232 | $ret['data'] = 'Channel is off hook'; |
||||
233 | break; |
||||
234 | case AGI_Others::AST_STATE_DIALING: |
||||
235 | $ret['data'] = 'Digits (or equivalent) have been dialed'; |
||||
236 | break; |
||||
237 | case AGI_Others::AST_STATE_RING: |
||||
238 | $ret['data'] = 'Line is ringing'; |
||||
239 | break; |
||||
240 | case AGI_Others::AST_STATE_RINGING: |
||||
241 | $ret['data'] = 'Remote end is ringing'; |
||||
242 | break; |
||||
243 | case AGI_Others::AST_STATE_UP: |
||||
244 | $ret['data'] = 'Line is up'; |
||||
245 | break; |
||||
246 | case AGI_Others::AST_STATE_BUSY: |
||||
247 | $ret['data'] = 'Line is busy'; |
||||
248 | break; |
||||
249 | case AGI_Others::AST_STATE_DIALING_OFFHOOK: |
||||
250 | $ret['data'] = 'Digits (or equivalent) have been dialed while offhook'; |
||||
251 | break; |
||||
252 | case AGI_Others::AST_STATE_PRERING: |
||||
253 | $ret['data'] = 'Channel has detected an incoming call and is waiting for ring'; |
||||
254 | break; |
||||
255 | default: |
||||
256 | $ret['data'] = "Unknown ({$ret['result']})"; |
||||
257 | break; |
||||
258 | } |
||||
259 | return $ret; |
||||
260 | } |
||||
261 | |||||
262 | /** |
||||
263 | * Deletes an entry in the Asterisk database for a given family and key. |
||||
264 | * |
||||
265 | * @link http://www.voip-info.org/wiki-database+del |
||||
266 | * @param string $family |
||||
267 | * @param string $key |
||||
268 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise. |
||||
269 | */ |
||||
270 | function database_del(string $family, string $key): array { |
||||
271 | return $this->evaluate("DATABASE DEL \"$family\" \"$key\""); |
||||
272 | } |
||||
273 | |||||
274 | /** |
||||
275 | * Deletes a family or specific keytree within a family in the Asterisk database. |
||||
276 | * |
||||
277 | * @link http://www.voip-info.org/wiki-database+deltree |
||||
278 | * @param string $family |
||||
279 | * @param string $keytree |
||||
280 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise. |
||||
281 | */ |
||||
282 | function database_deltree(string $family, string $keytree = ''): array { |
||||
283 | $cmd = "DATABASE DELTREE \"$family\""; |
||||
284 | if ($keytree != '') { |
||||
285 | $cmd .= " \"$keytree\""; |
||||
286 | } |
||||
287 | return $this->evaluate($cmd); |
||||
288 | } |
||||
289 | |||||
290 | /** |
||||
291 | * Retrieves an entry in the Asterisk database for a given family and key. |
||||
292 | * |
||||
293 | * @link http://www.voip-info.org/wiki-database+get |
||||
294 | * @param string $family |
||||
295 | * @param string $key |
||||
296 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 failure. ['data'] holds the value |
||||
297 | */ |
||||
298 | function database_get(string $family, string $key): array { |
||||
299 | return $this->evaluate("DATABASE GET \"$family\" \"$key\""); |
||||
300 | } |
||||
301 | |||||
302 | /** |
||||
303 | * Adds or updates an entry in the Asterisk database for a given family, key, and value. |
||||
304 | * |
||||
305 | * @param string $family |
||||
306 | * @param string $key |
||||
307 | * @param string $value |
||||
308 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise |
||||
309 | */ |
||||
310 | function database_put(string $family, string $key, string $value): array { |
||||
311 | $value = str_replace("\n", '\n', addslashes($value)); |
||||
312 | return $this->evaluate("DATABASE PUT \"$family\" \"$key\" \"$value\""); |
||||
313 | } |
||||
314 | |||||
315 | |||||
316 | /** |
||||
317 | * Sets a global variable, using Asterisk 1.6 syntax. |
||||
318 | * |
||||
319 | * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set |
||||
320 | * |
||||
321 | * @param string $pVariable |
||||
322 | * @param string|int|float $pValue |
||||
323 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise |
||||
324 | */ |
||||
325 | function set_global_var(string $pVariable, $pValue): array { |
||||
326 | if (is_numeric($pValue)) { |
||||
327 | return $this->evaluate("Set({$pVariable}={$pValue},g);"); |
||||
328 | } |
||||
329 | return $this->evaluate("Set({$pVariable}=\"{$pValue}\",g);"); |
||||
330 | } |
||||
331 | |||||
332 | |||||
333 | /** |
||||
334 | * Sets a variable, using Asterisk 1.6 syntax. |
||||
335 | * |
||||
336 | * @link http://www.voip-info.org/wiki/view/Asterisk+cmd+Set |
||||
337 | * |
||||
338 | * @param string $pVariable |
||||
339 | * @param string|int|float $pValue |
||||
340 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 otherwise |
||||
341 | */ |
||||
342 | function set_var(string $pVariable, $pValue): array { |
||||
343 | if (is_numeric($pValue)) { |
||||
344 | return $this->evaluate("Set({$pVariable}={$pValue});"); |
||||
345 | } |
||||
346 | return $this->evaluate("Set({$pVariable}=\"{$pValue}\");"); |
||||
347 | } |
||||
348 | |||||
349 | |||||
350 | /** |
||||
351 | * Executes the specified Asterisk application with given options. |
||||
352 | * |
||||
353 | * @link http://www.voip-info.org/wiki-exec |
||||
354 | * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands |
||||
355 | * @param string $application |
||||
356 | * @param mixed $options |
||||
357 | * @return array, see evaluate for return information. ['result'] is whatever the application returns, or -2 on failure to find application |
||||
358 | */ |
||||
359 | function exec(string $application, $options): array { |
||||
360 | if (is_array($options)) { |
||||
361 | $options = join('|', $options); |
||||
362 | } |
||||
363 | return $this->evaluate("EXEC $application $options"); |
||||
364 | } |
||||
365 | |||||
366 | /** |
||||
367 | * Plays the given file and receives DTMF data. |
||||
368 | * |
||||
369 | * This is similar to STREAM FILE, but this command can accept and return many DTMF digits, |
||||
370 | * while STREAM FILE returns immediately after the first DTMF digit is detected. |
||||
371 | * |
||||
372 | * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default. |
||||
373 | * |
||||
374 | * If the user doesn't press any keys when the message plays, there is $timeout milliseconds |
||||
375 | * of silence then the command ends. |
||||
376 | * |
||||
377 | * The user has the opportunity to press a key at any time during the message or the |
||||
378 | * post-message silence. If the user presses a key while the message is playing, the |
||||
379 | * message stops playing. When the first key is pressed a timer starts counting for |
||||
380 | * $timeout milliseconds. Every time the user presses another key the timer is restarted. |
||||
381 | * The command ends when the counter goes to zero or the maximum number of digits is entered, |
||||
382 | * whichever happens first. |
||||
383 | * |
||||
384 | * If you don't specify a time out then a default timeout of 2000 is used following a pressed |
||||
385 | * digit. If no digits are pressed then 6 seconds of silence follow the message. |
||||
386 | * |
||||
387 | * If you don't specify $max_digits then the user can enter as many digits as they want. |
||||
388 | * |
||||
389 | * Pressing the # key has the same effect as the timer running out: the command ends and |
||||
390 | * any previously keyed digits are returned. A side effect of this is that there is no |
||||
391 | * way to read a # key using this command. |
||||
392 | * |
||||
393 | * @param string $filename file to play. Do not include file extension. |
||||
394 | * @param integer $timeout milliseconds |
||||
395 | * @param integer $max_digits |
||||
396 | * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present. |
||||
397 | * |
||||
398 | * This differs from other commands with return DTMF as numbers representing ASCII characters. |
||||
399 | * @example examples/ping.php Ping an IP address |
||||
400 | * |
||||
401 | * @link http://www.voip-info.org/wiki-get+data |
||||
402 | */ |
||||
403 | function get_data(string $filename, $timeout = null, $max_digits = null): array { |
||||
404 | return $this->evaluate(rtrim("GET DATA $filename $timeout $max_digits")); |
||||
405 | } |
||||
406 | |||||
407 | /** |
||||
408 | * Fetch the value of a variable. |
||||
409 | * |
||||
410 | * Does not work with global variables. Does not work with some variables that are generated by modules. |
||||
411 | * |
||||
412 | * @link http://www.voip-info.org/wiki-get+variable |
||||
413 | * @link http://www.voip-info.org/wiki-Asterisk+variables |
||||
414 | * @param string $variable name |
||||
415 | * @param boolean $getvalue return the value only |
||||
416 | * @return mixed, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value. returns value if $getvalue is TRUE |
||||
417 | */ |
||||
418 | function get_variable(string $variable, bool $getvalue = false) { |
||||
419 | $res = $this->evaluate("GET VARIABLE $variable"); |
||||
420 | return $getvalue ? $res['data'] : $res; |
||||
421 | } |
||||
422 | |||||
423 | |||||
424 | /** |
||||
425 | * Fetch the value of a full variable. |
||||
426 | * |
||||
427 | * |
||||
428 | * @link http://www.voip-info.org/wiki/view/get+full+variable |
||||
429 | * @link http://www.voip-info.org/wiki-Asterisk+variables |
||||
430 | * @param string $variable name |
||||
431 | * @param string $channel channel |
||||
432 | * @param boolean $getvalue return the value only |
||||
433 | * @return mixed, see evaluate for return information. ['result'] is 0 if variable hasn't been set, 1 if it has. ['data'] holds the value. returns value if $getvalue is TRUE |
||||
434 | */ |
||||
435 | function get_fullvariable(string $variable, $channel = false, bool $getvalue = false) { |
||||
436 | $req = ($channel == false) ? $variable : $variable . ' ' . $channel; |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
437 | $res = $this->evaluate('GET FULL VARIABLE ' . $req); |
||||
438 | return $getvalue ? $res['data'] : $res; |
||||
439 | |||||
440 | } |
||||
441 | |||||
442 | /** |
||||
443 | * Hangup the specified channel. If no channel name is given, hang up the current channel. |
||||
444 | * |
||||
445 | * With power comes responsibility. Hanging up channels other than your own isn't something |
||||
446 | * that is done routinely. If you are not sure why you are doing so, then don't. |
||||
447 | * |
||||
448 | * @link http://www.voip-info.org/wiki-hangup |
||||
449 | * @example examples/dtmf.php Get DTMF tones from the user and say the digits |
||||
450 | * @example examples/input.php Get text input from the user and say it back |
||||
451 | * @example examples/ping.php Ping an IP address |
||||
452 | * |
||||
453 | * @param string $channel |
||||
454 | * @return array, see evaluate for return information. ['result'] is 1 on success, -1 on failure. |
||||
455 | */ |
||||
456 | function hangup(string $channel = ''): array { |
||||
457 | return $this->evaluate("HANGUP $channel"); |
||||
458 | } |
||||
459 | |||||
460 | /** |
||||
461 | * Does nothing. |
||||
462 | * |
||||
463 | * @link http://www.voip-info.org/wiki-noop |
||||
464 | * @param string $string |
||||
465 | * @return array, see evaluate for return information. |
||||
466 | */ |
||||
467 | function noop(string $string = ""): array { |
||||
468 | return $this->evaluate("NOOP \"$string\""); |
||||
469 | } |
||||
470 | |||||
471 | /** |
||||
472 | * Receive a character of text from a connected channel. Waits up to $timeout milliseconds for |
||||
473 | * a character to arrive, or infinitely if $timeout is zero. |
||||
474 | * |
||||
475 | * @link http://www.voip-info.org/wiki-receive+char |
||||
476 | * @param integer $timeout milliseconds |
||||
477 | * @return array, see evaluate for return information. ['result'] is 0 on timeout or not supported, -1 on failure. Otherwise |
||||
478 | * it is the decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
479 | */ |
||||
480 | function receive_char(int $timeout = -1): array { |
||||
481 | return $this->evaluate("RECEIVE CHAR $timeout"); |
||||
482 | } |
||||
483 | |||||
484 | /** |
||||
485 | * Record sound to a file until an acceptable DTMF digit is received or a specified amount of |
||||
486 | * time has passed. Optionally the file BEEP is played before recording begins. |
||||
487 | * |
||||
488 | * @link http://www.voip-info.org/wiki-record+file |
||||
489 | * @param string $file to record, without extension, often created in /var/lib/asterisk/sounds |
||||
490 | * @param string $format of the file. GSM and WAV are commonly used formats. MP3 is read-only and thus cannot be used. |
||||
491 | * @param string $escape_digits |
||||
492 | * @param integer $timeout is the maximum record time in milliseconds, or -1 for no timeout. |
||||
493 | * @param integer $offset to seek to without exceeding the end of the file. |
||||
494 | * @param boolean $beep |
||||
495 | * @param integer $silence number of seconds of silence allowed before the function returns despite the |
||||
496 | * lack of dtmf digits or reaching timeout. |
||||
497 | * @return array, see evaluate for return information. ['result'] is -1 on error, 0 on hangup, otherwise a decimal value of the |
||||
498 | * DTMF tone. Use chr() to convert to ASCII. |
||||
499 | */ |
||||
500 | function record_file(string $file, string $format, string $escape_digits = '', int $timeout = -1, |
||||
501 | $offset = null, bool $beep = false, $silence = null): array { |
||||
502 | $cmd = trim("RECORD FILE $file $format \"$escape_digits\" $timeout $offset"); |
||||
503 | if ($beep) { |
||||
504 | $cmd .= ' BEEP'; |
||||
505 | } |
||||
506 | if (!is_null($silence)) { |
||||
507 | $cmd .= " s=$silence"; |
||||
508 | } |
||||
509 | return $this->evaluate($cmd); |
||||
510 | } |
||||
511 | |||||
512 | /** |
||||
513 | * Say a given character string, returning early if any of the given DTMF digits are received on the channel. |
||||
514 | * |
||||
515 | * @link https://www.voip-info.org/say-alpha |
||||
516 | * @param string $text |
||||
517 | * @param string $escape_digits |
||||
518 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
519 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
520 | */ |
||||
521 | function say_alpha(string $text, string $escape_digits = ''): array { |
||||
522 | return $this->evaluate("SAY ALPHA $text \"$escape_digits\""); |
||||
523 | } |
||||
524 | |||||
525 | /** |
||||
526 | * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel. |
||||
527 | * |
||||
528 | * @link http://www.voip-info.org/wiki-say+digits |
||||
529 | * @param integer $digits |
||||
530 | * @param string $escape_digits |
||||
531 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
532 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
533 | */ |
||||
534 | function say_digits(int $digits, string $escape_digits = ''): array { |
||||
535 | return $this->evaluate("SAY DIGITS $digits \"$escape_digits\""); |
||||
536 | } |
||||
537 | |||||
538 | /** |
||||
539 | * Say the given number, returning early if any of the given DTMF escape digits are received on the channel. |
||||
540 | * |
||||
541 | * @link http://www.voip-info.org/wiki-say+number |
||||
542 | * @param integer $number |
||||
543 | * @param string $escape_digits |
||||
544 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
545 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
546 | */ |
||||
547 | function say_number(int $number, string $escape_digits = ''): array { |
||||
548 | return $this->evaluate("SAY NUMBER $number \"$escape_digits\""); |
||||
549 | } |
||||
550 | |||||
551 | /** |
||||
552 | * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel. |
||||
553 | * |
||||
554 | * @link http://www.voip-info.org/wiki-say+phonetic |
||||
555 | * @param string $text |
||||
556 | * @param string $escape_digits |
||||
557 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
558 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
559 | */ |
||||
560 | function say_phonetic(string $text, string $escape_digits = ''): array { |
||||
561 | return $this->evaluate("SAY PHONETIC $text \"$escape_digits\""); |
||||
562 | } |
||||
563 | |||||
564 | /** |
||||
565 | * Say a given time, returning early if any of the given DTMF escape digits are received on the channel. |
||||
566 | * |
||||
567 | * @link http://www.voip-info.org/wiki-say+time |
||||
568 | * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC). |
||||
569 | * @param string $escape_digits |
||||
570 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
571 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
572 | */ |
||||
573 | function say_time($time = null, string $escape_digits = ''): array { |
||||
574 | if (is_null($time)) { |
||||
575 | $time = time(); |
||||
576 | } |
||||
577 | return $this->evaluate("SAY TIME $time \"$escape_digits\""); |
||||
578 | } |
||||
579 | |||||
580 | /** |
||||
581 | * Send the specified image on a channel. |
||||
582 | * |
||||
583 | * Most channels do not support the transmission of images. |
||||
584 | * |
||||
585 | * @link http://www.voip-info.org/wiki-send+image |
||||
586 | * @param string $image without extension, often in /var/lib/asterisk/images |
||||
587 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the image is sent or |
||||
588 | * channel does not support image transmission. |
||||
589 | */ |
||||
590 | function send_image(string $image): array { |
||||
591 | return $this->evaluate("SEND IMAGE $image"); |
||||
592 | } |
||||
593 | |||||
594 | /** |
||||
595 | * Send the given text to the connected channel. |
||||
596 | * |
||||
597 | * Most channels do not support transmission of text. |
||||
598 | * |
||||
599 | * @link http://www.voip-info.org/wiki-send+text |
||||
600 | * @param $text |
||||
601 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if the text is sent or |
||||
602 | * channel does not support text transmission. |
||||
603 | */ |
||||
604 | function send_text($text): array { |
||||
605 | return $this->evaluate("SEND TEXT \"$text\""); |
||||
606 | } |
||||
607 | |||||
608 | /** |
||||
609 | * Cause the channel to automatically hangup at $time seconds in the future. |
||||
610 | * If $time is 0 then the auto hangup feature is disabled on this channel. |
||||
611 | * |
||||
612 | * If the channel is hangup prior to $time seconds, this setting has no effect. |
||||
613 | * |
||||
614 | * @link http://www.voip-info.org/wiki-set+autohangup |
||||
615 | * @param integer $time until automatic hangup |
||||
616 | * @return array, see evaluate for return information. |
||||
617 | */ |
||||
618 | function set_autohangup(int $time = 0): array { |
||||
619 | return $this->evaluate("SET AUTOHANGUP $time"); |
||||
620 | } |
||||
621 | |||||
622 | /** |
||||
623 | * Changes the caller ID of the current channel. |
||||
624 | * |
||||
625 | * @link http://www.voip-info.org/wiki-set+callerid |
||||
626 | * @param string $cid example: "John Smith"<1234567> |
||||
627 | * This command will let you take liberties with the <caller ID specification> but the format shown in the example above works |
||||
628 | * well: the name enclosed in double quotes followed immediately by the number inside angle brackets. If there is no name then |
||||
629 | * you can omit it. If the name contains no spaces you can omit the double quotes around it. The number must follow the name |
||||
630 | * immediately; don't put a space between them. The angle brackets around the number are necessary; if you omit them the |
||||
631 | * number will be considered to be part of the name. |
||||
632 | * @return array, see evaluate for return information. |
||||
633 | */ |
||||
634 | function set_callerid(string $cid): array { |
||||
635 | return $this->evaluate("SET CALLERID $cid"); |
||||
636 | } |
||||
637 | |||||
638 | /** |
||||
639 | * Sets the context for continuation upon exiting the application. |
||||
640 | * |
||||
641 | * Setting the context does NOT automatically reset the extension and the priority; if you want to start at the top of the new |
||||
642 | * context you should set extension and priority yourself. |
||||
643 | * |
||||
644 | * If you specify a non-existent context you receive no error indication (['result'] is still 0) but you do get a |
||||
645 | * warning message on the Asterisk console. |
||||
646 | * |
||||
647 | * @link http://www.voip-info.org/wiki-set+context |
||||
648 | * @param string $context |
||||
649 | * @return array, see evaluate for return information. |
||||
650 | */ |
||||
651 | function set_context(string $context): array { |
||||
652 | return $this->evaluate("SET CONTEXT $context"); |
||||
653 | } |
||||
654 | |||||
655 | /** |
||||
656 | * Set the extension to be used for continuation upon exiting the application. |
||||
657 | * |
||||
658 | * Setting the extension does NOT automatically reset the priority. If you want to start with the first priority of the |
||||
659 | * extension you should set the priority yourself. |
||||
660 | * |
||||
661 | * If you specify a non-existent extension you receive no error indication (['result'] is still 0) but you do |
||||
662 | * get a warning message on the Asterisk console. |
||||
663 | * |
||||
664 | * @link http://www.voip-info.org/wiki-set+extension |
||||
665 | * @param string $extension |
||||
666 | * @return array, see evaluate for return information. |
||||
667 | */ |
||||
668 | function set_extension(string $extension): array { |
||||
669 | return $this->evaluate("SET EXTENSION $extension"); |
||||
670 | } |
||||
671 | |||||
672 | /** |
||||
673 | * Enable/Disable Music on hold generator. |
||||
674 | * |
||||
675 | * @link http://www.voip-info.org/wiki-set+music |
||||
676 | * @param boolean $enabled |
||||
677 | * @param string $class |
||||
678 | * @return array, see evaluate for return information. |
||||
679 | */ |
||||
680 | function set_music(bool $enabled = true, string $class = ''): array { |
||||
681 | $enabled = ($enabled) ? 'ON' : 'OFF'; |
||||
682 | return $this->evaluate("SET MUSIC $enabled $class"); |
||||
683 | } |
||||
684 | |||||
685 | /** |
||||
686 | * Set the priority to be used for continuation upon exiting the application. |
||||
687 | * |
||||
688 | * If you specify a non-existent priority you receive no error indication (['result'] is still 0) |
||||
689 | * and no warning is issued on the Asterisk console. |
||||
690 | * |
||||
691 | * @link http://www.voip-info.org/wiki-set+priority |
||||
692 | * @param integer $priority |
||||
693 | * @return array, see evaluate for return information. |
||||
694 | */ |
||||
695 | function set_priority(int $priority): array { |
||||
696 | return $this->evaluate("SET PRIORITY $priority"); |
||||
697 | } |
||||
698 | |||||
699 | /** |
||||
700 | * Sets a variable to the specified value. The variables so created can later be used by later using ${<variablename>} |
||||
701 | * in the dialplan. |
||||
702 | * |
||||
703 | * These variables live in the channel Asterisk creates when you pickup a phone and as such they are both local and temporary. |
||||
704 | * Variables created in one channel can not be accessed by another channel. When you hang up the phone, the channel is deleted |
||||
705 | * and any variables in that channel are deleted as well. |
||||
706 | * |
||||
707 | * @link http://www.voip-info.org/wiki-set+variable |
||||
708 | * @param string $variable is case sensitive |
||||
709 | * @param string $value |
||||
710 | * @return array, see evaluate for return information. |
||||
711 | */ |
||||
712 | function set_variable(string $variable, string $value): array { |
||||
713 | $value = str_replace("\n", '\n', addslashes($value)); |
||||
714 | return $this->evaluate("SET VARIABLE $variable \"$value\""); |
||||
715 | } |
||||
716 | |||||
717 | /** |
||||
718 | * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA |
||||
719 | * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of |
||||
720 | * digits before returning. |
||||
721 | * |
||||
722 | * @param string $filename without extension, often in /var/lib/asterisk/sounds |
||||
723 | * @param string $escape_digits |
||||
724 | * @param integer $offset |
||||
725 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
726 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
727 | * @example examples/ping.php Ping an IP address |
||||
728 | * |
||||
729 | * @link http://www.voip-info.org/wiki-stream+file |
||||
730 | */ |
||||
731 | function stream_file(string $filename, string $escape_digits = '', int $offset = 0): array { |
||||
732 | return $this->evaluate("STREAM FILE $filename \"$escape_digits\" $offset"); |
||||
733 | } |
||||
734 | |||||
735 | /** |
||||
736 | * Enable or disable TDD transmission/reception on the current channel. |
||||
737 | * |
||||
738 | * @link http://www.voip-info.org/wiki-tdd+mode |
||||
739 | * @param string $setting can be on, off or mate |
||||
740 | * @return array, see evaluate for return information. ['result'] is 1 on success, 0 if the channel is not TDD capable. |
||||
741 | */ |
||||
742 | function tdd_mode(string $setting): array { |
||||
743 | return $this->evaluate("TDD MODE $setting"); |
||||
744 | } |
||||
745 | |||||
746 | /** |
||||
747 | * Sends $message to the Asterisk console via the 'verbose' message system. |
||||
748 | * |
||||
749 | * If the Asterisk verbosity level is $level or greater, send $message to the console. |
||||
750 | * |
||||
751 | * The Asterisk verbosity system works as follows. The Asterisk user gets to set the desired verbosity at startup time or later |
||||
752 | * using the console 'set verbose' command. Messages are displayed on the console if their verbose level is less than or equal |
||||
753 | * to desired verbosity set by the user. More important messages should have a low verbose level; less important messages |
||||
754 | * should have a high verbose level. |
||||
755 | * |
||||
756 | * @link http://www.voip-info.org/wiki-verbose |
||||
757 | * @param string $message |
||||
758 | * @param integer $level from 1 to 4 |
||||
759 | * @return array, see evaluate for return information. |
||||
760 | */ |
||||
761 | function verbose(string $message, int $level = 1): array { |
||||
762 | $ret = []; |
||||
763 | foreach (explode("\n", str_replace("\r\n", "\n", print_r($message, true))) as $msg) { |
||||
764 | @syslog(LOG_WARNING, $msg); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
syslog() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||
765 | $ret = $this->evaluate("VERBOSE \"$msg\" $level"); |
||||
766 | } |
||||
767 | return $ret; |
||||
768 | } |
||||
769 | |||||
770 | /** |
||||
771 | * Waits up to $timeout milliseconds for channel to receive a DTMF digit. |
||||
772 | * |
||||
773 | * @link http://www.voip-info.org/wiki-wait+for+digit |
||||
774 | * @param integer $timeout in milliseconds. Use -1 for the timeout value if you want the call to wait indefinitely. |
||||
775 | * @return array, see evaluate for return information. ['result'] is 0 if wait completes with no |
||||
776 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
777 | */ |
||||
778 | function wait_for_digit(int $timeout = -1): array { |
||||
779 | return $this->evaluate("WAIT FOR DIGIT $timeout"); |
||||
780 | } |
||||
781 | |||||
782 | |||||
783 | // ********************************************************************************************************* |
||||
784 | // ** APPLICATIONS ** |
||||
785 | // ********************************************************************************************************* |
||||
786 | |||||
787 | /** |
||||
788 | * Set absolute maximum time of call. |
||||
789 | * |
||||
790 | * Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up. |
||||
791 | * Each time you call AbsoluteTimeout(), all previous absolute timeouts are cancelled. |
||||
792 | * Will return the call to the T extension so that you can playback an explanatory note to the calling party (the called party |
||||
793 | * will not hear that) |
||||
794 | * |
||||
795 | * @link http://www.voip-info.org/wiki-Asterisk+-+documentation+of+application+commands |
||||
796 | * @link http://www.dynx.net/ASTERISK/AGI/ccard/agi-ccard.agi |
||||
797 | * @param integer $seconds allowed, 0 disables timeout |
||||
798 | * @return array, see evaluate for return information. |
||||
799 | */ |
||||
800 | function exec_absolutetimeout(int $seconds = 0): array { |
||||
801 | return $this->exec('AbsoluteTimeout', $seconds); |
||||
802 | } |
||||
803 | |||||
804 | /** |
||||
805 | * Executes an AGI compliant application. |
||||
806 | * |
||||
807 | * @param string $command |
||||
808 | * @param string $args |
||||
809 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or if application requested hangup, or 0 on non-hangup exit. |
||||
810 | */ |
||||
811 | function exec_agi(string $command, string $args): array { |
||||
812 | return $this->exec("AGI $command", $args); |
||||
813 | } |
||||
814 | |||||
815 | /** |
||||
816 | * Set Language. |
||||
817 | * |
||||
818 | * @param string $language code |
||||
819 | * @return array, see evaluate for return information. |
||||
820 | */ |
||||
821 | function exec_setlanguage(string $language = 'en'): array { |
||||
822 | return $this->exec('Set', 'CHANNEL(language)=' . $language); |
||||
823 | } |
||||
824 | |||||
825 | /** |
||||
826 | * Do ENUM Lookup. |
||||
827 | * |
||||
828 | * Note: to retrieve the result, use |
||||
829 | * get_variable('ENUM'); |
||||
830 | * |
||||
831 | * @param $exten |
||||
832 | * @return array, see evaluate for return information. |
||||
833 | */ |
||||
834 | function exec_enumlookup($exten): array { |
||||
835 | return $this->exec('EnumLookup', $exten); |
||||
836 | } |
||||
837 | |||||
838 | /** |
||||
839 | * Dial. |
||||
840 | * |
||||
841 | * Dial takes input from ${VXML_URL} to send XML Url to Cisco 7960 |
||||
842 | * Dial takes input from ${ALERT_INFO} to set ring cadence for Cisco phones |
||||
843 | * Dial returns ${CAUSECODE}: If the dial failed, this is the errormessage. |
||||
844 | * Dial returns ${DIALSTATUS}: Text code returning status of last dial attempt. |
||||
845 | * |
||||
846 | * @link http://www.voip-info.org/wiki-Asterisk+cmd+Dial |
||||
847 | * @param string $type |
||||
848 | * @param string $identifier |
||||
849 | * @param integer $timeout |
||||
850 | * @param string $options |
||||
851 | * @param string $url |
||||
852 | * @return array, see evaluate for return information. |
||||
853 | */ |
||||
854 | function exec_dial(string $type, string $identifier, $timeout = null, $options = null, $url = null): array { |
||||
855 | return $this->exec( |
||||
856 | 'Dial', trim("$type/$identifier" . $this->option_delim . $timeout . $this->option_delim . $options . $this->option_delim . $url, $this->option_delim)); |
||||
857 | } |
||||
858 | |||||
859 | /** |
||||
860 | * Goto. |
||||
861 | * |
||||
862 | * This function takes three arguments: context,extension, and priority, but the leading arguments |
||||
863 | * are optional, not the trailing arguments. Those goto($z) sets the priority to $z. |
||||
864 | * |
||||
865 | * @param string $a |
||||
866 | * @param string $b ; |
||||
867 | * @param string $c ; |
||||
868 | * @return array, see evaluate for return information. |
||||
869 | */ |
||||
870 | function exec_goto(string $a, $b = null, $c = null): array { |
||||
871 | return $this->exec('Goto', trim($a . $this->option_delim . $b . $this->option_delim . $c, $this->option_delim)); |
||||
872 | } |
||||
873 | |||||
874 | |||||
875 | // ********************************************************************************************************* |
||||
876 | // ** FAST PASSING ** |
||||
877 | // ********************************************************************************************************* |
||||
878 | |||||
879 | /** |
||||
880 | * Say the given digit string, returning early if any of the given DTMF escape digits are received on the channel. |
||||
881 | * Return early if $buffer is adequate for request. |
||||
882 | * |
||||
883 | * @link http://www.voip-info.org/wiki-say+digits |
||||
884 | * @param string $buffer |
||||
885 | * @param integer $digits |
||||
886 | * @param string $escape_digits |
||||
887 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
888 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
889 | */ |
||||
890 | function fastpass_say_digits(string &$buffer, int $digits, string $escape_digits = ''): array { |
||||
891 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
892 | $res = $this->say_digits($digits, $escape_digits); |
||||
893 | $this->appendBuffer($buffer, $res); |
||||
894 | return $res; |
||||
895 | } |
||||
896 | return ['code' => AGI_Others::AGIRES_OK, |
||||
897 | 'result' => $this->ordBuffer($buffer)]; |
||||
898 | } |
||||
899 | |||||
900 | /** |
||||
901 | * Say the given number, returning early if any of the given DTMF escape digits are received on the channel. |
||||
902 | * Return early if $buffer is adequate for request. |
||||
903 | * |
||||
904 | * @link http://www.voip-info.org/wiki-say+number |
||||
905 | * @param string $buffer |
||||
906 | * @param integer $number |
||||
907 | * @param string $escape_digits |
||||
908 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
909 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
910 | */ |
||||
911 | function fastpass_say_number(string &$buffer, int $number, string $escape_digits = ''): array { |
||||
912 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
913 | $res = $this->say_number($number, $escape_digits); |
||||
914 | $this->appendBuffer($buffer, $res); |
||||
915 | return $res; |
||||
916 | } |
||||
917 | return ['code' => AGI_Others::AGIRES_OK, |
||||
918 | 'result' => $this->ordBuffer($buffer)]; |
||||
919 | } |
||||
920 | |||||
921 | /** |
||||
922 | * Say the given character string, returning early if any of the given DTMF escape digits are received on the channel. |
||||
923 | * Return early if $buffer is adequate for request. |
||||
924 | * |
||||
925 | * @link http://www.voip-info.org/wiki-say+phonetic |
||||
926 | * @param string $buffer |
||||
927 | * @param string $text |
||||
928 | * @param string $escape_digits |
||||
929 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
930 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
931 | */ |
||||
932 | function fastpass_say_phonetic(string &$buffer, string $text, string $escape_digits = ''): array { |
||||
933 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
934 | $res = $this->say_phonetic($text, $escape_digits); |
||||
935 | $this->appendBuffer($buffer, $res); |
||||
936 | return $res; |
||||
937 | } |
||||
938 | return ['code' => AGI_Others::AGIRES_OK, |
||||
939 | 'result' => $this->ordBuffer($buffer)]; |
||||
940 | } |
||||
941 | |||||
942 | /** |
||||
943 | * Say a given time, returning early if any of the given DTMF escape digits are received on the channel. |
||||
944 | * Return early if $buffer is adequate for request. |
||||
945 | * |
||||
946 | * @link http://www.voip-info.org/wiki-say+time |
||||
947 | * @param string $buffer |
||||
948 | * @param integer $time number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC). |
||||
949 | * @param string $escape_digits |
||||
950 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
951 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
952 | */ |
||||
953 | function fastpass_say_time(string &$buffer, $time = null, string $escape_digits = ''): array { |
||||
954 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
955 | $res = $this->say_time($time, $escape_digits); |
||||
956 | $this->appendBuffer($buffer, $res); |
||||
957 | return $res; |
||||
958 | } |
||||
959 | return ['code' => AGI_Others::AGIRES_OK, |
||||
960 | 'result' => $this->ordBuffer($buffer)]; |
||||
961 | } |
||||
962 | |||||
963 | /** |
||||
964 | * Play the given audio file, allowing playback to be interrupted by a DTMF digit. This command is similar to the GET DATA |
||||
965 | * command but this command returns after the first DTMF digit has been pressed while GET DATA can accumulated any number of |
||||
966 | * digits before returning. |
||||
967 | * Return early if $buffer is adequate for request. |
||||
968 | * |
||||
969 | * @link http://www.voip-info.org/wiki-stream+file |
||||
970 | * @param string $buffer |
||||
971 | * @param string $filename without extension, often in /var/lib/asterisk/sounds |
||||
972 | * @param string $escape_digits |
||||
973 | * @param integer $offset |
||||
974 | * @return array, see evaluate for return information. ['result'] is -1 on hangup or error, 0 if playback completes with no |
||||
975 | * digit received, otherwise a decimal value of the DTMF tone. Use chr() to convert to ASCII. |
||||
976 | */ |
||||
977 | function fastpass_stream_file(string &$buffer, string $filename, string $escape_digits = '', int $offset = 0): array { |
||||
978 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
979 | $res = $this->stream_file($filename, $escape_digits, $offset); |
||||
980 | $this->appendBuffer($buffer, $res); |
||||
981 | return $res; |
||||
982 | } |
||||
983 | return ['code' => AGI_Others::AGIRES_OK, |
||||
984 | 'result' => $this->ordBuffer($buffer), |
||||
985 | 'endpos' => 0]; |
||||
986 | } |
||||
987 | |||||
988 | /** |
||||
989 | * Use festival to read text. |
||||
990 | * Return early if $buffer is adequate for request. |
||||
991 | * |
||||
992 | * @link http://www.cstr.ed.ac.uk/projects/festival/ |
||||
993 | * @param string $buffer |
||||
994 | * @param string $text |
||||
995 | * @param string $escape_digits |
||||
996 | * @param integer $frequency |
||||
997 | * @return array, see evaluate for return information. |
||||
998 | */ |
||||
999 | function fastpass_text2wav(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000): array { |
||||
1000 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
1001 | $res = $this->text2wav($text, $escape_digits, $frequency); |
||||
1002 | $this->appendBuffer($buffer, $res); |
||||
1003 | return $res; |
||||
1004 | } |
||||
1005 | return ['code' => AGI_Others::AGIRES_OK, |
||||
1006 | 'result' => $this->ordBuffer($buffer), |
||||
1007 | 'endpos' => 0]; |
||||
1008 | } |
||||
1009 | |||||
1010 | /** |
||||
1011 | * Use Cepstral Swift to read text. |
||||
1012 | * Return early if $buffer is adequate for request. |
||||
1013 | * |
||||
1014 | * @link http://www.cepstral.com/ |
||||
1015 | * @param string $buffer |
||||
1016 | * @param string $text |
||||
1017 | * @param string $escape_digits |
||||
1018 | * @param integer $frequency |
||||
1019 | * @param $voice |
||||
1020 | * @return array, see evaluate for return information. |
||||
1021 | */ |
||||
1022 | function fastpass_swift(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000, $voice = null): array { |
||||
1023 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
1024 | $res = $this->swift($text, $escape_digits, $frequency, $voice); |
||||
1025 | $this->appendBuffer($buffer, $res); |
||||
1026 | return $res; |
||||
1027 | } |
||||
1028 | return ['code' => AGI_Others::AGIRES_OK, |
||||
1029 | 'result' => $this->ordBuffer($buffer), |
||||
1030 | 'endpos' => 0]; |
||||
1031 | } |
||||
1032 | |||||
1033 | /** |
||||
1034 | * Say Punctuation in a string. |
||||
1035 | * Return early if $buffer is adequate for request. |
||||
1036 | * |
||||
1037 | * @param string $buffer |
||||
1038 | * @param string $text |
||||
1039 | * @param string $escape_digits |
||||
1040 | * @param integer $frequency |
||||
1041 | * @return array, see evaluate for return information. |
||||
1042 | */ |
||||
1043 | function fastpass_say_punctuation(string &$buffer, string $text, string $escape_digits = '', int $frequency = 8000): array { |
||||
1044 | if ($buffer == '' || $this->proceedDigits($buffer, $escape_digits)) { |
||||
1045 | $res = $this->say_punctuation($text, $escape_digits, $frequency); |
||||
1046 | $this->appendBuffer($buffer, $res); |
||||
1047 | return $res; |
||||
1048 | } |
||||
1049 | return ['code' => AGI_Others::AGIRES_OK, |
||||
1050 | 'result' => $this->ordBuffer($buffer)]; |
||||
1051 | } |
||||
1052 | |||||
1053 | /** |
||||
1054 | * Plays the given file and receives DTMF data. |
||||
1055 | * Return early if $buffer is adequate for request. |
||||
1056 | * |
||||
1057 | * This is similar to STREAM FILE, but this command can accept and return many DTMF digits, |
||||
1058 | * while STREAM FILE returns immediately after the first DTMF digit is detected. |
||||
1059 | * |
||||
1060 | * Asterisk looks for the file to play in /var/lib/asterisk/sounds by default. |
||||
1061 | * |
||||
1062 | * If the user doesn't press any keys when the message plays, there is $timeout milliseconds |
||||
1063 | * of silence then the command ends. |
||||
1064 | * |
||||
1065 | * The user has the opportunity to press a key at any time during the message or the |
||||
1066 | * post-message silence. If the user presses a key while the message is playing, the |
||||
1067 | * message stops playing. When the first key is pressed a timer starts counting for |
||||
1068 | * $timeout milliseconds. Every time the user presses another key the timer is restarted. |
||||
1069 | * The command ends when the counter goes to zero or the maximum number of digits is entered, |
||||
1070 | * whichever happens first. |
||||
1071 | * |
||||
1072 | * If you don't specify a time out then a default timeout of 2000 is used following a pressed |
||||
1073 | * digit. If no digits are pressed then 6 seconds of silence follow the message. |
||||
1074 | * |
||||
1075 | * If you don't specify $max_digits then the user can enter as many digits as they want. |
||||
1076 | * |
||||
1077 | * Pressing the # key has the same effect as the timer running out: the command ends and |
||||
1078 | * any previously keyed digits are returned. A side effect of this is that there is no |
||||
1079 | * way to read a # key using this command. |
||||
1080 | * |
||||
1081 | * @link http://www.voip-info.org/wiki-get+data |
||||
1082 | * @param string $buffer |
||||
1083 | * @param string $filename file to play. Do not include file extension. |
||||
1084 | * @param integer $timeout milliseconds |
||||
1085 | * @param integer $max_digits |
||||
1086 | * @return array, see evaluate for return information. ['result'] holds the digits and ['data'] holds the timeout if present. |
||||
1087 | * |
||||
1088 | * This differs from other commands with return DTMF as numbers representing ASCII characters. |
||||
1089 | */ |
||||
1090 | function fastpass_get_data(string &$buffer, string $filename, $timeout = null, $max_digits = null): array { |
||||
1091 | if (is_null($max_digits) || strlen($buffer) < $max_digits) { |
||||
1092 | if ($buffer == '') { |
||||
1093 | $res = $this->get_data($filename, $timeout, $max_digits); |
||||
1094 | if ($res['code'] == AGI_Others::AGIRES_OK) { |
||||
1095 | $buffer .= $res['result']; |
||||
1096 | } |
||||
1097 | return $res; |
||||
1098 | } |
||||
1099 | while (is_null($max_digits) || strlen($buffer) < $max_digits) { |
||||
1100 | $res = $this->wait_for_digit(); |
||||
1101 | if ($res['code'] != AGI_Others::AGIRES_OK) { |
||||
1102 | return $res; |
||||
1103 | } |
||||
1104 | if ($res['result'] == ord('#')) { |
||||
1105 | break; |
||||
1106 | } |
||||
1107 | $buffer .= chr($res['result']); |
||||
1108 | } |
||||
1109 | } |
||||
1110 | return ['code' => AGI_Others::AGIRES_OK, |
||||
1111 | 'result' => $buffer]; |
||||
1112 | } |
||||
1113 | |||||
1114 | // ********************************************************************************************************* |
||||
1115 | // ** DERIVED ** |
||||
1116 | // ********************************************************************************************************* |
||||
1117 | |||||
1118 | /** |
||||
1119 | * Menu. |
||||
1120 | * |
||||
1121 | * This function presents the user with a menu and reads the response |
||||
1122 | * |
||||
1123 | * @param array $choices has the following structure: |
||||
1124 | * array('1'=>'*Press 1 for this', // festival reads if prompt starts with * |
||||
1125 | * '2'=>'some-gsm-without-extension', |
||||
1126 | * '*'=>'*Press star for help'); |
||||
1127 | * @param int $timeout |
||||
1128 | * @return mixed key pressed on success, -1 on failure |
||||
1129 | */ |
||||
1130 | function menu(array $choices, int $timeout = 2000) { |
||||
1131 | $keys = join('', array_keys($choices)); |
||||
1132 | $choice = null; |
||||
1133 | while (is_null($choice)) { |
||||
1134 | foreach ($choices as $prompt) { |
||||
1135 | if ($prompt[0] == '*') { |
||||
1136 | $ret = $this->text2wav(substr($prompt, 1), $keys); |
||||
1137 | } else { |
||||
1138 | $ret = $this->stream_file($prompt, $keys); |
||||
1139 | } |
||||
1140 | |||||
1141 | if ($ret['code'] != AGI_Others::AGIRES_OK || $ret['result'] == -1) { |
||||
1142 | $choice = -1; |
||||
1143 | break; |
||||
1144 | } |
||||
1145 | |||||
1146 | if ($ret['result'] != 0) { |
||||
1147 | $choice = chr($ret['result']); |
||||
1148 | break; |
||||
1149 | } |
||||
1150 | } |
||||
1151 | |||||
1152 | if (is_null($choice)) { |
||||
1153 | $ret = $this->get_data('beep', $timeout, 1); |
||||
1154 | if ($ret['code'] != AGI_Others::AGIRES_OK || $ret['result'] == -1) { |
||||
1155 | $choice = -1; |
||||
1156 | } elseif ($ret['result'] != '' && strpos(' ' . $keys, $ret['result'])) { |
||||
1157 | $choice = $ret['result']; |
||||
1158 | } |
||||
1159 | } |
||||
1160 | } |
||||
1161 | return $choice; |
||||
1162 | } |
||||
1163 | |||||
1164 | /** |
||||
1165 | * setContext - Set context, extension and priority. |
||||
1166 | * |
||||
1167 | * @param string $context |
||||
1168 | * @param string $extension |
||||
1169 | * @param int $priority |
||||
1170 | */ |
||||
1171 | function setContext(string $context, string $extension = 's', int $priority = 1) { |
||||
1172 | $this->set_context($context); |
||||
1173 | $this->set_extension($extension); |
||||
1174 | $this->set_priority($priority); |
||||
1175 | } |
||||
1176 | |||||
1177 | /** |
||||
1178 | * Parse caller id. |
||||
1179 | * |
||||
1180 | * @param string $callerid |
||||
1181 | * @return array('Name'=>$name, 'Number'=>$number) |
||||
1182 | * @example examples/dtmf.php Get DTMF tones from the user and say the digits |
||||
1183 | * @example examples/input.php Get text input from the user and say it back |
||||
1184 | * |
||||
1185 | * "name" <proto:user@server:port> |
||||
1186 | * |
||||
1187 | */ |
||||
1188 | function parse_callerid($callerid = null): array { |
||||
1189 | if (is_null($callerid)) { |
||||
1190 | $callerid = $this->request['agi_callerid']; |
||||
1191 | } |
||||
1192 | |||||
1193 | $ret = ['name' => '', |
||||
1194 | 'protocol' => '', |
||||
1195 | 'username' => '', |
||||
1196 | 'host' => '', |
||||
1197 | 'port' => '']; |
||||
1198 | $callerid = trim($callerid); |
||||
1199 | |||||
1200 | if ($callerid[0] == '"' || $callerid[0] == "'") { |
||||
1201 | $d = $callerid[0]; |
||||
1202 | $callerid = explode($d, substr($callerid, 1)); |
||||
1203 | $ret['name'] = array_shift($callerid); |
||||
1204 | $callerid = join($d, $callerid); |
||||
1205 | } |
||||
1206 | |||||
1207 | $callerid = explode('@', trim($callerid, '<> ')); |
||||
1208 | $username = explode(':', array_shift($callerid)); |
||||
1209 | if (count($username) == 1) { |
||||
1210 | $ret['username'] = $username[0]; |
||||
1211 | } else { |
||||
1212 | $ret['protocol'] = array_shift($username); |
||||
1213 | $ret['username'] = join(':', $username); |
||||
1214 | } |
||||
1215 | |||||
1216 | $callerid = join('@', $callerid); |
||||
1217 | $host = explode(':', $callerid); |
||||
1218 | if (count($host) == 1) { |
||||
1219 | $ret['host'] = $host[0]; |
||||
1220 | } else { |
||||
1221 | $ret['host'] = array_shift($host); |
||||
1222 | $ret['port'] = join(':', $host); |
||||
1223 | } |
||||
1224 | |||||
1225 | return $ret; |
||||
1226 | } |
||||
1227 | |||||
1228 | /** |
||||
1229 | * Use festival to read text. |
||||
1230 | * |
||||
1231 | * @param string $text |
||||
1232 | * @param string $escape_digits |
||||
1233 | * @param integer $frequency |
||||
1234 | * @return array | bool, see evaluate for return information. |
||||
1235 | * @example examples/dtmf.php Get DTMF tones from the user and say the digits |
||||
1236 | * @example examples/input.php Get text input from the user and say it back |
||||
1237 | * @example examples/ping.php Ping an IP address |
||||
1238 | * |
||||
1239 | * @link http://www.cstr.ed.ac.uk/projects/festival/ |
||||
1240 | */ |
||||
1241 | function text2wav(string $text, string $escape_digits = '', int $frequency = 8000) { |
||||
1242 | $text = trim($text); |
||||
1243 | if ($text == '') { |
||||
1244 | return true; |
||||
1245 | } |
||||
1246 | |||||
1247 | $hash = md5($text); |
||||
1248 | $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR; |
||||
1249 | $fname .= 'text2wav_' . $hash; |
||||
1250 | |||||
1251 | // create wave file |
||||
1252 | if (!file_exists("$fname.wav")) { |
||||
1253 | // write text file |
||||
1254 | if (!file_exists("$fname.txt")) { |
||||
1255 | $fp = fopen("$fname.txt", 'w'); |
||||
1256 | fputs($fp, $text); |
||||
1257 | fclose($fp); |
||||
1258 | } |
||||
1259 | |||||
1260 | shell_exec("{$this->config['festival']['text2wave']} -F $frequency -o $fname.wav $fname.txt"); |
||||
1261 | } else { |
||||
1262 | touch("$fname.txt"); |
||||
1263 | touch("$fname.wav"); |
||||
1264 | } |
||||
1265 | |||||
1266 | // stream it |
||||
1267 | $ret = $this->stream_file($fname, $escape_digits); |
||||
1268 | |||||
1269 | // clean up old files |
||||
1270 | $delete = time() - 2592000; // 1 month |
||||
1271 | foreach (glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'text2wav_*') as $file) { |
||||
1272 | if (filemtime($file) < $delete) { |
||||
1273 | unlink($file); |
||||
1274 | } |
||||
1275 | } |
||||
1276 | |||||
1277 | return $ret; |
||||
1278 | } |
||||
1279 | |||||
1280 | /** |
||||
1281 | * Use Cepstral Swift to read text. |
||||
1282 | * |
||||
1283 | * @link http://www.cepstral.com/ |
||||
1284 | * @param string $text |
||||
1285 | * @param string $escape_digits |
||||
1286 | * @param integer $frequency |
||||
1287 | * @param $voice |
||||
1288 | * @return array | bool, see evaluate for return information. |
||||
1289 | */ |
||||
1290 | function swift(string $text, string $escape_digits = '', int $frequency = 8000, $voice = null) { |
||||
1291 | if (!is_null($voice)) { |
||||
1292 | $voice = "-n $voice"; |
||||
1293 | } elseif (isset($this->config['cepstral']['voice'])) { |
||||
1294 | $voice = "-n {$this->config['cepstral']['voice']}"; |
||||
1295 | } |
||||
1296 | |||||
1297 | $text = trim($text); |
||||
1298 | if ($text == '') { |
||||
1299 | return true; |
||||
1300 | } |
||||
1301 | |||||
1302 | $hash = md5($text); |
||||
1303 | $fname = $this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR; |
||||
1304 | $fname .= 'swift_' . $hash; |
||||
1305 | |||||
1306 | // create wave file |
||||
1307 | if (!file_exists("$fname.wav")) { |
||||
1308 | // write text file |
||||
1309 | if (!file_exists("$fname.txt")) { |
||||
1310 | $fp = fopen("$fname.txt", 'w'); |
||||
1311 | fputs($fp, $text); |
||||
1312 | fclose($fp); |
||||
1313 | } |
||||
1314 | |||||
1315 | shell_exec("{$this->config['cepstral']['swift']} -p audio/channels=1,audio/sampling-rate=$frequency $voice -o $fname.wav -f $fname.txt"); |
||||
1316 | } |
||||
1317 | |||||
1318 | // stream it |
||||
1319 | $ret = $this->stream_file($fname, $escape_digits); |
||||
1320 | |||||
1321 | // clean up old files |
||||
1322 | $delete = time() - 2592000; // 1 month |
||||
1323 | foreach (glob($this->config['phpagi']['tempdir'] . DIRECTORY_SEPARATOR . 'swift_*') as $file) { |
||||
1324 | if (filemtime($file) < $delete) { |
||||
1325 | unlink($file); |
||||
1326 | } |
||||
1327 | } |
||||
1328 | |||||
1329 | return $ret; |
||||
1330 | } |
||||
1331 | |||||
1332 | /** |
||||
1333 | * Text Input. |
||||
1334 | * |
||||
1335 | * Based on ideas found at http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText |
||||
1336 | * |
||||
1337 | * Example: |
||||
1338 | * UC H LC i , SP h o w SP a r e SP y o u ? |
||||
1339 | * $string = '*8'.'44*'.'*5'.'444*'.'00*'.'0*'.'44*'.'666*'.'9*'.'0*'.'2*'.'777*'.'33*'.'0*'.'999*'.'666*'.'88*'.'0000*'; |
||||
1340 | * |
||||
1341 | * @link http://www.voip-info.org/wiki-Asterisk+cmd+DTMFToText |
||||
1342 | * @example examples/input.php Get text input from the user and say it back |
||||
1343 | * |
||||
1344 | * @param string $mode |
||||
1345 | * @return string |
||||
1346 | */ |
||||
1347 | function text_input(string $mode = 'NUMERIC'): string { |
||||
1348 | $alpha = ['k0' => ' ', |
||||
1349 | 'k00' => ',', |
||||
1350 | 'k000' => '.', |
||||
1351 | 'k0000' => '?', |
||||
1352 | 'k00000' => '0', |
||||
1353 | 'k1' => '!', |
||||
1354 | 'k11' => ':', |
||||
1355 | 'k111' => ';', |
||||
1356 | 'k1111' => '#', |
||||
1357 | 'k11111' => '1', |
||||
1358 | 'k2' => 'A', |
||||
1359 | 'k22' => 'B', |
||||
1360 | 'k222' => 'C', |
||||
1361 | 'k2222' => '2', |
||||
1362 | 'k3' => 'D', |
||||
1363 | 'k33' => 'E', |
||||
1364 | 'k333' => 'F', |
||||
1365 | 'k3333' => '3', |
||||
1366 | 'k4' => 'G', |
||||
1367 | 'k44' => 'H', |
||||
1368 | 'k444' => 'I', |
||||
1369 | 'k4444' => '4', |
||||
1370 | 'k5' => 'J', |
||||
1371 | 'k55' => 'K', |
||||
1372 | 'k555' => 'L', |
||||
1373 | 'k5555' => '5', |
||||
1374 | 'k6' => 'M', |
||||
1375 | 'k66' => 'N', |
||||
1376 | 'k666' => 'O', |
||||
1377 | 'k6666' => '6', |
||||
1378 | 'k7' => 'P', |
||||
1379 | 'k77' => 'Q', |
||||
1380 | 'k777' => 'R', |
||||
1381 | 'k7777' => 'S', |
||||
1382 | 'k77777' => '7', |
||||
1383 | 'k8' => 'T', |
||||
1384 | 'k88' => 'U', |
||||
1385 | 'k888' => 'V', |
||||
1386 | 'k8888' => '8', |
||||
1387 | 'k9' => 'W', |
||||
1388 | 'k99' => 'X', |
||||
1389 | 'k999' => 'Y', |
||||
1390 | 'k9999' => 'Z', |
||||
1391 | 'k99999' => '9']; |
||||
1392 | $symbol = ['k0' => '=', |
||||
1393 | 'k1' => '<', |
||||
1394 | 'k11' => '(', |
||||
1395 | 'k111' => '[', |
||||
1396 | 'k1111' => '{', |
||||
1397 | 'k11111' => '1', |
||||
1398 | 'k2' => '@', |
||||
1399 | 'k22' => '$', |
||||
1400 | 'k222' => '&', |
||||
1401 | 'k2222' => '%', |
||||
1402 | 'k22222' => '2', |
||||
1403 | 'k3' => '>', |
||||
1404 | 'k33' => ')', |
||||
1405 | 'k333' => ']', |
||||
1406 | 'k3333' => '}', |
||||
1407 | 'k33333' => '3', |
||||
1408 | 'k4' => '+', |
||||
1409 | 'k44' => '-', |
||||
1410 | 'k444' => '*', |
||||
1411 | 'k4444' => '/', |
||||
1412 | 'k44444' => '4', |
||||
1413 | 'k5' => "'", |
||||
1414 | 'k55' => '`', |
||||
1415 | 'k555' => '5', |
||||
1416 | 'k6' => '"', |
||||
1417 | 'k66' => '6', |
||||
1418 | 'k7' => '^', |
||||
1419 | 'k77' => '7', |
||||
1420 | 'k8' => "\\", |
||||
1421 | 'k88' => '|', |
||||
1422 | 'k888' => '8', |
||||
1423 | 'k9' => '_', |
||||
1424 | 'k99' => '~', |
||||
1425 | 'k999' => '9']; |
||||
1426 | $text = ''; |
||||
1427 | do { |
||||
1428 | $command = false; |
||||
1429 | $result = $this->get_data('beep'); |
||||
1430 | foreach (explode('*', $result['result']) as $code) { |
||||
1431 | if ($command) { |
||||
1432 | switch ($code[0]) { |
||||
1433 | case '2': |
||||
1434 | $text = substr($text, 0, strlen($text) - 1); |
||||
1435 | break; // backspace |
||||
1436 | case '5': |
||||
1437 | $mode = 'LOWERCASE'; |
||||
1438 | break; |
||||
1439 | case '6': |
||||
1440 | $mode = 'NUMERIC'; |
||||
1441 | break; |
||||
1442 | case '7': |
||||
1443 | $mode = 'SYMBOL'; |
||||
1444 | break; |
||||
1445 | case '8': |
||||
1446 | $mode = 'UPPERCASE'; |
||||
1447 | break; |
||||
1448 | case '9': |
||||
1449 | $text = explode(' ', $text); |
||||
1450 | unset($text[count($text) - 1]); |
||||
1451 | $text = join(' ', $text); |
||||
1452 | break; // backspace a word |
||||
1453 | } |
||||
1454 | $code = substr($code, 1); |
||||
1455 | $command = false; |
||||
1456 | } |
||||
1457 | if ($code == '') { |
||||
1458 | $command = true; |
||||
1459 | } elseif ($mode == 'NUMERIC') { |
||||
1460 | $text .= $code; |
||||
1461 | } elseif ($mode == 'UPPERCASE' && isset($alpha['k' . $code])) { |
||||
1462 | $text .= $alpha['k' . $code]; |
||||
1463 | } elseif ($mode == 'LOWERCASE' && isset($alpha['k' . $code])) { |
||||
1464 | $text .= strtolower($alpha['k' . $code]); |
||||
1465 | } elseif ($mode == 'SYMBOL' && isset($symbol['k' . $code])) { |
||||
1466 | $text .= $symbol['k' . $code]; |
||||
1467 | } |
||||
1468 | } |
||||
1469 | $this->say_punctuation($text); |
||||
1470 | } while (substr($result['result'], -2) == '**'); |
||||
1471 | return $text; |
||||
1472 | } |
||||
1473 | |||||
1474 | /** |
||||
1475 | * Say Punctuation in a string. |
||||
1476 | * |
||||
1477 | * @param string $text |
||||
1478 | * @param string $escape_digits |
||||
1479 | * @param integer $frequency |
||||
1480 | * @return array, see evaluate for return information. |
||||
1481 | */ |
||||
1482 | function say_punctuation(string $text, string $escape_digits = '', int $frequency = 8000): array { |
||||
1483 | $ret = ""; |
||||
1484 | for ($i = 0; $i < strlen($text); $i++) { |
||||
1485 | switch ($text[$i]) { |
||||
1486 | case ' ': |
||||
1487 | $ret .= 'SPACE '; |
||||
1488 | break; |
||||
1489 | case ',': |
||||
1490 | $ret .= 'COMMA '; |
||||
1491 | break; |
||||
1492 | case '.': |
||||
1493 | $ret .= 'PERIOD '; |
||||
1494 | break; |
||||
1495 | case '?': |
||||
1496 | $ret .= 'QUESTION MARK '; |
||||
1497 | break; |
||||
1498 | case '!': |
||||
1499 | $ret .= 'EXPLANATION POINT '; |
||||
1500 | break; |
||||
1501 | case ':': |
||||
1502 | $ret .= 'COLON '; |
||||
1503 | break; |
||||
1504 | case ';': |
||||
1505 | $ret .= 'SEMICOLON '; |
||||
1506 | break; |
||||
1507 | case '#': |
||||
1508 | $ret .= 'POUND '; |
||||
1509 | break; |
||||
1510 | case '=': |
||||
1511 | $ret .= 'EQUALS '; |
||||
1512 | break; |
||||
1513 | case '<': |
||||
1514 | $ret .= 'LESS THAN '; |
||||
1515 | break; |
||||
1516 | case '(': |
||||
1517 | $ret .= 'LEFT PARENTHESIS '; |
||||
1518 | break; |
||||
1519 | case '[': |
||||
1520 | $ret .= 'LEFT BRACKET '; |
||||
1521 | break; |
||||
1522 | case '{': |
||||
1523 | $ret .= 'LEFT BRACE '; |
||||
1524 | break; |
||||
1525 | case '@': |
||||
1526 | $ret .= 'AT '; |
||||
1527 | break; |
||||
1528 | case '$': |
||||
1529 | $ret .= 'DOLLAR SIGN '; |
||||
1530 | break; |
||||
1531 | case '&': |
||||
1532 | $ret .= 'AMPERSAND '; |
||||
1533 | break; |
||||
1534 | case '%': |
||||
1535 | $ret .= 'PERCENT '; |
||||
1536 | break; |
||||
1537 | case '>': |
||||
1538 | $ret .= 'GREATER THAN '; |
||||
1539 | break; |
||||
1540 | case ')': |
||||
1541 | $ret .= 'RIGHT PARENTHESIS '; |
||||
1542 | break; |
||||
1543 | case ']': |
||||
1544 | $ret .= 'RIGHT BRACKET '; |
||||
1545 | break; |
||||
1546 | case '}': |
||||
1547 | $ret .= 'RIGHT BRACE '; |
||||
1548 | break; |
||||
1549 | case '+': |
||||
1550 | $ret .= 'PLUS '; |
||||
1551 | break; |
||||
1552 | case '-': |
||||
1553 | $ret .= 'MINUS '; |
||||
1554 | break; |
||||
1555 | case '*': |
||||
1556 | $ret .= 'ASTERISK '; |
||||
1557 | break; |
||||
1558 | case '/': |
||||
1559 | $ret .= 'SLASH '; |
||||
1560 | break; |
||||
1561 | case "'": |
||||
1562 | $ret .= 'SINGLE QUOTE '; |
||||
1563 | break; |
||||
1564 | case '`': |
||||
1565 | $ret .= 'BACK TICK '; |
||||
1566 | break; |
||||
1567 | case '"': |
||||
1568 | $ret .= 'QUOTE '; |
||||
1569 | break; |
||||
1570 | case '^': |
||||
1571 | $ret .= 'CAROT '; |
||||
1572 | break; |
||||
1573 | case "\\": |
||||
1574 | $ret .= 'BACK SLASH '; |
||||
1575 | break; |
||||
1576 | case '|': |
||||
1577 | $ret .= 'BAR '; |
||||
1578 | break; |
||||
1579 | case '_': |
||||
1580 | $ret .= 'UNDERSCORE '; |
||||
1581 | break; |
||||
1582 | case '~': |
||||
1583 | $ret .= 'TILDE '; |
||||
1584 | break; |
||||
1585 | default: |
||||
1586 | $ret .= $text[$i] . ' '; |
||||
1587 | break; |
||||
1588 | } |
||||
1589 | } |
||||
1590 | return $this->text2wav($ret, $escape_digits, $frequency); |
||||
1591 | } |
||||
1592 | |||||
1593 | /** |
||||
1594 | * Create a new AGI_AsteriskManager. |
||||
1595 | */ |
||||
1596 | function &new_AsteriskManager(): AGI_AsteriskManager { |
||||
1597 | $this->asm = new AGI_AsteriskManager(null, $this->config['asmanager']); |
||||
1598 | $this->asm->setPagi($this); |
||||
1599 | $this->config['asmanager'] = &$this->asm->config['asmanager']; |
||||
1600 | return $this->asm; |
||||
1601 | } |
||||
1602 | |||||
1603 | |||||
1604 | // ********************************************************************************************************* |
||||
1605 | // ** PRIVATE ** |
||||
1606 | // ********************************************************************************************************* |
||||
1607 | |||||
1608 | |||||
1609 | /** |
||||
1610 | * Evaluate an AGI command. |
||||
1611 | * |
||||
1612 | * @access private |
||||
1613 | * @param string $command |
||||
1614 | * @return array ('code'=>$code, 'result'=>$result, 'data'=>$data) |
||||
1615 | */ |
||||
1616 | private function evaluate(string $command): array { |
||||
1617 | $broken = ['code' => 500, |
||||
1618 | 'result' => -1, |
||||
1619 | 'data' => '']; |
||||
1620 | |||||
1621 | // write command |
||||
1622 | if (!@fwrite($this->out, trim($command) . "\n")) { |
||||
1623 | return $broken; |
||||
1624 | } |
||||
1625 | fflush($this->out); |
||||
1626 | |||||
1627 | // Read result. Occasionally, a command return a string followed by an extra new line. |
||||
1628 | // When this happens, our script will ignore the new line, but it will still be in the |
||||
1629 | // buffer. So, if we get a blank line, it is probably the result of a previous |
||||
1630 | // command. We read until we get a valid result or asterisk hangs up. One offending |
||||
1631 | // command is SEND TEXT. |
||||
1632 | $count = 0; |
||||
1633 | do { |
||||
1634 | $str = trim(fgets($this->in, 4096)); |
||||
1635 | } while ($str == '' && $count++ < 5); |
||||
1636 | |||||
1637 | if ($count >= 5) { |
||||
1638 | // $this->conlog("evaluate error on read for $command"); |
||||
1639 | return $broken; |
||||
1640 | } |
||||
1641 | |||||
1642 | // parse result |
||||
1643 | $ret['code'] = substr($str, 0, 3); |
||||
1644 | $str = trim(substr($str, 3)); |
||||
1645 | |||||
1646 | if ($str[0] == '-') { // we have a multiline response! |
||||
1647 | $count = 0; |
||||
1648 | $str = substr($str, 1) . "\n"; |
||||
1649 | $line = fgets($this->in, 4096); |
||||
1650 | while (substr($line, 0, 3) != $ret['code'] && $count < 5) { |
||||
1651 | $str .= $line; |
||||
1652 | $line = fgets($this->in, 4096); |
||||
1653 | $count = (trim($line) == '') ? $count + 1 : 0; |
||||
1654 | } |
||||
1655 | if ($count >= 5) { |
||||
1656 | // $this->conlog("evaluate error on multiline read for $command"); |
||||
1657 | return $broken; |
||||
1658 | } |
||||
1659 | } |
||||
1660 | |||||
1661 | $ret['result'] = null; |
||||
1662 | $ret['data'] = ''; |
||||
1663 | if ($ret['code'] != AGI_Others::AGIRES_OK) { // some sort of error |
||||
1664 | $ret['data'] = $str; |
||||
1665 | $this->conlog(/** @scrutinizer ignore-type */ print_r($ret, true)); |
||||
1666 | } else { // normal AGIRES_OK response |
||||
1667 | $parse = explode(' ', trim($str)); |
||||
1668 | $in_token = false; |
||||
1669 | foreach ($parse as $token) { |
||||
1670 | if ($in_token) { // we previously hit a token starting with ')' but not ending in ')' |
||||
1671 | $ret['data'] .= ' ' . trim($token, '() '); |
||||
1672 | if ($token[strlen($token) - 1] == ')') { |
||||
1673 | $in_token = false; |
||||
1674 | } |
||||
1675 | } elseif ($token[0] == '(') { |
||||
1676 | if ($token[strlen($token) - 1] != ')') { |
||||
1677 | $in_token = true; |
||||
1678 | } |
||||
1679 | $ret['data'] .= ' ' . trim($token, '() '); |
||||
1680 | } elseif (strpos($token, '=')) { |
||||
1681 | $token = explode('=', $token); |
||||
1682 | $ret[$token[0]] = $token[1]; |
||||
1683 | } elseif ($token != '') { |
||||
1684 | $ret['data'] .= ' ' . $token; |
||||
1685 | } |
||||
1686 | } |
||||
1687 | $ret['data'] = trim($ret['data']); |
||||
1688 | } |
||||
1689 | |||||
1690 | // log some errors |
||||
1691 | if ($ret['result'] < 0) { |
||||
1692 | $this->conlog("$command returned {$ret['result']}"); |
||||
1693 | } |
||||
1694 | |||||
1695 | return $ret; |
||||
1696 | } |
||||
1697 | |||||
1698 | /** |
||||
1699 | * Log to console if debug mode. |
||||
1700 | * |
||||
1701 | * @param string $str |
||||
1702 | * @param integer $vbl verbose level |
||||
1703 | * @example examples/ping.php Ping an IP address |
||||
1704 | * |
||||
1705 | */ |
||||
1706 | function conlog(string $str, int $vbl = 1) { |
||||
1707 | static $busy = false; |
||||
1708 | |||||
1709 | if ($this->config['phpagi']['debug'] != false) { |
||||
1710 | if (!$busy) { // no conlogs inside conlog!!! |
||||
1711 | $busy = true; |
||||
1712 | $this->verbose($str, $vbl); |
||||
1713 | $busy = false; |
||||
1714 | } |
||||
1715 | } |
||||
1716 | } |
||||
1717 | |||||
1718 | /** |
||||
1719 | * Find an executable in the path. |
||||
1720 | * |
||||
1721 | * @access private |
||||
1722 | * @param string $cmd command to find |
||||
1723 | * @param string $checkpath path to check |
||||
1724 | * @return string the path to the command |
||||
1725 | */ |
||||
1726 | private function which(string $cmd, $checkpath = null) { |
||||
1727 | if (is_null($checkpath)) { |
||||
1728 | $chpath = getenv('PATH'); |
||||
1729 | if ($chpath === false) { |
||||
1730 | $chpath = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:' . |
||||
1731 | '/usr/X11R6/bin:/usr/local/apache/bin:/usr/local/mysql/bin'; |
||||
1732 | } |
||||
1733 | } else { |
||||
1734 | $chpath = $checkpath; |
||||
1735 | } |
||||
1736 | |||||
1737 | foreach (explode(':', $chpath) as $path) { |
||||
1738 | if (is_executable("$path/$cmd")) { |
||||
1739 | return "$path/$cmd"; |
||||
1740 | } |
||||
1741 | } |
||||
1742 | |||||
1743 | return false; |
||||
0 ignored issues
–
show
|
|||||
1744 | } |
||||
1745 | |||||
1746 | /** |
||||
1747 | * Make a folder recursively. |
||||
1748 | * |
||||
1749 | * @access private |
||||
1750 | * @param string $folder |
||||
1751 | * @param integer $perms |
||||
1752 | * @return boolean |
||||
1753 | */ |
||||
1754 | private function make_folder(string $folder, int $perms = 0755): bool { |
||||
1755 | $f = explode(DIRECTORY_SEPARATOR, $folder); |
||||
1756 | $base = ''; |
||||
1757 | for ($i = 0; $i < count($f); $i++) { |
||||
1758 | $base .= $f[$i]; |
||||
1759 | if ($f[$i] != '' && !file_exists($base)) { |
||||
1760 | if (mkdir($base, $perms) == false) { |
||||
1761 | return (false); |
||||
1762 | } |
||||
1763 | } |
||||
1764 | $base .= DIRECTORY_SEPARATOR; |
||||
1765 | } |
||||
1766 | return (true); |
||||
1767 | } |
||||
1768 | |||||
1769 | private function proceedDigits(string $buffer, string $escapeDigits): bool { |
||||
1770 | if ($escapeDigits != "" && $buffer != "") { |
||||
1771 | if (!strpos(chr(255) . $escapeDigits, $buffer[strlen($buffer) - 1])) { |
||||
1772 | return true; |
||||
1773 | } |
||||
1774 | } |
||||
1775 | return false; |
||||
1776 | } |
||||
1777 | |||||
1778 | private function ordBuffer(string $buffer): int { |
||||
1779 | return ord($buffer[strlen($buffer) - 1]); |
||||
1780 | } |
||||
1781 | |||||
1782 | private function appendBuffer(string &$buffer, array $res) { |
||||
1783 | if ($res['code'] == AGI_Others::AGIRES_OK && $res['result'] > 0) { |
||||
1784 | $buffer .= chr($res['result']); |
||||
1785 | } |
||||
1786 | } |
||||
1787 | } |
||||
1788 | |||||
1789 |