{"id":149,"date":"2018-01-27T23:33:25","date_gmt":"2018-01-28T04:33:25","guid":{"rendered":"https:\/\/www.patellis.us\/?p=149"},"modified":"2026-05-04T13:45:17","modified_gmt":"2026-05-04T17:45:17","slug":"asterisk-to-the-rescue","status":"publish","type":"post","link":"https:\/\/www.patellis.com\/index.php\/2018\/01\/27\/asterisk-to-the-rescue\/","title":{"rendered":"Asterisk to the Rescue!"},"content":{"rendered":"<p><strong>Problem 1<\/strong>: Lack of attendance at our homeowners meetings due to no one remembering when the meeting is&#8230;<\/p>\n<p><strong>Problem 2:<\/strong>\u00a0\u00a0It was the secretary&#8217;s responsiblity to do the calling&#8230;<\/p>\n<p><strong>Bigger Problem:\u00a0<em>I&#8217;m the secretary<\/em>.<\/strong>..<\/p>\n<p><strong>Solution:<\/strong>\u00a0\u00a0Use asterisk to out dial to the members and play a recorded messages.<\/p>\n<p>Damn, I love technology! \u00a0Created a mysql database of all the members, containing first\/last name, phone number, status, last time called and an opt-out flag. Next, threw together am outbound\u00a0dial plan, to check for human or machine answered calls, update the database with the call&#8217;s outcome. \u00a0Added menu to allow user to replay message, opt-out of receiving future messages and allow user to said (by pressing 3) they might attend the meeting. Finally, wrote a quick php program to create \u00a0call files and to feed them to asterisk via \/var\/spool\/asterisk\/outgoing. The program is called by a crontab entry, but only does it&#8217;s thing second\u00a0tuesday of the month at 12:00 noon. \u00a0Added code to check that only 1 outbound call was active at any one time. \u00a0This is due to the licensing of the voice font (Cepstral) that I use. \u00a0I could not see spending $129 to license 2 outbound calls to be active at one time. \u00a0So what if it it takes hour or so to call the members.<\/p>\n<p>The dialplan:<\/p>\n<p><em><span style=\"font-size: 10px;\">I know that I should make a change to allow for a gosub for the main &#8220;text&#8221; of the message, since the HUMAN-ANSWERED and MACHINE-ANSWERED message is 99% the same, the only difference is that the MACHINE-ANSWERED message skips the menu options.<br \/>\n<\/span><\/em><\/p>\n<pre><span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\">[outcallv2]<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,1,MYSQL(Connect connid localhost dbuser dbpassword DATABASE)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,GotoIf($[\"${connid}\" = \"\"]?error,1)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,MYSQL(Query resultid ${connid} Update Members SET Status='DIALING' where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,NoCDR<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Answer<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,PlayBack(en\/silence\/halfsecond)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,AMD<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(Status-${AMDSTATUS})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n(Status-HUMAN),Swift(\"Hello, this is an automated reminder from Homeowners Association.\")<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,MYSQL(Query resultid ${connid} Update Members SET Status='HUMAN ANSWERED',LastCalled=now() where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Swift(\"This reminder is intended for ${FirstName}. The meeting is tonight at 7 P.M. at the Beer Garden on Lake Drive.\",500,0)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Swift(\"Press 1 to listen again. Press 2 to be removed from future calls. Press 3 if you think you will be attending\",4000,1)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n(Done),MYSQL(Disconnect ${connid})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(error,2)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n(Status-MACHINE),WaitForSilence(1000)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,MYSQL(Query resultid ${connid} Update Members SET Status='ANSWER MACHINE',LastCalled=now() where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Swift(\"Hello, this is an automated reminder from Homeowners Association.\")<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Swift(\"This reminder is intended for ${FirstName}. The meeting is tonight at 7 P.M. at the Beer Garden on Lake Drive.\",400,0)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Swift(\"Thank you\")<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n(Status-NOTSURE),MYSQL(Query resultid ${connid} Update Members SET Status='HUNG UP',LastCalled=now() where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n(Status-HANGUP),MYSQL(Query resultid ${connid} Update Members SET Status='NOT SURE',LastCalled=now() where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; s,n,Goto(Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 1,1,Goto(s,Status-HUMAN)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 2,1,Swift(\"You will no longer receive reminders.\")<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 2,n,Swift(\"Thank you\",500,0)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 2,n,MYSQL(Query resultid ${connid} Update Members SET CallReminder='N' where Id=${RecID})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 2,n(Done),MYSQL(Clear ${resultid})<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 2,n,Goto(s,Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 3,1,Swift(\"Great! See you there.\")<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; 3,n,Goto(s,Done)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; error,1,NoOp(Connection error - Ack!)<\/span>\n<span style=\"font-family: arial, helvetica, sans-serif; font-size: 12px;\"> exten =&gt; error,2,Hangup<\/span><\/pre>\n<p><strong>Example of a call file:<\/strong><\/p>\n<p><span style=\"font-size: 12px;\">Channel:\u00a0<a href=\"mailto:SIP\/3217201747@sip.broadvoice.com\">SIP\/3215551212@mysipvendor<\/a><\/span><br \/>\n<span style=\"font-size: 12px;\">MaxRetries: 2<\/span><br \/>\n<span style=\"font-size: 12px;\">RetryTime: 3<\/span><br \/>\n<span style=\"font-size: 12px;\">WaitTime: 60<\/span><br \/>\n<span style=\"font-size: 12px;\">Context: outcallv2<\/span><br \/>\n<span style=\"font-size: 12px;\">Extension: s<\/span><br \/>\n<span style=\"font-size: 12px;\">Priority: 1<\/span><br \/>\n<span style=\"font-size: 12px;\">Setvar: FirstName=Dan<\/span><br \/>\n<span style=\"font-size: 12px;\">Setvar: RecId=1310518<\/span><\/p>\n<p><strong>The &#8220;program&#8221; that runs every Tuesday:\u00a0<\/strong><\/p>\n<pre><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&amp;lt;?php\n\n$dbHost='localhost';\n$dbDatabase='DBNAME';\n$dbUser='dbuser';\n$dbPassword='dbpassword';\n\nfunction ErrPrint($str)\n{\n\tprint &amp;quot;$str\\n&amp;quot;;\n}\n\nfunction dbConnect()\n{\n\tglobal $dbHost,$dbUser,$dbPassword,$dbDatabase,$dbConn,$debuglevel;\n\tif (!isset($dbConn))\n\t{\n\t\t$dbConn=mysql_connect($dbHost,$dbUser,$dbPassword);\n\t\tif ($debuglevel&amp;gt;2)\n\t\t{\n\t\t\tErrPrint(&amp;quot;Creating Database Connection&amp;quot;);\n\t\t\texit();\n\t\t}\n\t\tmysql_selectdb($dbDatabase,$dbConn);\n\t\tif (mysql_errno()&amp;lt;&amp;gt;0)\n\t\t{\n\t\t\ttrigger_error(mysql_error());\n\t\t}\n\t}\n}\n\nfunction dbDisconnect()\n{\n\tglobal $dbConn,$debuglevel;\n\tif (isset($dbConn))\n\t{\n\t\tif ($debuglevel&amp;gt;2)\n\t\t{\n\t\t\tErrPrint(&amp;quot;Destroying Database Connection&amp;quot;);\n\t\t}\n\t\tmysql_close($dbConn);\n\t\tunset($GLOBALS&#x5B;'dbConn']);\n\t}\n}\n\nfunction dbEscape($str)\n{\n\tglobal $dbConn;\n\tif (isset($dbConn))\n\t{\n\t\t$ret=mysql_escape_string($str);\n\t\treturn( $ret );\n\t}\n\telse\n\t{\n\t\treturn( '' );\n\t}\n}\n\nfunction dbEscapeQ($str)\n{\n\treturn( &amp;quot;'&amp;quot;.dbEscape($str).&amp;quot;'&amp;quot; );\n}\n\nfunction dbQuery($str)\n{\n\tglobal $dbConn,$debuglevel;\n\n\tif (isset($dbConn))\n\t{\n\t\tif ($debuglevel&amp;gt;2)\n\t\t{\n\t\t\tErrPrint(&amp;quot;Query: $str&amp;quot;);\n\t\t}\n\t\t$ret=mysql_query($str,$dbConn);\n\t\tif (mysql_errno()&amp;lt;&amp;gt;0)\n\t\t{\n\t\t\ttrigger_error(mysql_error());\n\t\t}\n\t\treturn( $ret );\n\t}\n\telse\n\t{\n\t\treturn( null );\n\t}\n}\n\nfunction dbFinish($str)\n{\n\tglobal $dbConn;\n\tif (isset($dbConn))\n\t{\n\t\tmysql_free_result($str);\n\t}\n}\n\nfunction dbRows($str)\n{\n\tglobal $dbConn,$debuglevel;\n\tif (isset($dbConn))\n\t{\n\t\t$cnt=mysql_num_rows($str);\n\t\tif ($debuglevel&amp;gt;2)\n\t\t{\n\t\t\tErrPrint(&amp;quot;Row Count: $cnt&amp;quot;);\n\t\t}\n\t\treturn($cnt);\n\t}\n\telse\n\t{\n\t\treturn(0);\n\t}\n}\n\n\/\/only run on the 2nd Tuesday of the month\ndate_default_timezone_set(&amp;quot;America\/New_York&amp;quot;);\n$Today=date(&amp;quot;d&amp;quot;,time());\n$MonthYear=date(&amp;quot;Y-m&amp;quot;,time());\n$SecondTues = strftime(&amp;quot;%d&amp;quot;,strtotime(&amp;quot;second tuesday of $MonthYear&amp;quot;));\n\nif ($Today !=$SecondTues)\n{\n\tprintf(&amp;quot;Today is %s, which is not the second Tuesday of the month\\n&amp;quot;,$Today);\n\texit();\n}\n\ndbConnect();\n$SqlCmd=&amp;quot;SELECT Id,FirstName,LastName,PhoneNumber from Members where CallReminder='Y' order by LastName asc&amp;quot;;\n$Result = dbQuery($SqlCmd);\n$Sequence=0;\nif (dbRows($Result)&amp;gt;0)\n{\n\twhile ($RowData=mysql_fetch_assoc($Result))\n\t{\n\t\t$FileName=sprintf(&amp;quot;\/tmp\/ToCall\/%s_%s&amp;quot;,$RowData&#x5B;'LastName'],$RowData&#x5B;'PhoneNumber']);\n\t\t$FileList&#x5B;$Sequence]=$FileName;\n\t\t$FileOut&#x5B;$Sequence]=sprintf(&amp;quot;\/var\/spool\/asterisk\/outgoing\/%s_%s&amp;quot;,$RowData&#x5B;'LastName'],$RowData&#x5B;'PhoneNumber']);\n\t\t$CurrentCalling&#x5B;$Sequence]=sprintf(&amp;quot;%s %s&amp;quot;,$RowData&#x5B;'FirstName'],$RowData&#x5B;'LastName']);\n\t\t$FP=fopen($FileName,&amp;quot;w&amp;quot;);\n\t\tfprintf($FP,&amp;quot;Channel:\u00a0SIP\/%s@MySipProvider\\n&amp;quot;;,$RowData&#x5B;'PhoneNumber']);\n\t\tfprintf($FP,&amp;quot;CallerID: MyCallerId &amp;lt;3215551212&amp;gt;\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;MaxRetries: 1\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;RetryTime: 120\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;WaitTime: 30\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;Context: outcallv2\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;Extension: s\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;Priority: 1\\n&amp;quot;);\n\t\tfprintf($FP,&amp;quot;Setvar: FirstName=%s\\n&amp;quot;,$RowData&#x5B;'FirstName']);\n\t\tfprintf($FP,&amp;quot;Setvar: RecId=%d\\n&amp;quot;,$RowData&#x5B;'Id']);\n\t\t$Sequence++;\n\t\tfclose($FP);\n\t}\n\tdbFinish($Result);\n }\n\ndbDisconnect();\nprintf(&amp;quot;Have %d calls to make.\\n&amp;quot;,$Sequence);\n\nfor ($count=0;$count&amp;lt;$Sequence;$count++)\n{\n\t$Cmd=sprintf(&amp;quot;\/bin\/mv %s \/var\/spool\/asterisk\/outgoing\/.&amp;quot;,$FileList&#x5B;$count]);\n\tprintf(&amp;quot;%2d - Calling %s&amp;quot;,$count+1,$CurrentCalling&#x5B;$count]);\n\tSystem($Cmd);\n\twhile (file_exists($FileOut&#x5B;$count]))\n\t{\n\t\tsleep(10);\n\t\tprint(&amp;quot;.&amp;quot;);\n\t}\n\tprint(&amp;quot;\\n&amp;quot;);\n}\nprint(&amp;quot;All Done!\\n&amp;quot;);\n?&amp;gt;\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Problem 1: Lack of attendance at our homeowners meetings due to no one remembering when the meeting is&#8230; Problem 2:\u00a0\u00a0It was the secretary&#8217;s responsiblity to do the calling&#8230; Bigger Problem:\u00a0I&#8217;m the secretary&#8230; Solution:\u00a0\u00a0Use asterisk to out dial to the members and play a recorded messages. Damn, I love technology! \u00a0Created a mysql database of all &hellip; <a href=\"https:\/\/www.patellis.com\/index.php\/2018\/01\/27\/asterisk-to-the-rescue\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Asterisk to the Rescue!&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-149","post","type-post","status-publish","format-standard","hentry","category-code"],"_links":{"self":[{"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/posts\/149","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/comments?post=149"}],"version-history":[{"count":10,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/posts\/149\/revisions"}],"predecessor-version":[{"id":336,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/posts\/149\/revisions\/336"}],"wp:attachment":[{"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/media?parent=149"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/categories?post=149"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.patellis.com\/index.php\/wp-json\/wp\/v2\/tags?post=149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}