PageRenderTime 26ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/scripts/auto_attendant.php

https://github.com/vir/freesentral
PHP | 289 lines | 218 code | 24 blank | 47 comment | 37 complexity | 1f5a4a02be510b2116a6ad22df14adef MD5 | raw file
  1. #!/usr/bin/php -q
  2. <?php
  3. /**
  4. * auto_attendant.php
  5. * This file is part of the FreeSentral Project http://freesentral.com
  6. *
  7. * FreeSentral - is a Web Graphical User Interface for easy configuration of the Yate PBX software
  8. * Copyright (C) 2008-2009 Null Team
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  23. */
  24. ?>
  25. <?php
  26. require_once("lib_queries.php");
  27. require_once("libyate.php");
  28. $ourcallid = "auto_attendant/" . uniqid(rand(),1);
  29. $wait_time = 4; //number of seconds that script has to wait after user input in order to see if another digit will be pressed
  30. $hold_keys = '';
  31. $count = false; //start to count seconds since a digit has been pressed
  32. $state = "enter";
  33. function debug($mess)
  34. {
  35. Yate::Debug("auto_attendant.php: ".$mess);
  36. }
  37. /* Perform machine status transitions */
  38. function setState($newstate)
  39. {
  40. global $ourcallid;
  41. global $partycallid;
  42. global $state;
  43. global $uploaded_prompts;
  44. global $keys;
  45. global $wait_time;
  46. global $caller;
  47. global $hold_keys;
  48. global $destination;
  49. global $called;
  50. global $ev;
  51. // are we exiting?
  52. if ($state == "")
  53. return;
  54. debug("setState('$newstate') state: $state");
  55. $state = $newstate;
  56. // always obey a return to prompt
  57. switch ($newstate) {
  58. case "greeting":
  59. // check what prompt to use for this time of day
  60. $query = "select prompts.prompt_id, prompts.file as prompt from time_frames, prompts where numeric_day=extract(dow from now()) and cast(start_hour as integer)<=extract(HOUR FROM now()) AND cast(end_hour as integer)>extract(HOUR FROM now()) and time_frames.prompt_id=prompts.prompt_id UNION select prompt_id, file as prompt from prompts where status='offline'";
  61. $res = query_to_array($query);
  62. if(!count($res))
  63. {
  64. debug("Auto-Attendant is not configured!!");
  65. setState("goodbye");
  66. return;
  67. }
  68. $prompt_id = $res[0]["prompt_id"];
  69. $prompt = $res[0]["prompt"];
  70. // here we must have ".au"
  71. $prompt = str_ireplace(".mp3", ".slin", $prompt);
  72. $query = "SELECT key, destination FROM keys WHERE prompt_id=$prompt_id";
  73. $keys = query_to_array($query);
  74. $m = new Yate("chan.attach" );
  75. $m->params["source"] = "wave/play/$uploaded_prompts/auto_attendant/$prompt";
  76. $m->params["notify"] = $ourcallid;
  77. $m->Dispatch();
  78. break;
  79. case "prolong_greeting":
  80. $m = new Yate("chan.attach");
  81. $m->params["consumer"] = "wave/record/-";
  82. $m->params["notify"] = $ourcallid;
  83. $m->params["maxlen"] = $wait_time*10000;
  84. $m->Dispatch();
  85. break;
  86. case "goodbye":
  87. $m = new Yate("chan.attach");
  88. $m->params["source"] = "tone/congestion";
  89. $m->params["consumer"] = "wave/record/-";
  90. $m->params["maxlen"] = 32000;
  91. $m->params["notify"] = $ourcallid;
  92. $m->Dispatch();
  93. break;
  94. case "call.route":
  95. $to_call = null;
  96. for($i=0; $i<count($keys); $i++) {
  97. if($keys[$i]["key"] == $hold_keys) {
  98. $to_call = $keys[$i]["destination"];
  99. //$hold_keys = null;
  100. break;
  101. }
  102. }
  103. if($hold_keys == '') {
  104. $query = "SELECT (CASE WHEN extension_id IS NOT NULL THEN (SELECT extension FROM extensions WHERE extensions.extension_id=dids.extension_id) ELSE (SELECT extension FROM groups WHERE groups.group_id=dids.group_id) END) as called FROM dids WHERE number='$called'";
  105. $res = query_to_array($query);
  106. if(!count($res) || !strlen($res[0]["called"])) {
  107. // this should never happen
  108. setState("goodbye");
  109. return;
  110. }
  111. $to_call = $res[0]["called"];
  112. }
  113. if (!$to_call)
  114. $to_call = $hold_keys;
  115. $m = new Yate("call.route");
  116. $m->params["caller"] = $caller;
  117. $m->params["called"] = $to_call;
  118. $m->params["id"] = $ourcallid;
  119. $m->params["already-auth"] = "yes";
  120. $m->params["call_type"] = "from outside";
  121. $m->Dispatch();
  122. break;
  123. case "send_call":
  124. $m = new Yate("chan.masquerade");
  125. $m->params = $ev->params;
  126. $m->params["message"] = "call.execute";
  127. $m->params["id"] = $partycallid;
  128. $m->params["callto"] = $destination;
  129. $m->Dispatch();
  130. break;
  131. }
  132. }
  133. /* Handle all DTMFs here */
  134. function gotDTMF($text)
  135. {
  136. global $state;
  137. global $keys;
  138. global $destination;
  139. global $hold_keys;
  140. global $count;
  141. debug("gotDTMF('$text') state: $state");
  142. $count = true;
  143. switch ($state) {
  144. case "greeting":
  145. case "prolong_greeting":
  146. if($text != "#" && $text != "*")
  147. $hold_keys .= $text;
  148. else {
  149. //i will consider that this are accelerating keys
  150. setState("call.route");
  151. break;
  152. }
  153. return;
  154. }
  155. }
  156. function gotNotify($reason)
  157. {
  158. global $state;
  159. debug("gotNotify('$reason') state: $state");
  160. if ($reason == "replaced")
  161. return;
  162. switch($state) {
  163. case "greeting":
  164. setState("prolong_greeting");
  165. break;
  166. case "prolong_greeting":
  167. setState("call.route");
  168. break;
  169. case "goodbye":
  170. setState("");
  171. break;
  172. }
  173. }
  174. Yate::Init();
  175. /* Install filtered handlers for the wave end and dtmf notify messages */
  176. // chan.dtmf should have a greater priority than that of pbxassist(by default 15)
  177. Yate::Install("chan.dtmf",10,"targetid",$ourcallid);
  178. Yate::Install("chan.notify",100,"targetid",$ourcallid);
  179. Yate::Install("engine.timer",100);
  180. /* The main loop. We pick events and handle them */
  181. while ($state != "") {
  182. $ev=Yate::GetEvent();
  183. /* If Yate disconnected us then exit cleanly */
  184. if ($ev === false)
  185. break;
  186. /* No need to handle empty events in this application */
  187. if ($ev === true)
  188. continue;
  189. /* If we reached here we should have a valid object */
  190. switch ($ev->type) {
  191. case "incoming":
  192. switch ($ev->name) {
  193. case "call.execute":
  194. $partycallid = $ev->GetValue("id");
  195. $caller = $ev->GetValue("caller");
  196. $called = $ev->GetValue("called");
  197. if ($ev->GetValue("debug_on") == "yes") {
  198. Yate::Output(true);
  199. Yate::Debug(true);
  200. }
  201. if ($ev->GetValue("query_on") == "yes") {
  202. $query_on = true;
  203. }
  204. $ev->params["targetid"] = $ourcallid;
  205. $ev->handled = true;
  206. /* We must ACK this message before dispatching a call.answered */
  207. $ev->Acknowledge();
  208. /* Prevent a warning if trying to ACK this message again */
  209. $ev = false;
  210. /* Signal we are answering the call */
  211. $m = new Yate("call.answered");
  212. $m->params["id"] = $ourcallid;
  213. $m->params["targetid"] = $partycallid;
  214. $m->Dispatch();
  215. setState("greeting");
  216. break;
  217. case "chan.notify":
  218. gotNotify($ev->GetValue("reason"));
  219. $ev->handled = true;
  220. break;
  221. case "chan.dtmf":
  222. $text = $ev->GetValue("text");
  223. for ($i = 0; $i < strlen($text); $i++)
  224. gotDTMF($text[$i]);
  225. $ev->handled = true;
  226. break;
  227. case "engine.timer":
  228. if(!$count)
  229. break;
  230. if($count === true)
  231. $count = 1;
  232. else
  233. $count++;
  234. if($count == $wait_time)
  235. setState("call.route");
  236. break;
  237. }
  238. /* This is extremely important.
  239. We MUST let messages return, handled or not */
  240. if ($ev)
  241. $ev->Acknowledge();
  242. break;
  243. case "answer":
  244. //Yate::Debug("PHP Answered: " . $ev->name . " id: " . $ev->id);
  245. if ($ev->name == "call.route") {
  246. $destination = $ev->retval;
  247. if ($destination)
  248. setState("send_call");
  249. else
  250. setState("goodbye");
  251. }
  252. break;
  253. case "installed":
  254. // Yate::Debug("PHP Installed: " . $ev->name);
  255. break;
  256. case "uninstalled":
  257. // Yate::Debug("PHP Uninstalled: " . $ev->name);
  258. break;
  259. default:
  260. // Yate::Output("PHP Event: " . $ev->type);
  261. }
  262. }
  263. Yate::Output("PHP Auto Attendant: bye!");
  264. /* vi: set ts=8 sw=4 sts=4 noet: */
  265. ?>