Why Asterisk-based GUI software should make better use of regular expressions

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Having recently discovered that Asterisk supports regular expressions (with some limitations), I got to wondering why the FreePBX-based GUI’s don’t make better use of them.  For example, in FreePBX, Trixbox, Elastix, etc., the Outbound Route pages all support entering patterns that are then directly used in Asterisk dialplans, in a most inefficient manner.  Let me give you an example.  Let’s say you have created an Outbound Route for toll-free calls, and in the “Dial Patterns that will use this Route” section you have entered these patterns:

1800NXXXXXX
1844NXXXXXX
1855NXXXXXX
1866NXXXXXX
1877NXXXXXX
1888NXXXXXX
800NXXXXXX
844NXXXXXX
855NXXXXXX
866NXXXXXX
877NXXXXXX
888NXXXXXX

Let’s further say that you have specified three trunks in the “Trunk Sequence for Matched Routes” section (this is actually conservative given that there are a number of ways you can route toll-free calls for free).  Anyway, this is an actual representative context that would be generated in extensions_additional.conf:

[outrt-5] ; TollFree
include => outrt-5-custom
exten => _1800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1800NXXXXXX,n,Set(_NODEST=)
exten => _1800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(outisbusy,)
exten => _1844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1844NXXXXXX,n,Set(_NODEST=)
exten => _1844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(outisbusy,)
exten => _1855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1855NXXXXXX,n,Set(_NODEST=)
exten => _1855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(outisbusy,)
exten => _1866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1866NXXXXXX,n,Set(_NODEST=)
exten => _1866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(outisbusy,)
exten => _1877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1877NXXXXXX,n,Set(_NODEST=)
exten => _1877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(outisbusy,)
exten => _1888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1888NXXXXXX,n,Set(_NODEST=)
exten => _1888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(outisbusy,)
exten => _800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _800NXXXXXX,n,Set(_NODEST=)
exten => _800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _800NXXXXXX,n,Macro(outisbusy,)
exten => _844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _844NXXXXXX,n,Set(_NODEST=)
exten => _844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _844NXXXXXX,n,Macro(outisbusy,)
exten => _855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _855NXXXXXX,n,Set(_NODEST=)
exten => _855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _855NXXXXXX,n,Macro(outisbusy,)
exten => _866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _866NXXXXXX,n,Set(_NODEST=)
exten => _866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _866NXXXXXX,n,Macro(outisbusy,)
exten => _877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _877NXXXXXX,n,Set(_NODEST=)
exten => _877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _877NXXXXXX,n,Macro(outisbusy,)
exten => _888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _888NXXXXXX,n,Set(_NODEST=)
exten => _888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _888NXXXXXX,n,Macro(outisbusy,)

; end of [outrt-5]

That is 112 lines of dialplan generated by only 12 lines of pattern in the Outbound Route and three trunk selections.  If you have more patterns and/or more trunks, the generated dialplan can grow MUCH larger.  At one point I had a dialplan so large that whenever I did an “orange bar” reload, it took two to three minutes to finish reloading.  Clearly, there is something wrong here.

Now, let’s suppose that instead of, or better yet, in addition to the current Outbound Route pages, we had pages called “Outbound Route – Regular Expression”.  It would eliminate the “Dial Patterns that will use this Route” section, replacing it with a “Regular Expression to select this Route” text box, and it would also have a text box that says “Extensions allowed to use this route (separated by the | character – RegEx patterns may be used).

Let’s say that the contents of the first text box are ^1?8(00|44|55|66|77|88)[2-9][0-9]{6}$ and are stored in variable string1. And let’s say that the contents of the second text box are 10[0-4]X|106[5-8] (specifying that extensions 1000-1049 and 1065-1068 may use this route) and are stored in string 2. Assuming that the part of the dialplan that processes this were in some kind of macro, and the called number is in the variable OUTNUM, you could perhaps do something like this:

exten => s,n,ExecIf($[${REGEX(“${string2}” ${CALLERID(num)})} != 1]?MacroExit())
exten => s,n,ExecIf($[${REGEX(“${string1}” ${OUTNUM})} != 1]?MacroExit())
exten => s,n,Macro(dialout-trunk,7,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,10,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,8,${OUTNUM},)
exten => s,n,Macro(outisbusy,)

The first line bails out of the macro if the Caller ID doesn’t match one of the permitted extensions (this would assume you by default use a regex that matches any extension on your system, for example ^[0-9]{4}$ would match any 4 digit extension number. If you don’t assume this, then you’d need an extra bit of code to skip a null value in string2).

The second line bails if the called number doesn’t match the pattern in string1.

If the called number does match on the regular expression for this route, and the call is from a permitted extension, then the three trunks are tried. Note that now you only need to list each of the three trunks once.

Granted this doesn’t account for every line in the original context, but the other lines could also be transferred into the macro, with a bit of thought. Wouldn’t this be simpler for those who know how to use regular expressions? And, it has the potential to generate MUCH shorter dialplans.

Those who understand the power of regular expressions and who have long or complicated dialplans would appreciate the ability to use regular expressions!  If anyone implements this, don’t be stingy with the maximum string length in the Regular Expression text boxes; I guarantee you that 256 characters is not enough, and neither is 1,024 characters! At a MINIMUM I would say that you need to allow a 4,096 character (4K) regular expression, and while I personally can’t conceive of anyone exceeding that, my bet is that sooner or later someone will.  Remember, you are potentially saving a whole bunch of memory and drive space by allowing regular expressions, so you don’t have to be miserly on the space allotted for the expressions themselves.

How to send various types of notifications on an incoming call in FreePBX

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

PLEASE NOTE: When you look at examples in this article, be aware that WordPress has a nasty habit of changing quotation marks to “prettified” quotes, which WILL NOT WORK. So, unfortunately, if you copy an example line or code block, you may need to go through and change the quotation marks back to the “real” quotes that most software understands. I get SO sick and tired of WordPress changing my articles on me!

Every so often I’ve had someone ask if they could be sent some sort of notification whenever they get an incoming call. It is, although it will send a notification on any incoming call, not just a missed one (if you only want notifications of missed calls, look toward the bottom of this article). The basic technique is as follows:

First, create or edit a Follow-Me for the user’s extension. Let’s say the extension number is 1122. In the Follow-Me, add a line that looks like this:

****1122#

Note you can use about anything in place **** followed by the extension number and # sign — only the trailing # must be there — but I do it that way so I can keep track of what’s going on.  If you are creating the Follow-Me for the first time, be sure to also put your actual extension number in the Follow-Me list (with NO trailing # sign), and check the other Follow-Me options to make sure that they are what you want (for example, that the “Destination if no answer” is your voicemail, if that’s what you want).

Next, in /etc/asterisk/extensions_custom.conf, find the [from-internal-custom] context (it’s right at the top of the file) and add lines similar to the ones shown below to the bottom of that context. Again, we are using 1122 as the extension, but replace it with the actual extension number:

Note that some of these examples depend on certain other software being present. Also, the FIRST line in each sequence must have the line number 1, but if you use more than one of these for an extension, replace the 1 with n in subsequent lines.

To send an e-mail or SMS message (the mail command must function properly from the Linux command prompt for this to work):

exten => ****1122,1,TrySystem(echo "Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)}" | mail 9995559999@yourcarrier.com)

Replace 9995559999@yourcarrier.com with the e-mail address (which could be an e-mail to SMS gateway – most wireless carriers have one. Just search for “Email to SMS Gateway” and add your carrier’s name to the search, and you should find the format to use).  You can use “System” or “TrySystem” in the line above, the difference being that TrySystem is non-blocking, so the call should not be delayed if the process of sending the e-mail bogs things down.

To send an instant message using the XMPP/Jabber protocol — for this to work /etc/asterisk/xmpp_custom.conf (/etc/asterisk/jabber.conf in older versions of FreePBX) must be configured correctly, and there must NOT be a noload => res_jabber.so statement in modules.conf:

exten => ****1122,1,JabberSend(asterisk,username@gmail.com,Call from ${CALLERID(name)} at ${CALLERID(number)} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})

In the above, asterisk is the account context in xmpp_custom.conf or jabber.conf, and username@gmail.com is the address of the XMPP/Jabber user you want to send the IM to (in this case we assume you and the user are on Google Chat, but you could use any other XMPP/Jabber-based IM service, including a local XMPP/Jabber server set up with Prosody or Openfire or similar software. EDIT: If you are running FreePBX on a Raspberry Pi, see this article and this thread for information on setting up a Prosody server).

If you have Kodi (formerly XBMC) running on a system on your local network, you could use a line such as this to send notifications to it (EDITED to include Kodi Leia, Matrix, and Nexus):

In Eden and other Pre-Frodo versions:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/xbmcCmds/xbmcHttp?command=ExecBuiltIn&parameter=XBMC.Notification(Call%20from%20%22${URIENCODE(${CALLERID(name)})}%22%2C${CALLERID(number)}%20calling%20extension%2C15000%2C%2Fhome%2Fusername%2Fphone.png)")

In Frodo through Krypton:

exten => ****1122,1,TrySystem(wget -b -O /dev/null -o /dev/null "http://192.168.0.234:8080/jsonrpc?request={%22jsonrpc%22:%222.0%22,%22method%22:%22GUI.ShowNotification%22,%22params%22:{%22title%22:%22Call%20from%20${URIENCODE(${CALLERID(name)})}%22,%22message%22:%22${CALLERID(number)}%20calling%20extension%22,%22displaytime%22:15000,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22},%22id%22:1}")

In Leia, Matrix, Nexus and probably later versions, until they decide to change it again:

exten => ****1122,1,TrySystem(curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"Call from ${CALLERID(name)}","message":"${CALLERID(number)} calling extension","displaytime":15000,"image":"/home/username/phone.png"},"id":0}' http://192.168.0.234:8080/jsonrpc)

Note: If you set Kodi to require authentication, then in place of the example http://192.168.0.234:8080/jsonrpc instead use the format http://kodiusername:password@ip-address:port/jsonrpc where kodiusername, password and port are the same values you used when setting up your Settings | Service | Control | Web Server settings in Kodi. Note that “kodiusername” is the user name set up in Kodi’s web server settings, and is probably not the same as “username” in the phone.png icon path (which in that case is the name of the user’s home directory).

Note that each of the above is a single long line, and to see any of those lines in its entirety you will need to copy and paste it to a text editor. The above examples assume that Kodi is configured to allow control via a Web interface, at a fixed IP address and port (192.168.0.234 port 8080 in this example). Phone icon - right click and copy imageIf you happen to still have a Linux version of Boxee, the Eden version line might also work with it, perhaps with a bit of tweaking.  I have no idea if it would work (with appropriate modification) with any other standalone home theater PC equipment.  You will likely need to replace username with the user’s name, extension with the called extension’s number, and change the icon path to wherever you put the phone.png file (which is an icon you need to supply, such as the one at the right). Note that the icon path requires %2F in place of forward slashes in pre-Leia versions (therefore %2Fhome%2Fusername%2Fphone.png really means /home/username/phone.png) and this refers to the icon directory and filename — if you choose not to use an icon then leave out this part, depending on your Kodi version:

Eden and prior: %2C%2Fhome%2Fusername%2Fphone.png
Frodo through Krypton: ,%22image%22:%22%2Fhome%2Fusername%2Fphone.png%22
Leia through Nexus (and beyond?): ,"image":"/home/username/phone.png"

Note the comma at the start; it must be included in the deleted section).  By the way, if you are wondering why it had to be changed again for Kodi Leia, see this thread in the Kodi forum.

If you want to receive Caller ID popup notifications on your computer, see A Perl script to send Caller ID popups from Asterisk to computers running Notify OSD (such as Ubuntu Linux) or any command-line invoked notification system and/or A Perl script to send Caller ID popups from Asterisk to computers running Growl under OS X on a Mac or Growl for Windows.  These articles are a bit dated and may not be compatible with some of the newer notification methods in use (such as the Notification Center used in MacOS that was introduced in OS X 10.8); in such cases it may be easier to just send instant messages to the computer as described above, although it may be possible to ssh into the computer and send a notification from the command line – this article describes how to send a notification from the command line in MacOS.

After using the any of above lines (and, again, remember to change the line number from 1 to n on any lines after the first), you may want to include a statement like this:

exten => ****1122,n,NoCDR()

That should prevent Asterisk from creating a separate, usually unwanted CDR entry for the ****(extension number) call.  There will still be a CDR record created for the actual call. EDIT: Actually, if you are going to use this, you probably should make it line 1, and move it above the other ****(extension number) lines, so that the CDR record is not saved even if the call gets disconnected in the middle of sending the notification. EDIT 2: In recent Asterisk versions NoCDR() has been deprecated and replaced by CDR_PROP(disable) so be sure to make that change if you are getting BUSY lines in the CDR when this runs.

Finally, to end this part of the context, you need to include a statement that ends the call in a “not completed” state, so that any other extensions in the Follow-Me will be called.  A couple of possibilities are:

exten => ****1122,n,Congestion
exten => ****1122,n,Busy

Use one and if it doesn’t work as expected, try the other. On my system it appears either will work. Remember that you must do a configuration reload in FreePBX before any of your changes to extensions_custom.conf will take effect!

One reason you may want to do instant message or SMS notifications is if you are forwarding calls via a trunk that does not preserve the original caller’s Caller ID.  If you were forwarding the call to a smart phone (perhaps one that also has a Jabber client running), you could deliver the original caller’s name and number to the phone via instant message or SMS.  It may or may not arrive in time for you to make a decision on whether to answer the call, but if you chose not to answer you’d at least know within a few seconds to a few minutes who the call was from, and you could decide whether to call them back.

If you have figured out any neat tricks to do other types of notifications, or if you know of a way to send a notification only when a call was not answered, feel free to leave a comment.

EDIT: At one time this page showed a method for sending SMS notifications using Google Voice rather than an e-mail to SMS gateway. Unfortunately it appears that the unofficial API that was used by such applications is no longer functional. If you know of a way to send a SMS message from a Linux command prompt using Google Voice that is currently working, please leave a message in the comments!

EDIT: I did think of a couple of techniques that might work for sending a notification only if the call is “missed” — the first is a bit of a hack, and has a serious limitation in that if the caller hangs up prior to the call going to voicemail, no notification would be sent (which might or might not be what you want). Basically, you would create (or modify) a Follow-Me as described above, but don’t put the ****extension# line in it. Instead of making the “Destination if no answer” your voicemail (for example), you would point it to a Ring Group that you have created, and in that Ring Group you would place the ****extension# line, and make the “Destination if no answer” of the ring group your voicemail. So the Follow-Me would ring your extension(s) for the number of seconds in the “Ring Time” setting, but if that time expires and no one answers, the call would be sent to the Ring Group where the only “extensions” would be your ****, which would do their thing and immediately return a “busy” or “congestion”. Since there are no other extensions to try, the Ring Group should immediately pass the call on to its “Destination if no answer”, which would most likely be your voicemail.

I haven’t tested this but I see no reason why it would not work, and in that case the notification would be sent just as the caller is being transferred to voicemail. If the caller doesn’t stick around that long you don’t get a notification, however, the fact that you do get a notification doesn’t necessarily mean that the caller actually left a voicemail — if they hang up during the voicemail greeting, a notification would still have already been sent.

EDIT: Here is a different technique for sending notifications on ONLY missed calls, and with this one the notification is sent whether the caller sticks around to leave a voicemail or not. You do NOT add anything to your Follow Me list when using this technique, nor do you modify extensions_custom.conf.

Note that this technique probably will not work on extensions that are part of a Ring Group or Follow Me where more than one extension is rung simultaneously (at least not without some modification — it should work if you use a ring strategy that only rings one phone at a time, though, such as firstnotonphone or firstavailable) (2017 EDIT: Some users have reported those two ring strategies are broken in FreePBX 13 and 14). Be aware that this one is a bit riskier since you will need to re-do the first part of the following procedure after any FreePBX upgrade, otherwise things may break!

  • Load the /etc/asterisk/extensions.conf file into a text editor such as nano
  • Search (Control-W in nano) for the string [from-internal] (including the brackets)
  • Copy the entire context (you can omit the comments) to a new open page in a text editor on your local computer
  • Next, search for the string [from-did-direct] (including the brackets)
  • Also copy that entire context (without the comments) to the same open page in your text editor
  • You MAY also need to copy the context [macro-hangupcall] which is found in /etc/asterisk/extensions_additional.conf. You would do that the same way as the previous two. I don’t show it in the examples below, but further down I’ll explain why you might need to do that one.

At this point the open page in your text editor will look something like this, although this may vary depending on your version of FreePBX. It is important that you copy these contexts from YOUR system, and repeat the procedure every time you upgrade FreePBX to a newer version:

[from-internal] include => from-internal-noxfer
include => from-internal-xfer
include => bad-number ; auto-generated

[from-did-direct] include => ext-findmefollow
include => ext-local

Now add these two lines to the bottom of EACH of the above two contexts (or all three if you also added [macro-hangupcall]):

exten => h,1,Macro(missed-call)
exten => h,n,hangup

The above is the part you will need to redo if you upgrade FreePBX. Next, add a new macro that looks something like this (this is just an example, don’t copy it verbatim!):

[macro-missed-call]
exten => s,1,NoOp(Checking to see if we need to send a missed call notification)
exten => s,n,GotoIf($["${CT_EXTEN}" > "1121" & "${CT_EXTEN}" < "1125" & "${CT_EXTEN}" != "" & "${ANSWEREDTIME}" = ""]?notify1)
exten => s,n,MacroExit()
exten => s,n(notify1),NoOp(Sending notification of missed call on extension ${CT_EXTEN})
exten => s,n,ExecIf($["${CALLERID(num)}" > "1121" & "${CALLERID(num)}" < "1125"]?MacroExit())
exten => s,n,ExecIf($["${CALLERID(num)}" = "8005551212"]?MacroExit())
exten => s,n,JabberSend(asterisk,username@gmail.com,Missed call from ${CALLERID(name)} at ${CALLERID(num)} for extension ${CT_EXTEN} received ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,MacroExit()

Here’s an explanation of what’s happening in the above Macro, so you can modify it for your specific needs:

  • Line 1 is just a NoOp that tells what the macro is doing.
  • Line 2 says that if the call was for extensions 1122 through 1124 (greater than 1121 and less than 1125), that we didn’t get a null value for the extension, and the call was not answered, go to the label notify1. If we only wanted to test for a single extension such as 1122, instead of “${CT_EXTEN}” > “1121” & “${CT_EXTEN}” < “1125” just use “${CT_EXTEN}” = “1122”. You can add as many lines as you need for various extensions, just be sure to jump to different labels.
  • Line 3 says that if none of the above conditions match, exit the macro.
  • Line 4 is just a NoOp that serves as a placeholder for the label and prepares to send the notification.
  • Line 5 is optional and shows how to exclude notifications for calls if the calling number is within a range, in this case 1122-1124 (so, in this example no notifications would be made on calls from one of your own extensions to another in the 1122-1124 range). You can use as many of these lines as you need to exclude more ranges, or none at all if you don’t want to exclude any ranges. Make sure you get the quotation marks right (and again, if you copy lines from this blog, beware of the quotation marks that WordPress has “prettified” – you’ll need to fix them before the lines will work!).
  • Line 6 is optional and shows how to exclude a specific number from triggering the notification. In this case, if the call comes from 8005551212, no notification would be sent. Note that you could use the ${CALLERID(name) variable as a trigger also, and exclude calls from certain names from triggering a notification. Note you can place multiple tests in the same expression by using the bar character (logical OR in Asterisk) to connect them, for example, “${CALLERID(num)}” = “8005551212” | “${CALLERID(name)}” = “INFORMATION” would match if either condition were true. This line can be omitted completely if you don’t want to exclude calls from any specific numbers.
  • Line 7 is where we actually send the notification. Basically any of the methods mentioned in the previous part of this article can be used here, just replace ****1122,1, in the example lines with s,n,
  • Line 8 is where we gracefully exit the macro. Of course, you can repeat lines 4-7 as many times as needed if you are doing notifications for more than one extension.

Now that you have the three (or four) macros in your text file and edited as they should be, copy the whole mess and paste it into the existing file /etc/asterisk/extensions_override_freepbx.conf — do not just overwrite the existing file! Instead, open it with nano or another text editor and paste in the lines you’ve been working on. Also, do a quick scan to make sure you haven’t duplicated a context that’s already in there (the likelihood of that is pretty remote unless you have done something like this in the past). Note that after you do a FreePBX reload, the contexts in this file will replace the equivalents in the files generated by FreePBX, so that’s why you have to check that the originals haven’t changed each time you upgrade FreePBX.

If you want to know what variables are available for you to trigger on, instead of a line sending a notification you can use a line like this:

exten => s,n,DumpChan

This will show you a lot of information, including how several of the channel variables are set.

One final note, the added macro call under [from-internal] is the one that would be taken after a failed call from another extension, while the one under [from-did-direct] is the one used by failed calls coming in from outside the system. Note that most answered calls (not including ones that go to voicemail) will wind up in [macro-hangupcall] and not go through either of those, BUT in certain circumstances unanswered calls might also end via [macro-hangupcall], such as if they go through a ring group. That is why I mentioned above that you might need to include [macro-hangupcall] along with the other contexts copied into /etc/asterisk/extensions_override_freepbx.conf. However, I have not tested every possible condition, and I do know that some things won’t work — such as if multiple extensions are rung simultaneously, no extension number shows up in the ${CT_EXTEN} variable, so those tests in [macro-missed-call] that depend on that variable being set will fail. You may be able to modify the macro so that it will work in such circumstances by using one or more variables you can find by using DumpChan, but that’s a piece of dialplan I’m not prepared to write, since I personally have no need for it (and have already spent way too much time on this)!

I probably should also include this note about the use of extensions_override_freepbx.conf as taken from this page:

If extensions.conf (or extensions_additional.conf) has a context or macro that you NEED to modify, you place that code here as asterisk will only execute the first occurrences of that code and ignores other occurrences. This file will not be overwritten. Be very careful as replacing an existing piece of code this way is the fastest possible way to break your system. If you are doing this you should probably think about filing for a feature request or bug fix to get it addressed properly.

Of course, my experience is that filing a feature request with FreePBX is a great way to either be ignored, or lectured by some a**h*le on why you don’t need that feature, so I wouldn’t bother. Just be sure that if you upgrade FreePBX, or even just the Core or Framework modules, you check to make sure that the contexts you’ve copied into extensions_override_freepbx.conf haven’t been changed (or if they have, make sure you copy over the changes).

How to use the FreePBX [macro-dialout-trunk-predial-hook] macro and regular expressions to blacklist or whitelist outgoing calls on all trunks

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission.

DISCLAIMER: Just because I do something a certain way doesn’t mean YOU should, at least not without extensive testing to make sure that it will do what you want. I do not warrant that anything shown here will in any way save you from getting a high phone bill. If that should happen, consider it a happy coincidence, but if it doesn’t work as you expected, don’t come crying to me. I only offer what I think might work, it’s up to YOU to test it on YOUR system.

This is a followup to my article, Did you know that Asterisk has the ability to evaluate Regular Expressions, though not in the same way as Perl or FreeSWITCH? If you have not read that article, you may want to do that first. Both that article and this one are subject to future revision, if I discover more on this subject.

The problem I have always had on the two systems I help administer is that we don’t allow international calls that cost money. In the past I had resorted to putting a complete list of valid area code patterns in each user’s Outbound Route, and since I had to include patterns for both 11 digit and 10 digit calls, it grew to be a pretty long list. Fortunately, when adding a new user, it was easy to copy the list from an existing user’s outbound account, bring it into a text editor to edit the /extension suffix, and paste it into the new user’s Outbound Route. That worked in FreePBX 2.7, and in versions 2.8 and 2.9 if you use the Swiss Army Knife module, but that module won’t work with FreePBX 2.10 and above.

Unfortunately, as you are very well aware if you have read many of my previous posts, starting in FreePBX 2.8 the FreePBX developers changed the entry method for Outbound Route dial patterns, changing it from an incredibly easy process to enter a list of patterns into a painful, totally stupid method involving individual text boxes for each individual part of each line of each Outbound Route dial pattern. That one change was enough to make me hate FreePBX. The FreePBX developers have been totally unresponsive to those who don’t care for the new method (they pretty much try to pretend such users don’t even exist).

One thing I am NOT going to EVER do is enter a long list of dial patterns using their input method! Someone once said of me that I would spend weeks looking for a way around having to re-type something, when it might only take me a few hours to re-type it, and that’s true. I have even abandoned whole articles because my system crashed and there was no backup, and there was no damn way I was retyping what I had already typed. So in that spirit, I started looking for ways to get around having to enter all those dial patterns for each Outbound Route. I’ve used various stopgap measures including weird things like custom trunks, but that was an ugly solution and leaves extra entries in the Call Detail Records. When I found out that Asterisk could use Regular Expressions, I figured out this method, which is actually much faster, and gives me another way to bypass the limitations of FreePBX.

This uses the [macro-dialout-trunk-predial-hook] macro which is already present, but commented out, in /etc/asterisk/extensions_custom.conf. The thing you need to understand about this macro is that it is called AFTER most of the dial plan associated with both the outbound route and the trunk have been applied. So if you change the called number in either the Outbound Route Dial Patterns or the Trunk Dialed Number Manipulation Rules, those transformations will already have been applied by the time this macro is called. Also, if the calling number has been changed (by setting the Outbound Caller ID in the Trunk, for example), that change have been made by the time this macro is called. And finally, this macro is called when a call is about to go out on ANY trunk in your system (at least I think it does, although I haven’t tested it with every possible trunk type. I’m reasonably sure it is called whenever a call is destined for a SIP trunk, but I am not sure that it gets called if you use one of the other available types of trunks. It does NOT seem to get called when a call is destined for an ENUM trunk). If you don’t want it to act on calls from a certain trunk, you need to test for that and bail out of the macro (I show an example of that below).

The interesting thing about this is that because this macro is invoked so late in the progress of an outgoing call, you can use it as a last chance to look for “undesirable things” or to send notifications of certain happenings. For example, although I don’t show exactly how to do it below, you could have it send an instant message or an e-mail notification when someone calls a specific number, such as 911.

What I am showing here is what I have set up on one of my systems. This assumes that the outbound routes only contain the following Dial Patterns (not all Outbound Routes will contain all of these, and I assume that each user has their own outbound route, or that groups of users may share outbound routes):

1NXXNXXXXXX/ext
NXXNXXXXXX/ext
1aaa+NXXXXXX/ext

…where aaa is the user’s local area code for 7 digit dialing , and ext is the user’s extension (which could be a pattern to allow a group of extensions). In addition, if the user’s service allows the *67 code to be used to block outgoing Caller ID, then these patterns might also be used (with the same substitutions):

*671NXXNXXXXXX/ext
*67NXXNXXXXXX/ext
*671aaa+*67|NXXXXXX/ext

The way I have set it up also ignores any number under 10 digits (allowing it to go through) so if you have dial patterns that allow users to dial provider service codes, or extensions on another connected server, or some such thing, those calls won’t be affected — only calls of 10 digits or longer are checked (this is one reason I prepend the area code to 7 digit calls in the Outbound Routes). Note that there are long lines in the following so if you want to see them in their entirety, you need to cut and paste this into a text editor:

[macro-dialout-trunk-predial-hook]
; this macro intentially left blank so it may be safely overwritten for any custom
; requirements that an installation may have.
;
; MACRO RETURN CODE: ${PREDIAL_HOOK_RET}
;                    if set to "BYPASS" then this trunk will be skipped
;
exten => s,1,NoOp(Trunk ${OUT_${DIAL_TRUNK}} selected)
exten => s,n,ExecIf($[${LEN(${OUTNUM})}<10]?MacroExit())
exten => s,n,ExecIf($["${OUT_${DIAL_TRUNK}}"="SIP/TollFree-Future9"]?MacroExit())
exten => s,n,Set(regx=^([*]67)?1?900[0-9]{7}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)
exten => s,n,Set(regx=^([*]67)?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?tollfree)
exten => s,n,Set(regx=^([*]67)?1?[2-9][0-9]{2}976[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)
exten => s,n,Set(regx=^([*]67)?1?(201|202|203|205|206|207|208|209|210|212|213|214|215|216|217|218|219|220|223|224|225|228|229|231|234|239|240|248|251|252|253|254|256|260|262|267|269|270|272|274|276|279|281|301|302|303|304|305|307|308|309|310|312|313|314|315|316|317|318|319|320|321|323|325|327|330|331|332|334|336|337|339|346|347|351|352|360|361|364|380|385|386|401|402|404|405|406|407|408|409|410|412|413|414|415|417|419|423|424|425|430|432|434|435|440|442|443|445|458|463|469|470|475|478|479|480|484|501|502|503|504|505|507|508|509|510|512|513|515|516|517|518|520|530|534|539|540|541|551|559|561|562|563|564|567|570|571|573|574|575|580|585|586|601|602|603|605|606|607|608|609|610|612|614|615|616|617|618|619|620|623|626|628|629|630|631|636|641|646|650|651|657|660|661|662|667|669|678|680|681|682|701|702|703|704|706|707|708|712|713|714|715|716|717|718|719|720|724|725|726|727|731|732|734|737|740|743|747|754|757|760|762|763|765|769|770|772|773|774|775|779|781|785|786|801|802|803|804|805|806|808|810|812|813|814|815|816|817|818|828|830|831|832|838|843|845|847|848|850|854|856|857|858|859|860|862|863|864|865|870|872|878|901|903|904|906|907|908|909|910|912|913|914|915|916|917|918|919|920|925|928|929|930|931|934|936|937|938|940|941|947|949|951|952|954|956|959|970|971|972|973|978|979|980|984|985|986|989)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?usa)
exten => s,n,Set(regx=^([*]67)?1?(204|226|236|249|250|289|306|343|365|367|403|416|418|431|437|438|450|506|514|519|548|579|581|587|604|613|639|647|705|709|778|780|782|807|819|825|867|873|879|902|905)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?canada)
exten => s,n,Set(regx=^0118835100[0-9]{8}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?inum)
exten => s,n,NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a NOT a permitted call)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(custom/sorry-outside-us-canada)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()
exten => s,n(premium),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a premium number call - NOT perrmitted)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call a premium number ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(silence/1&sorry-cant-let-you-do-that3)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()
exten => s,n(usa),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a USA call)
exten => s,n,MacroExit()
exten => s,n(canada),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a Canada call)
exten => s,n,MacroExit()
exten => s,n(tollfree),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a toll free call)
exten => s,n,MacroExit()
exten => s,n(inum),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is an inum call)
exten => s,n,MacroExit()
exten => h,1,Macro(hangupcall,)

Now here’s a breakdown of the above, in which I try to explain what I have done. I will note at the outset that some may think I am using two lines where one will do, by putting the regular expression into a variable when I could just evaluate it directly in the REGEX function in the following line. However, when I was researching the use of regular expressions in Asterisk, I read on several pages that at least some versions of Asterisk have issues parsing a regular expression if it contains certain characters that also have a meaning to Asterisk, unless you put the regex into a string variable first and then use that in the REGEX function. I don’t know if that’s always true but I figured better safe than sorry, and besides, it’s much less confusing to me when I do it that way. But if you want to try combining the two-line pairs into one, feel free — it’s your system, and your headache if it doesn’t work. Also, remember that Asterisk doesn’t parse regular expressions in the same way that some other software (such as Perl) does, so if you try to simplify my regex patterns you might run into trouble. So with that said, let’s begin our tour.

This part of the macro is already present in extensions_custom.conf. You only need to uncomment the lines that are not obviously comments:

[macro-dialout-trunk-predial-hook]
; this macro intentially left blank so it may be safely overwritten for any custom
; requirements that an installation may have.
;
; MACRO RETURN CODE: ${PREDIAL_HOOK_RET}
;                    if set to "BYPASS" then this trunk will be skipped
;
exten => s,1,NoOp(Trunk ${OUT_${DIAL_TRUNK}} selected)

The next line is the first of many places we will use MacroExit() to bail out of the macro. This checks to see in the number called was 9 digits or less. We don’t want to block 911 calls, intra-company calls to other extensions, calls to another network that uses extensions of 9 digits or less, etc.:

exten => s,n,ExecIf($[${LEN(${OUTNUM})}<10]?MacroExit())

This bails out if the call is going out a specific trunk. In this case we don’t want to mess with toll-free calls going to Future9 because they are sent using a rather unusual pattern that would not be recognized by the regular expressions below.

exten => s,n,ExecIf($["${OUT_${DIAL_TRUNK}}"="SIP/TollFree-Future9"]?MacroExit())

Here’s an example of blocking a call that conforms to a specific pattern, in this case 1-900-XXX-XXXX or just 900-XXX-XXXX. It will block calls with or without the *67 (privacy) prefix, and with or without the leading “1”. If you don’t allow *67 calling then just remove the [*]67)? from the start of the expression. If it matches that pattern, it is transferred to the label “premium”, which we’ll get to in a bit:

exten => s,n,Set(regx=^([*]67)?1?900[0-9]{7}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)

Here’s an example of allowing a call that matches a specific pattern, in this case a NANP toll-free call, again with or without the *67 (privacy) prefix (as is the case with most of these regular expressions — note that your outbound route(s) would also need to allow the *67 pattern). These calls are transferred to the label “tollfree”, which we’ll also talk about later:

exten => s,n,Set(regx=^([*]67)?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?tollfree)

Here we block calls that match the pattern 1-[2-9]XX-976-XXXX or just [2-9]XX-976-XXXX (with or without a *67 prepend) and send them to “premium”:

exten => s,n,Set(regx=^([*]67)?1?[2-9][0-9]{2}976[0-9]{4}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?premium)

Here we test to see if the call is going to any USA area code. This is a really long regex but it works. If the pattern matches, it transfers to the “usa” label

exten => s,n,Set(regx=^([*]67)?1?(201|202|203|205|206|207|208|209|210|212|213|214|215|216|217|218|219|220|223|224|225|228|229|231|234|239|240|248|251|252|253|254|256|260|262|267|269|270|272|274|276|279|281|301|302|303|304|305|307|308|309|310|312|313|314|315|316|317|318|319|320|321|323|325|327|330|331|332|334|336|337|339|346|347|351|352|360|361|364|380|385|386|401|402|404|405|406|407|408|409|410|412|413|414|415|417|419|423|424|425|430|432|434|435|440|442|443|445|458|463|469|470|475|478|479|480|484|501|502|503|504|505|507|508|509|510|512|513|515|516|517|518|520|530|534|539|540|541|551|559|561|562|563|564|567|570|571|573|574|575|580|585|586|601|602|603|605|606|607|608|609|610|612|614|615|616|617|618|619|620|623|626|628|629|630|631|636|641|646|650|651|657|660|661|662|667|669|678|680|681|682|701|702|703|704|706|707|708|712|713|714|715|716|717|718|719|720|724|725|726|727|731|732|734|737|740|743|747|754|757|760|762|763|765|769|770|772|773|774|775|779|781|785|786|801|802|803|804|805|806|808|810|812|813|814|815|816|817|818|828|830|831|832|838|843|845|847|848|850|854|856|857|858|859|860|862|863|864|865|870|872|878|901|903|904|906|907|908|909|910|912|913|914|915|916|917|918|919|920|925|928|929|930|931|934|936|937|938|940|941|947|949|951|952|954|956|959|970|971|972|973|978|979|980|984|985|986|989)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?usa)

Same as the previous except now we’re testing for Canadian area codes, and if successful transferring to the “canada” label:

exten => s,n,Set(regx=^([*]67)?1?(204|226|236|249|250|289|306|343|365|367|403|416|418|431|437|438|450|506|514|519|548|579|581|587|604|613|639|647|705|709|778|780|782|807|819|825|867|873|879|902|905)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?canada)

(NOTE: It should be noted that a few new area codes are added every year. You can see a list of the most recently added ones here: NPAs Introduced over the Last 10 Years. You should check this periodically and add the new USA and Canadian area codes to the appropriate sections above.)

Google Voice used to complete calls to iNum numbers for free, so this test allowed for those. Now I don’t use this section, but leave it here as an example. Back when these calls were free, this was the only “international” format call that was permitted on our system:

exten => s,n,Set(regx=^0118835100[0-9]{8}$)
exten => s,n,GotoIf($[${REGEX("${regx}" ${OUTNUM})} = 1]?inum)

If we get here, the call failed all the tests that would indicate it might be a permitted call. I play a custom recorded message that tells the caller that their call is not allowed, possibly because it’s not to a destination in the United States or Canada. Just prior to playing the message, I use the Jabbersend function to send myself an instant message showing that a call failed. I mainly do this so I can determine whether the caller really tried to dial a number that’s not permitted, or if there’s an error in my logic. For example, someone could be trying to place calls to a valid area code that somehow got omitted from the regular expression. Note that Jabbersend will only work if you have jabber.conf configured correctly AND the res_jabber.so module is loading when Asterisk starts up. If you have ever configured your server to place Google Voice calls using Asterisk’s jabber/gtalk support, those things are probably true, but it still may be a bit tricky to get working, however I’m not going to discuss that in this article — just search for Asterisk Jabbersend in a search engine if you need help:

exten => s,n,NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a NOT a permitted call)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(custom/sorry-outside-us-canada)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()

One further note about the last three lines in the above section: Setting the PREDIAL_HOOK_RET variable and the MacroExit() are probably superfluous because the Macro(hangupcall,) will terminate the call, but I left them there for safety. Note that if you were to comment out the Macro(hangupcall,), the last two lines would execute, and then if more than one trunk was specified in the Outbound Route, it would keep retrying the call on each of those trunks (and the caller would hear the error message multiple times, and you’d get multiple IM’s). Although I can’t personally envision such a situation, there might be situations where trying the additional trunks would be desirable.

Next is where the call goes if the caller attempted to dial a premium (900 or 976) number. This is similar to the previous section, except the recording played to the caller is one second of silence followed by a standard system recording. The silence is necessary to keep the first word or two of the recording from being cut off on some clients. Also the IM I send to myself is slightly different. And the same comment applies on the last three lines. I know, I could have done a Goto to the same three lines in the previous section, but that would have only saved me two lines, and since the last two lines are superfluous anyway, if you are that tight on space you can just delete them.

exten => s,n(premium),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a premium number call - NOT perrmitted)
exten => s,n,Jabbersend(asterisk,system_administrator_address@gmail.com,${CALLERID(num)} attempted to call a premium number ${OUTNUM} at ${STRFTIME(${EPOCH},,%l:%M:%S %p %Z on %A %B %e)})
exten => s,n,Playback(silence/1&sorry-cant-let-you-do-that3)
exten => s,n,Macro(hangupcall,)
exten => s,n,Set(PREDIAL_HOOK_RET=BYPASS)
exten => s,n,MacroExit()

The next sections contain the usa, canada, tollfree, and inum labels. All they do is print an appropriate message to the CLI and exit. Rather than jump to these labels, I could have simply used statements of the form ExecIf($[${REGEX(“${regx}” ${OUTNUM})} = 1]?MacroExit()) and left out this entire next section, but I like the idea of being able to see which regex allowed the call in the CLI, and this also allows for the possibility of additional code. For example, I could insert code that keeps a running tally of calls broken down by their destination. I have no need to do anything like that, but maybe you do:

exten => s,n(usa),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a USA call)
exten => s,n,MacroExit()
exten => s,n(canada),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a Canada call)
exten => s,n,MacroExit()
exten => s,n(tollfree),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a toll free call)
exten => s,n,MacroExit()
exten => s,n(inum),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is an inum call)
exten => s,n,MacroExit()

This last line is important and was omitted from the default macro. It handles the case where the caller hangs up while this code is executing. Although that window is only a small fraction of second, bad things could happen if the caller does manage to hang up within that window and this statement isn’t present:

exten => h,1,Macro(hangupcall,)

Well, that’s how I’m doing it right now. It may not be how I do it two months from now, but this seems better than any method I have used thus far.

I will point out, for whatever it’s worth, that you COULD use an Asterisk dial command right in this macro. So, for example, let’s say you had a trunk that was real expensive at a certain time of the day but much cheaper during another. You COULD test to see if the call is being made within the “cheap” time and if so just exit, but if not you could execute a dial command that looks like the one normally executed, which is…

exten => s,n,Dial(${OUT_${DIAL_TRUNK}}/${OUTNUM},300,${DIAL_TRUNK_OPTIONS})

…except that in place of ${OUT_${DIAL_TRUNK}} you could specify a different trunk, for example SIP/OtherTrunk. And of course you could use this macro to change the dial trunk options for specific trunks, or to play a recording to callers if their call is going out over an “expensive” trunk. In fact, I can see where this macro could be used to get to get around some of the FreePBX syntax checking that has plagued me in the past.

It should probably be noted that if you watch these macros execute in the Asterisk CLI, every time one of them is executed you may see a WARNING message such as this:

[2012-11-22 11:48:12] WARNING[30588][C-00000055]: pbx.c:1585 pbx_exec: The application delimiter is now the comma, not the pipe. Did you forget to convert your dialplan? (Set(regx=^([\*]{2}1)?([\*]67)?W?1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$))

This warning doesn’t apply in this situation because the pipe IS the correct character to use in a regular expression, when you are matching a single regular expression out of several possible regular expressions. See this page on Alternation with The Vertical Bar or Pipe Symbol for more information, with the caveat that Asterisk’s expression engine is a bit different from that used in other software, and therefore may not recognize syntax that would be perfectly valid in another application. Therefore, not all examples shown on various regular expression tutorials or reference pages will work. If you wish to try a particular syntax, be sure to do a test run or two and make sure it works. But, Asterisk’s regular expression engine DOES recognize the pipe character when used as shown in the above examples, but spits out these warning messages anyway, even though the pipe characters are part of the regular expression, and are not being used as application delimiters. You can safely ignore the warnings – they are a nuisance, but are meaningless in this particular situation. If you really don’t want to see those warnings, use a text editor to go to /etc/asterisk/asterisk.conf and uncomment this line:

;dontwarn = yes                 ; Disable some warnings.

This will disable some non-essential warnings such as this one. If it were me, I would have named this option “stopwhining” rather than “dontwarn”, because Asterisk does tend to complain a lot about things that don’t matter and that you can’t do anything about. 🙂

Once again, I will mention that articles in this blog (and especially one such as this) are subject to future editing as I discover more about the subject. If you find any neat uses for the [macro-dialout-trunk-predial-hook] macro (ESPECIALLY if they allow you to do something that FreePBX tries to stop you from doing, but that’s perfectly allowable in Asterisk), feel free to leave a comment!

Did you know that Asterisk has the ability to evaluate Regular Expressions, though not in the same way as Perl or FreeSWITCH?

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

I happened to be searching the Asterisk issue tracker a few minutes ago (trying to figure out why the Asterisk Blacklist suddenly stopped working for no apparent reason on one of my systems) and happened to notice this issue which had just been posted today:

REGEX function ignores shorthand character starting with backslash

Wait, you mean Asterisk can evaluate Regular Expressions? I didn’t think it had that capability. So, working off the example shown in that issue report (the working expression, naturally) I tried putting this in the FreePBX extensions_custom.conf [macro-dialout-trunk-predial-hook] context (I had to uncomment some existing lines and add others). This is just example code and not meant to do anything useful. NOTE: Many of the lines in code blocks below won’t fit on the screen due to the way WordPress formats this blog, but if you copy and paste the lines into a text editor you should see them in their entirety:

[macro-dialout-trunk-predial-hook]
; this macro intentially left blank so it may be safely overwritten for any custom
; requirements that an installation may have.
;
; MACRO RETURN CODE: ${PREDIAL_HOOK_RET}
;                    if set to "BYPASS" then this trunk will be skipped
;
exten => s,1,NoOp(Trunk ${OUT_${DIAL_TRUNK}} selected)
exten => s,n,Set(tftest=^1?8(00|22|33|44|55|66|77|88)[2-9][0-9]{6}$)
exten => s,n,GotoIf($[${REGEX("${tftest}" ${OUTNUM})} = 1]?tollfree)
exten => s,n,NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a NOT a toll free call)
exten => s,n,MacroExit()
exten => s,n(tollfree),NoOp(${CALLERID(num)} is calling ${OUTNUM} which is a toll free call)
exten => s,n,MacroExit()

If you place an outgoing call and watch the Asterisk CLI, you will see that if you place a call it knows what trunk was selected and what number the call came from, and prints that it is or is not a toll-free call. And I assume you could get other information about the call from other system variables. This could be handy for extended call routing, or for blocking of certain calls.

The thing to note here is that the dialplan only uses only one line to test for all the possible NANP toll-free area codes, both with and without a “1” prefix — you could do this without using a regular expression, but you’d need one line per area code to test. If you’ve ever looked at the FreePBX generated dialplan (in extensions_additional.conf) you know that much of the dialplan is lines upon lines of pattern specifications. I’ve mentioned in the past the problem of specifying every USA or Canadian area code in an outbound routes (so you can disallow calls to NANP points not covered by your calling plan) but it occurs to me that if FreePBX utilized regular expressions, it MIGHT be possible to condense many lines into one. Of course this would partly depend on whether there is a length limit on a regular expression, but in later testing I tried setting REGEX variables for the USA and Canada, like this:

This line that would create a regex that would test for USA calls (50 states + D.C.):

exten => s,n,Set(usatest=^1?(201|202|203|205|206|207|208|209|210|212|213|214|215|216|217|218|219|220|223|224|225|228|229|231|234|239|240|248|251|252|253|254|256|260|262|267|269|270|272|274|276|279|281|301|302|303|304|305|307|308|309|310|312|313|314|315|316|317|318|319|320|321|323|325|327|330|331|332|334|336|337|339|346|347|351|352|360|361|364|380|385|386|401|402|404|405|406|407|408|409|410|412|413|414|415|417|419|423|424|425|430|432|434|435|440|442|443|445|458|463|469|470|475|478|479|480|484|501|502|503|504|505|507|508|509|510|512|513|515|516|517|518|520|530|534|539|540|541|551|559|561|562|563|564|567|570|571|573|574|575|580|585|586|601|602|603|605|606|607|608|609|610|612|614|615|616|617|618|619|620|623|626|628|629|630|631|636|641|646|650|651|657|660|661|662|667|669|678|680|681|682|701|702|703|704|706|707|708|712|713|714|715|716|717|718|719|720|724|725|726|727|731|732|734|737|740|743|747|754|757|760|762|763|765|769|770|772|773|774|775|779|781|785|786|801|802|803|804|805|806|808|810|812|813|814|815|816|817|818|828|830|831|832|838|843|845|847|848|850|854|856|857|858|859|860|862|863|864|865|870|872|878|901|903|904|906|907|908|909|910|912|913|914|915|916|917|918|919|920|925|928|929|930|931|934|936|937|938|940|941|947|949|951|952|954|956|959|970|971|972|973|978|979|980|984|985|986|989)[2-9][0-9]{6}$)

Yes, that is all one single line (which you won’t see in its entirety if you don’t copy and paste the part you do see into a text editor, but it’s humongous)! But it seems to work if you use the ${usatest} variable instead of ${tftest} in the line containing the REGEX function! And here’s one for Canada that puts the expression into ${cdntest}:

exten => s,n,Set(cdntest=^1?(204|226|236|249|250|289|306|343|365|367|403|416|418|431|437|438|450|506|514|519|548|579|581|587|604|613|639|647|705|709|778|780|782|807|819|825|867|873|879|902|905)[2-9][0-9]{6}$)

Of course you could test for all three types of calls in the same block of code. Just remember that the code as shown above is handling calls destined for ALL your trunks, so if you only want it to operate on a single trunk you’ll have to test for that trunk only, and bail out if the call is headed for any other trunk.

For the moment all I will say is that this opens up interesting possibilities, including possibly new ways to get around some of the developer-imposed limitations of FreePBX, and also to streamline custom dialplan. Unfortunately it appears that Asterisk’s REGEX function support is not much used nor well documented, so you may have trouble finding other examples of working code that uses this function.

Also, Matt Jordan commented on the issue linked above, noting that “REGCOMP is built on the GNU extended regular expressions library (see regex.h). That library does not support the shorthand characters (see this comparison of regular expression libraries). As such, this is not a bug, but a limitation of the library that provides the regular expression functionality.” What this apparently means is that you cannot use something like d or [:digit:] to represent any digit, as you can do in other software that recognizes regular expressions, but [0-9] works just fine for that purpose. However, it would not be correct to say that backslashes can never be used, as I found these uses of REGEX in extensions_additional.conf:

exten => s-fixed,1,ExecIf($["${REGEX("^[+]?[0-9]+$" ${DB(RINGGROUP/${NODEST}/fixedcid)})}" = "1"]?Set(__TRUNKCIDOVERRIDE=${DB(RINGGROUP/${NODEST}/fixedcid)}))
exten => s-fixed,n,Return()
exten => s-extern,1,ExecIf($["${REGEX("^[+]?[0-9]+$" ${DB(RINGGROUP/${NODEST}/fixedcid)})}" == "1" & "${FROM_DID}" != ""]?Set(__TRUNKCIDOVERRIDE=${DB(RINGGROUP/${NODEST}/fixedcid)}))
exten => s-extern,n,Return()
exten => s-did,1,ExecIf($["${REGEX("^[+]?[0-9]+$" ${FROM_DID})}" = "1"]?Set(__REALCALLERIDNUM=${FROM_DID}))
exten => s-did,n,Return()
exten => s-forcedid,1,ExecIf($["${REGEX("^[+]?[0-9]+$" ${FROM_DID})}" = "1"]?Set(__TRUNKCIDOVERRIDE=${FROM_DID}))
exten => s-forcedid,n,Return()

Note the use of [+] in the REGEX expressions above. That “escapes” the + character, which otherwise would have a meaning in a regular expression, but for some reason the escaped character has to be enclosed in square brackets.  This can lead to some non-obvious regex constructions.  For example, look again at the line that creates a regex that would test for USA calls.  It begins like this:

exten => s,n,Set(usatest=^1?(201...

The question mark after the “1” means it’s optional. But what if the provider also accepts calls prefixed with *67 to block the Caller ID? You could allow for that possibility like this in most software that accepts regular expressions, but not in Asterisk…

exten => s,n,Set(usatest=^(*67)?1?(201...

The backslash would escape the * character, and therefore the string *67 would be considered optional. But in Asterisk, you have to enclose the * in square brackets to make this work, like so:

exten => s,n,Set(usatest=^([*]67)?1?(201...

Anyway, I just wanted to bring this to your attention, in case that you (like me) were thinking that Asterisk doesn’t interpret regular expressions. It DOES, but not in the same manner as Perl, nor FreeSWITCH for that matter. If anyone knows of an online reference or “cheat sheet” for this particular variety of regular expression library, I’d appreciate the link.

Link: How to do Painless MySQL Server Backups with AutoMySQLBackup

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Image representing MySQL as depicted in CrunchBase

I have not tried or tested this, but just wanted to point it out as it might prove useful to some readers. I still prefer MondoRescue for a full system backup, but I can see how this might also come in handy in certain situations:

How to do Painless MySQL Server Backups with AutoMySQLBackup (Linux.com)

Yes, you can run FusionPBX and FreeSWITCH on a Raspberry Pi

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

By now most technically inclined folks have heard of the Raspberry Pi, the small $35 computer that can do big things. If you are going to buy one, just make sure you get one of the newer models with 512 MB of memory, rather than an older model with only 256 MB.

But, you may wonder, can I run a decent PBX system (one that won’t get in my way and treat me like a blithering idiot while I’m attempting to configure it) on a computer this small? Well, it turns out that people are doing just that:

The following guide is a relatively easy way to install FusionPBX and FreeSWITCH with the Ubuntu/Debian script.

Raspberry Pi Script (FusionPBX Wiki)

EDIT April, 2017: For a newer method see this DSLReports thread.

It should be obvious that you’ll probably find this easier if you know a bit about the Raspberry Pi first (Google it) but if you want a reliable and configurable PBX, and you think you have the skills to follow these instructions and make it work, I’d definitely give it a try. Besides, for home users, it’s a lot easier to justify a separate computer just to handle your phone calls if it’s small, cheap, and unobtrusive, and has low power consumption.

How to force FreePBX to immediately retry the same trunk again if it fails the first time even though the FreePBX developers apparently don’t want you to be able to do that

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

This is going to be a very barebones explanation because to be honest I’m really not motivated to make things easier for FreePBX users anymore.  I really want you to think about trying other PBX software that doesn’t treat its users as if they need afternoon naps and their diapers changed occasionally.

But I have found a way to overcome the limitation, however you need Webmin or phpMyAdmin, and you need to be very careful because one wrong click could screw up your database.  I use Webmin so that’s what I’ll be talking about here.  I am NOT telling you to do this on your system, and if you should choose to follow my example and mess up your system, don’t email me about it because I can’t help you and I’m warning you that this might be dangerous if you don’t know what you are doing, and I will not assume any responsibility for what you choose to do.  You break it, you own all the parts.  Also note that I’m doing this on an FreePBX 2.8 system, so if you are using another version, things might be different from what I’m describing.  This is what I have done that worked for me; it may or may not work for you, but if you wish to attempt it I strongly recommend that you make sure you have a full system backup (MondoRescue is a good program to use to create such a backup).

In Webmin, go into Servers | MySQL Database Server and in the MySQL Databases section, click on the icon or label (NOT the checkbox) for asterisk.

On the next page, you may get a message saying “There are too many tables to display. Find tables matching” followed by a search field.  If so, enter the word “Outbound” and click “Search”.

Click on the icon or label (NOT the checkbox) for outbound_route_trunks

At this point, if your browser supports it, duplicate the tab so you have two tabs showing this page.  In the first, click the Export as CSV button and in the next page, select “Yes” for “Include column names in CSV?“and select “Display in browser” as the Export Destination and then click Export Now.  You should see a display showing your Outbound Routes, but since they are all identified by numbers, you won’t know which is which.  Leave this tab open and do not reload it.  This is a “snapshot” of your Outbound Route trunk selections as the database sees them, before you make any changes.

Now open another browser tab and go to the Outbound Route page for the route where you want to select the same trunk twice.  In the Trunk Sequence for Matched Routes section, where you’d add the desired trunk the second time, add ANY other trunk instead, and Submit Changes, but don’t click the orange bar to apply the configuration changes.  Do make sure that everything is correct in the route configuration because after you finish this you won’t be able to make any changes without repeating all these tedious steps, which would be totally unnecessary if… never mind.  And make a mental note of the total number of trunks in the list, and don’t make any changes to any other Outbound Routes until you’ve completed this process. EDIT: Also, while that same Outbound Route page is open, look at your browser’s URL bar and note the address that is open — that may include the route_id number that will be needed in the next step. For example, if the address is https://your.server.address/admin/config.php?display=routing&extdisplay=21 then 21 is most likely the route_id number.

Now go back to the duplicated Webmin tab (the one NOT showing the CSV display), and click View Data. Under route_id, each outbound route is identified by a number, and the number of times that number appears equals the number of trunks in the Trunk Sequence for Matched Routes section,  Note that if there are more than 25 entries you may have to page through them to find the route you are trying to change.  So, let’s say that your Outbound Route originally had one trunk selection, and you added the second one which is incorrect, just to force FreePBX to accept it, so there is now a total of two trunks for that route.  You’d look through the route_id’s to find one where the route_id number is listed twice (as many times as the number of trunk selections) and note the route_id number.  You would then flip back to the other tab showing the CSV output to see if that route was only listed once (only had one trunk selected) when you took the CSV “snapshot”.  If so, you have found the correct route; if not, keep looking.

The trunk order is determined by the number in the seq column and is zero based, so if you had two trunks total, and the first trunk was correct and the second was the incorrect trunk that you added, then look for the line with the correct route_id number and 0 in the seq column, and note the trunk_id for that line.  Now find the line with the same route_id number but with 1 in the seq column.  Make a mental note of the trunk_id so you can change it back if you somehow managed to get the wrong route, and click the checkbox at the start of the line and then click Edit Selected Rows.  That row should turn into text boxes and what you want to do is change the trunk_id so that it matches that of the trunk selection you want to duplicate (the one with 0 in the seq column in this example).  Click Save.

Go back to the Outbound Route page and do not click Submit Changes.  Instead, reload the page, or go to another Outbound Route and come back.  It should show the duplicated trunk where the incorrect trunk selection was.  Once again, do not click Submit Changes, but NOW you can click the orange bar to apply the configuration changes. Remember that if you EVER click “Submit Changes” on this page from now on, the duplicated trunk will be removed and you get to do this all over again (unless you can figure out how to modify the source code so it doesn’t do that). 🙁

If you use phpMyAdmin you should be able to do something similar.  Just remember that every time you need to go through this tedious procedure, you can silently “thank” the FreePBX developers for once again making the extra effort to make life difficult for their experienced users.

If you’ve somehow managed to screw up your system by doing this (and I hope you don’t), just remember I warned you that doing this could be dangerous, but on the flip side, now would be a really good time to try some other software (you could try FusionPBX, for example).

Using YATE to overcome Google Voice issues in FreeSWITCH and Asterisk

 

Notice
(May, 2018): FreePBX and Asterisk users that wish to continue using Google Voice after Google drops XMPP support should go here: How to use Google Voice with FreePBX and Asterisk without using XMPP or buying new hardware. The information in this article is VERY outdated and probably will not work.

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

If you have been less than thrilled with the Google Voice support in another software PBX, such as Asterisk or FreeSWITCH, you could try using YATE as a Google Voice Gateway.  It can be installed on either a separate server, or on the same server as your FreeSWITCH or Asterisk installation, however if you are running virtual machines then I recommend the separate server approach.  In fact, that may be the only way to do it with FreeSWITCH if you installed FreeSWITCH under Debian or Ubuntu, since the YATE install requires CentOS.  If you are a Linux expert you may be able to get around this, but don’t ask me how.

To install YATE, see this article from Nerd Vittles:

YATE in a Flash: Rolling Your Own SIP to Google Voice Gateway for Asterisk

EDIT: You may want to upgrade YATE to the latest version.

Just follow the instructions there, and the ones that you see after running the script to add a Google Voice user, and you should be fine, if you are using Asterisk.  The only things I would suggest that are not shown in those instructions are that you set your Trunk “Maximum Channels” to 2, because a Google Voice account will only permit two simultaneous channels of usage maximum, and that if YATE is on a separate server with a static IP address then I’d suggest adding permit/deny lines to the Asterisk Trunk PEER details to enhance security, like so:

permit=xx.xx.xx.xx/255.255.255.255
deny=0.0.0.0/0.0.0.0

Make sure the lines appear in that order, and replace xx.xx.xx.xx with the static IP address of the YATE server.  This may not help much because Asterisk is registering with the YATE server, but it can’t hurt either.

Also, you might want to consider changing the context statement to

context=from-pstn-e164-us

to remove the +1 from the start of the Caller ID number on incoming calls.

The instructions don’t tell you to add a Dialed Number Manipulation Rule to your trunk configuration, but if you want to allow ten digit calls from any of your endpoints then you should add one rule that prepends 1 to 10 digit calls:

1+NXXNXXXXXX (The 1 goes in the first field, the NXXNXXXXXX in the third field)

If you are using the CallerID Superfecta module, and you use “Trunk Provided” as one of your data source, then after adding a Google Voice account to YATE I suggest editing /usr/local/etc/yate/regexroute.conf on the YATE server. You may need to install an editor first. For example, to install nano and then edit the file:

yum install nano
nano /usr/local/etc/yate/regexroute.conf

Look for the [contexts] section and there you will see a line for each of your Google Voice accounts that looks like this:

${in_line}GV1234567890=;called=GV1234567890;jingle_version=0;jingle_flags=noping;dtmfmethod=rfc2833

Just add ;callername to the end of each such line:

${in_line}GV1234567890=;called=GV1234567890;jingle_version=0;jingle_flags=noping;dtmfmethod=rfc2833;callername

This will make sure that nothing is sent for a Caller ID name, so that Caller ID Superfecta will recognize that there is no “Trunk Provided” name and attempt to do a name lookup (note that you could also use ;callername=something to set the Caller ID name to a specific value). If you want to have ;callername
automatically appended whenever you create a new account, just use an editor to edit the script you use to add users, and find the line that looks like this (it should be near the bottom of the script):

${in_line}GV’$acctphone’=;called=GV’$acctphone’;jingle_version=0;jingle_flags=noping;dtmfmethod=rfc2833

Add ;callername to the end of the line, like so:

${in_line}GV’$acctphone’=;called=GV’$acctphone’;jingle_version=0;jingle_flags=noping;dtmfmethod=rfc2833;callername

Save the modified file, and any time you add a new user it will automatically write that line with ;callername appended.

Thanks to Bill Simon for telling me about this method of sending the blank Caller ID name. Alternately, if you don’t want to mess with the YATE configuration, you could add a new Caller ID Scheme in Caller ID Superfecta that is only used with your Google Voice DID’s and that doesn’t include “Trunk Provided” as a data source.

Whether you are connecting from Asterisk or FreeSWITCH, if YATE is running on a separate server and the other system can’t register with YATE, it may be a firewall issue on the YATE server.  After I did the install I found that iptables was configured to only allow incoming ssh connections.  I modified that rule to only allow incoming ssh from a particular IP address (the one I’d be coming in from) and then added rules to permit traffic from the two servers allowed to talk to that YATE server.

EDIT: Hopefully this will not affect you if you have upgraded YATE to the latest version, but if you have a moderate number of Google Voice accounts, you may experience another issue.  If you start seeing messages like this when you telnet to YATE and then use debug on to see what is happening:

<sip:MILD> Flood detected: 20 handled events

And if every so often, the server appears to go into a semi-catatonic state, where calls come in but they don’t go out (this happened to me at least twice before I figured out what was happening), then you may have this issue.  It occurs when you have the same Asterisk server using multiple trunks to connect to YATE.  It turns out that whenever you reload Asterisk (as you might after making a configuration change, for example the “orange bar reload” in one particular GUI), it resends all of the registrations at once, and gives them all a default timeout of 120 seconds, so they all attempt to re-register at the exact same intervals.  And if you have several trunks, there are a LOT of SIP packets sent.  Plus, with qualifyfreq value set to 240, that means that every other time the registrations are taking place, qualifies are also taking place at the same time.  It appears that this is sufficient to cause that warning to appear once in a while.

The method I found that seems to fix this may not be the best way (feel free to comment if you know a better way), but it’s one way to deal with it.  What you need to do is change the registration expiration on each individual trunk so they are not all the same.  In Asterisk this can be accomplished by adding both of these settings to the trunk configuration (susbtitute nn with some random number of seconds, say between 90 and 120, and make it the same for both settings in each trunk, but different for different trunks)

In the trunk PEER details, add:

defaultexpiry=nn

In the Register String, add  ~nn  to the end of the line, replacing nn with the same value used in the defaultexpiry setting, like so:
GV1234567890:password@exampleaddress.com:5060/1234567890~nn

You might also need to vary the qualifyfreq value a bit in each trunk, so that it’s a bit under the specified 240 seconds and different for each trunk.  If doing those things doesn’t fix the issue, and you still get the <sip:MILD> Flood detected: 20 handled events message frequently, that could mean you are being subjected to an actual SIP attack.  The YATE installation includes a script with the filename /usr/src/yate/share/scripts/banbrutes.php that can be used to deal with that issue, but it’s not enabled by default.  View the banbrutes.php script in a text editor, and you’ll find instructions at the beginning of the script.  Or, you could tighten up the iptables firewall to only allow traffic from systems that are supposed to be talking to your YATE server.

END OF EDIT.

As for FusionPBX, when you create a new Google Voice account on the YATE server using the provided add-yate-user script, at the end it will give you a bunch of configuration information for Asterisk.  These translate to FusionPBX Gateway settings as follows (showing what the script prints and the equivalent FusionPBX Gateway settings):

Trunk Name: YIAF1 ; or increment 1 if more than one (in FusionPBX I suggest you don’t use this; instead use the same setting as the Username for the Gateway name, particularly if you plan on having more than one Google Voice account)

host=x.x.x.x (Proxy in FusionPBX)
username=GV1234567890 (Username in FusionPBX)
secret=password (Password in FusionPBX)
type=peer (Not needed in FusionPBX)
port=5060 (Not needed in FusionPBX)
qualify=yes (Not needed in FusionPBX)
qualifyfreq=240 (Not needed in FusionPBX)
insecure=port,invite (Not needed in FusionPBX)
context=from-trunk (Not needed in FusionPBX)

Register String: … (Not needed in FusionPBX)

In FusionPBX, set Register to True and Enabled to True, and leave other Gateway settings at the defaults (EDIT: however, if you have several gateways to YATE, you might want to use the Expire seconds setting in FusionPBX to vary the registration timeouts a bit so that all your accounts aren’t trying to re-register at exactly the same time — see the longer EDIT section above for details).  Note that after you save the settings, it may take a few seconds for the state to change to REGED, so refresh the Gateways page after a bit and it should be okay if everything is configured properly and there are no firewall issues.

For your Inbound Route in FusionPBX, just use the Trunk Name/Username as the Destination Number (including the leading “GV“, which you can also use it in the Inbound Route name field if you like) and then choose the appropriate Action. When you first create the Inbound Route it will complain if you try to save a Destination Number that is not completely numeric, so just use any number and save the settings, then go back and edit the Destination Number field and also the Data field for the destination_number condition (which should be something like ^GV1234567890$, substituting your Google Voice number for the digits, of course).

For your Outbound Route, select your Google Voice trunk as the Gateway, and then select “11 digits long distance” from the dropdown in the “Dialplan Expression” setting. Save that, and if you only have one Google Voice trunk for all users on the system, that is all you need to do.  However, if you want to have multiple Google Voice trunks and have certain extensions only have access to certain trunks, the edit the Outbound Route you just created, and in the “Conditions and Actions” section at the bottom of the page, edit the last action on the page (the “bridge” action).  You want to change the Data field – it will contain something like sofia/gateway/GV1234567890/$1 and you want to change that to sofia/gateway/${accountcode}/$1 — save that change, and then when the Outbound Route page reappears, you may want to change the name to ${accountcode}.11d and add a Description like “Google Voice: Extension Account Code = Gateway Name” so you understand what the route is doing.  This single Outbound Route will handle all your Google Voice calls from all your extensions, if the Account Code setting for each Extension is set to the name of the Gateway for the Google Voice account you want that extension to use.

Note that if you are running PBX in a Flash, you can use the “Caller ID Superfecta” module to try to get a Caller ID name.  IF YATE itself has any ability to do Caller ID name lookups, someone will have to tell me how to enable and configure it, because at this point I would have no clue.  If you know, please leave a comment!

EDIT: To keep the YATE log file from growing too large over time, copy the file /usr/src/yate/packing/yate.logrotate into /etc/logrotate.d as “yate” (get rid of the .logrotate extension).  That file instructs the system logrotate job to roll the yate log file when it gets to 100 MB.  Thanks to Bill Simon for that tip!

EDIT 2: If you have ignored the advice given almost everywhere to create a new, separate Gmail account, and then use that account when you create your Google Voice account, then you have probably run into the issue of not receiving your incoming calls when you are logged into that Google account and for some time thereafter.  That problem, and one possible fix (along with the drawbacks) were discussed in a post in the thread “YATE in a Flash 1.2 Ready” on the PBX in a Flash Forum, which unfortunately disappeared from that site due to a server crash.  The post, originally by user Marian on Aug 6, 2012, read as follows:

Gmail sets a greater resource priority when you connect and don’t advertise unavailable for a while after you disconnect.
So, if you connect to GMail using the same account as yate the calls will be sent there until GMail advertise resource unavailable.
You can set priority=10 in accfile.conf, gmail account section.
But, if you do that you might not see your chat in GMail or another jabber client connected to GMail for the same account (like GTalk or Yate Client).
Unfortunately, the jabber protocol don’t allow setting different priorities for the same resource for different services (e.g. you can’t set a priority for chat and another one for another capatibility, like jingle calls).
I didn’t found a workaround for this situation: having, for the same account, a resource for chat and another one for jingle calls.
This would require a custom jabber client or a custom jabber server.

That, coupled with information from other posts around the web, means the best advice is to add a line of the form:

priority=127

in each of your Google Voice accounts in the file accfile.conf (in the /usr/local/etc/yate directory).

If you want that line to be added by default when you add a new Google Voice account to your YATE server, open the add-yate-user script (which is probably in your /root directory) in a text editor such as nano, and find this line:

echo “options=allowplainauth” >> accfile.conf

and underneath it add this:

echo “priority=127″ >> accfile.conf

Then save the edited file.  I make no guarantees that this will actually work, but it’s worth a try. NOTE: The thread mentioned above suggested setting the priority to 10, however, the Asterisk developers are now using 25. As this wiki page explains:

More about Priorities

As many different connections to Google are possible simultaneously via different client mechanisms, it is important to understand the role of priorities in the routing of inbound calls. Proper usage of the priority setting can allow use of a Google account that is not otherwise entirely dedicated to voice services.

With priorities, the higher the setting value, the more any client using that value is preferred as a destination for inbound calls, in deference to any other client with a lower priority value. Known values of commonly used clients include the Gmail chat client, which maintains a priority of 20, and the Windows GTalk client, which uses a priority of 24. The maximum allowable value is 127. Thus, setting one’s priority option for the XMPP peer in res_xmpp.conf to a value higher than 24 will cause inbound calls to flow to Asterisk, even while one is logged into either Gmail or the Windows GTalk client.

Outbound calls are unaffected by the priority setting.

This would be true in Asterisk OR YATE, therefore the recommendation is to now use at least 25 as the priority value, up to the maximum of 127 as suggested above.

Link: Google Voice Customers Cry Out For Help, No One At Google Hears Them

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

The Consumerist is just waking up to a fact that many of us Google Voice users realized a long time ago:  There is virtually no such thing as customer support at Google Voice.  For example, they still haven’t fixed the bug that even if you disable call screening, it’s still turned on if the calls are delivered via Google Chat, and that’s been a problem for at least three or four years now.  Nor have they come up with a way to change the amount of time the call rings at the destination before Google snatches it back and sends it to Google Voice’s voicemail (approximately 25 seconds is just too short in some situations).

The Consumerist article doesn’t touch on either of those specific issues, but at least they’re beginning to understand that the complete lack of effective support at Google Voice can really be a problem:

Google Voice Customers Cry Out For Help, No One At Google Hears Them (The Consumerist via the Wayback Machine)

Yes, I know it’s a free service and some will say you get what you pay for, and I guess that will fly as long as the service remains free, but when they charge for a service (such as the number port mentioned in the article) then they should at least have an effective way to address issues and complaints about the services people have paid for (and perhaps not received)!

 

Two things I wish you could do in Asterisk or FreePBX, or ANY free software PBX

 

Important
This is a heavily edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

I want to explain a problem that apparently exists in current implementations of Asterisk and FreePBX (and by extension, all distributions based on those pieces of software).

Let’s say you have several extensions on your system and many, if not all of them, have a specific “trunk” associated with that extension.  It may be a provider account or a Google Voice account that’s used exclusively by that extension.  Routing INCOMING calls is usually not difficult at all, you simply use the trunk’s DID in an Inbound Route and then route the calls from that DID directly to the desired extension.  However, OUTBOUND is another matter.  You have to create an Outbound Route, and in that route you have to put your dial patterns and use the /extension suffix. It can still be difficult to set up the dial patterns the way you need them.  In 2.8 and later it is much harder because of the individual boxes for each segment of each pattern.

Let’s say you want certain extensions to only be able to call numbers in U.S. area codes, but each of those extensions has its own trunk. And let’s say your extensions are numbered 1000 through 1099. Oh, and you want to support both 10 and 11 digit dialing. So in your outbound route for extension 1000, you might have a list of patterns like this (please scroll down to the end of this long list — it’s only about 600 lines!):

1201NXXXXXX/1000
1202NXXXXXX/1000
1203NXXXXXX/1000
1205NXXXXXX/1000
1206NXXXXXX/1000
1207NXXXXXX/1000
1208NXXXXXX/1000
1209NXXXXXX/1000
1210NXXXXXX/1000
1212NXXXXXX/1000
1213NXXXXXX/1000
1214NXXXXXX/1000
1215NXXXXXX/1000
1216NXXXXXX/1000
1217NXXXXXX/1000
1218NXXXXXX/1000
1219NXXXXXX/1000
1224NXXXXXX/1000
1225NXXXXXX/1000
1228NXXXXXX/1000
1229NXXXXXX/1000
1231NXXXXXX/1000
1234NXXXXXX/1000
1239NXXXXXX/1000
1240NXXXXXX/1000
1248NXXXXXX/1000
1251NXXXXXX/1000
1252NXXXXXX/1000
1253NXXXXXX/1000
1254NXXXXXX/1000
1256NXXXXXX/1000
1260NXXXXXX/1000
1262NXXXXXX/1000
1267NXXXXXX/1000
1269NXXXXXX/1000
1270NXXXXXX/1000
1274NXXXXXX/1000
1276NXXXXXX/1000
1281NXXXXXX/1000
1301NXXXXXX/1000
1302NXXXXXX/1000
1303NXXXXXX/1000
1304NXXXXXX/1000
1305NXXXXXX/1000
1307NXXXXXX/1000
1308NXXXXXX/1000
1309NXXXXXX/1000
1310NXXXXXX/1000
1312NXXXXXX/1000
1313NXXXXXX/1000
1314NXXXXXX/1000
1315NXXXXXX/1000
1316NXXXXXX/1000
1317NXXXXXX/1000
1318NXXXXXX/1000
1319NXXXXXX/1000
1320NXXXXXX/1000
1321NXXXXXX/1000
1323NXXXXXX/1000
1325NXXXXXX/1000
1327NXXXXXX/1000
1330NXXXXXX/1000
1331NXXXXXX/1000
1334NXXXXXX/1000
1336NXXXXXX/1000
1337NXXXXXX/1000
1339NXXXXXX/1000
1347NXXXXXX/1000
1351NXXXXXX/1000
1352NXXXXXX/1000
1360NXXXXXX/1000
1361NXXXXXX/1000
1364NXXXXXX/1000
1385NXXXXXX/1000
1386NXXXXXX/1000
1401NXXXXXX/1000
1402NXXXXXX/1000
1404NXXXXXX/1000
1405NXXXXXX/1000
1406NXXXXXX/1000
1407NXXXXXX/1000
1408NXXXXXX/1000
1409NXXXXXX/1000
1410NXXXXXX/1000
1412NXXXXXX/1000
1413NXXXXXX/1000
1414NXXXXXX/1000
1415NXXXXXX/1000
1417NXXXXXX/1000
1419NXXXXXX/1000
1423NXXXXXX/1000
1424NXXXXXX/1000
1425NXXXXXX/1000
1430NXXXXXX/1000
1432NXXXXXX/1000
1434NXXXXXX/1000
1435NXXXXXX/1000
1440NXXXXXX/1000
1442NXXXXXX/1000
1443NXXXXXX/1000
1458NXXXXXX/1000
1469NXXXXXX/1000
1470NXXXXXX/1000
1475NXXXXXX/1000
1478NXXXXXX/1000
1479NXXXXXX/1000
1480NXXXXXX/1000
1484NXXXXXX/1000
1501NXXXXXX/1000
1502NXXXXXX/1000
1503NXXXXXX/1000
1504NXXXXXX/1000
1505NXXXXXX/1000
1507NXXXXXX/1000
1508NXXXXXX/1000
1509NXXXXXX/1000
1510NXXXXXX/1000
1512NXXXXXX/1000
1513NXXXXXX/1000
1515NXXXXXX/1000
1516NXXXXXX/1000
1517NXXXXXX/1000
1518NXXXXXX/1000
1520NXXXXXX/1000
1530NXXXXXX/1000
1534NXXXXXX/1000
1539NXXXXXX/1000
1540NXXXXXX/1000
1541NXXXXXX/1000
1551NXXXXXX/1000
1559NXXXXXX/1000
1561NXXXXXX/1000
1562NXXXXXX/1000
1563NXXXXXX/1000
1567NXXXXXX/1000
1570NXXXXXX/1000
1571NXXXXXX/1000
1573NXXXXXX/1000
1574NXXXXXX/1000
1575NXXXXXX/1000
1580NXXXXXX/1000
1585NXXXXXX/1000
1586NXXXXXX/1000
1601NXXXXXX/1000
1602NXXXXXX/1000
1603NXXXXXX/1000
1605NXXXXXX/1000
1606NXXXXXX/1000
1607NXXXXXX/1000
1608NXXXXXX/1000
1609NXXXXXX/1000
1610NXXXXXX/1000
1612NXXXXXX/1000
1614NXXXXXX/1000
1615NXXXXXX/1000
1616NXXXXXX/1000
1617NXXXXXX/1000
1618NXXXXXX/1000
1619NXXXXXX/1000
1620NXXXXXX/1000
1623NXXXXXX/1000
1626NXXXXXX/1000
1630NXXXXXX/1000
1631NXXXXXX/1000
1636NXXXXXX/1000
1641NXXXXXX/1000
1646NXXXXXX/1000
1650NXXXXXX/1000
1651NXXXXXX/1000
1657NXXXXXX/1000
1660NXXXXXX/1000
1661NXXXXXX/1000
1662NXXXXXX/1000
1667NXXXXXX/1000
1669NXXXXXX/1000
1678NXXXXXX/1000
1681NXXXXXX/1000
1682NXXXXXX/1000
1701NXXXXXX/1000
1702NXXXXXX/1000
1703NXXXXXX/1000
1704NXXXXXX/1000
1706NXXXXXX/1000
1707NXXXXXX/1000
1708NXXXXXX/1000
1712NXXXXXX/1000
1713NXXXXXX/1000
1714NXXXXXX/1000
1715NXXXXXX/1000
1716NXXXXXX/1000
1717NXXXXXX/1000
1718NXXXXXX/1000
1719NXXXXXX/1000
1720NXXXXXX/1000
1724NXXXXXX/1000
1727NXXXXXX/1000
1731NXXXXXX/1000
1732NXXXXXX/1000
1734NXXXXXX/1000
1737NXXXXXX/1000
1740NXXXXXX/1000
1747NXXXXXX/1000
1754NXXXXXX/1000
1757NXXXXXX/1000
1760NXXXXXX/1000
1762NXXXXXX/1000
1763NXXXXXX/1000
1765NXXXXXX/1000
1769NXXXXXX/1000
1770NXXXXXX/1000
1772NXXXXXX/1000
1773NXXXXXX/1000
1774NXXXXXX/1000
1775NXXXXXX/1000
1779NXXXXXX/1000
1781NXXXXXX/1000
1785NXXXXXX/1000
1786NXXXXXX/1000
1801NXXXXXX/1000
1802NXXXXXX/1000
1803NXXXXXX/1000
1804NXXXXXX/1000
1805NXXXXXX/1000
1806NXXXXXX/1000
1808NXXXXXX/1000
1810NXXXXXX/1000
1812NXXXXXX/1000
1813NXXXXXX/1000
1814NXXXXXX/1000
1815NXXXXXX/1000
1816NXXXXXX/1000
1817NXXXXXX/1000
1818NXXXXXX/1000
1828NXXXXXX/1000
1830NXXXXXX/1000
1831NXXXXXX/1000
1832NXXXXXX/1000
1843NXXXXXX/1000
1845NXXXXXX/1000
1847NXXXXXX/1000
1848NXXXXXX/1000
1850NXXXXXX/1000
1856NXXXXXX/1000
1857NXXXXXX/1000
1858NXXXXXX/1000
1859NXXXXXX/1000
1860NXXXXXX/1000
1862NXXXXXX/1000
1863NXXXXXX/1000
1864NXXXXXX/1000
1865NXXXXXX/1000
1870NXXXXXX/1000
1872NXXXXXX/1000
1878NXXXXXX/1000
1901NXXXXXX/1000
1903NXXXXXX/1000
1904NXXXXXX/1000
1906NXXXXXX/1000
1907NXXXXXX/1000
1908NXXXXXX/1000
1909NXXXXXX/1000
1910NXXXXXX/1000
1912NXXXXXX/1000
1913NXXXXXX/1000
1914NXXXXXX/1000
1915NXXXXXX/1000
1916NXXXXXX/1000
1917NXXXXXX/1000
1918NXXXXXX/1000
1919NXXXXXX/1000
1920NXXXXXX/1000
1925NXXXXXX/1000
1928NXXXXXX/1000
1929NXXXXXX/1000
1931NXXXXXX/1000
1936NXXXXXX/1000
1937NXXXXXX/1000
1938NXXXXXX/1000
1940NXXXXXX/1000
1941NXXXXXX/1000
1947NXXXXXX/1000
1949NXXXXXX/1000
1951NXXXXXX/1000
1952NXXXXXX/1000
1954NXXXXXX/1000
1956NXXXXXX/1000
1970NXXXXXX/1000
1971NXXXXXX/1000
1972NXXXXXX/1000
1973NXXXXXX/1000
1978NXXXXXX/1000
1979NXXXXXX/1000
1980NXXXXXX/1000
1984NXXXXXX/1000
1985NXXXXXX/1000
1989NXXXXXX/1000
201NXXXXXX/1000
202NXXXXXX/1000
203NXXXXXX/1000
205NXXXXXX/1000
206NXXXXXX/1000
207NXXXXXX/1000
208NXXXXXX/1000
209NXXXXXX/1000
210NXXXXXX/1000
212NXXXXXX/1000
213NXXXXXX/1000
214NXXXXXX/1000
215NXXXXXX/1000
216NXXXXXX/1000
217NXXXXXX/1000
218NXXXXXX/1000
219NXXXXXX/1000
224NXXXXXX/1000
225NXXXXXX/1000
228NXXXXXX/1000
229NXXXXXX/1000
231NXXXXXX/1000
234NXXXXXX/1000
239NXXXXXX/1000
240NXXXXXX/1000
248NXXXXXX/1000
251NXXXXXX/1000
252NXXXXXX/1000
253NXXXXXX/1000
254NXXXXXX/1000
256NXXXXXX/1000
260NXXXXXX/1000
262NXXXXXX/1000
267NXXXXXX/1000
269NXXXXXX/1000
270NXXXXXX/1000
274NXXXXXX/1000
276NXXXXXX/1000
281NXXXXXX/1000
301NXXXXXX/1000
302NXXXXXX/1000
303NXXXXXX/1000
304NXXXXXX/1000
305NXXXXXX/1000
307NXXXXXX/1000
308NXXXXXX/1000
309NXXXXXX/1000
310NXXXXXX/1000
312NXXXXXX/1000
313NXXXXXX/1000
314NXXXXXX/1000
315NXXXXXX/1000
316NXXXXXX/1000
317NXXXXXX/1000
318NXXXXXX/1000
319NXXXXXX/1000
320NXXXXXX/1000
321NXXXXXX/1000
323NXXXXXX/1000
325NXXXXXX/1000
327NXXXXXX/1000
330NXXXXXX/1000
331NXXXXXX/1000
334NXXXXXX/1000
336NXXXXXX/1000
337NXXXXXX/1000
339NXXXXXX/1000
347NXXXXXX/1000
351NXXXXXX/1000
352NXXXXXX/1000
360NXXXXXX/1000
361NXXXXXX/1000
364NXXXXXX/1000
385NXXXXXX/1000
386NXXXXXX/1000
401NXXXXXX/1000
402NXXXXXX/1000
404NXXXXXX/1000
405NXXXXXX/1000
406NXXXXXX/1000
407NXXXXXX/1000
408NXXXXXX/1000
409NXXXXXX/1000
410NXXXXXX/1000
412NXXXXXX/1000
413NXXXXXX/1000
414NXXXXXX/1000
415NXXXXXX/1000
417NXXXXXX/1000
419NXXXXXX/1000
423NXXXXXX/1000
424NXXXXXX/1000
425NXXXXXX/1000
430NXXXXXX/1000
432NXXXXXX/1000
434NXXXXXX/1000
435NXXXXXX/1000
440NXXXXXX/1000
442NXXXXXX/1000
443NXXXXXX/1000
458NXXXXXX/1000
469NXXXXXX/1000
470NXXXXXX/1000
475NXXXXXX/1000
478NXXXXXX/1000
479NXXXXXX/1000
480NXXXXXX/1000
484NXXXXXX/1000
501NXXXXXX/1000
502NXXXXXX/1000
503NXXXXXX/1000
504NXXXXXX/1000
505NXXXXXX/1000
507NXXXXXX/1000
508NXXXXXX/1000
509NXXXXXX/1000
510NXXXXXX/1000
512NXXXXXX/1000
513NXXXXXX/1000
515NXXXXXX/1000
516NXXXXXX/1000
517NXXXXXX/1000
518NXXXXXX/1000
520NXXXXXX/1000
530NXXXXXX/1000
534NXXXXXX/1000
539NXXXXXX/1000
540NXXXXXX/1000
541NXXXXXX/1000
551NXXXXXX/1000
559NXXXXXX/1000
561NXXXXXX/1000
562NXXXXXX/1000
563NXXXXXX/1000
567NXXXXXX/1000
570NXXXXXX/1000
571NXXXXXX/1000
573NXXXXXX/1000
574NXXXXXX/1000
575NXXXXXX/1000
580NXXXXXX/1000
585NXXXXXX/1000
586NXXXXXX/1000
601NXXXXXX/1000
602NXXXXXX/1000
603NXXXXXX/1000
605NXXXXXX/1000
606NXXXXXX/1000
607NXXXXXX/1000
608NXXXXXX/1000
609NXXXXXX/1000
610NXXXXXX/1000
612NXXXXXX/1000
614NXXXXXX/1000
615NXXXXXX/1000
616NXXXXXX/1000
617NXXXXXX/1000
618NXXXXXX/1000
619NXXXXXX/1000
620NXXXXXX/1000
623NXXXXXX/1000
626NXXXXXX/1000
630NXXXXXX/1000
631NXXXXXX/1000
636NXXXXXX/1000
641NXXXXXX/1000
646NXXXXXX/1000
650NXXXXXX/1000
651NXXXXXX/1000
657NXXXXXX/1000
660NXXXXXX/1000
661NXXXXXX/1000
662NXXXXXX/1000
667NXXXXXX/1000
669NXXXXXX/1000
678NXXXXXX/1000
681NXXXXXX/1000
682NXXXXXX/1000
701NXXXXXX/1000
702NXXXXXX/1000
703NXXXXXX/1000
704NXXXXXX/1000
706NXXXXXX/1000
707NXXXXXX/1000
708NXXXXXX/1000
712NXXXXXX/1000
713NXXXXXX/1000
714NXXXXXX/1000
715NXXXXXX/1000
716NXXXXXX/1000
717NXXXXXX/1000
718NXXXXXX/1000
719NXXXXXX/1000
720NXXXXXX/1000
724NXXXXXX/1000
727NXXXXXX/1000
731NXXXXXX/1000
732NXXXXXX/1000
734NXXXXXX/1000
737NXXXXXX/1000
740NXXXXXX/1000
747NXXXXXX/1000
754NXXXXXX/1000
757NXXXXXX/1000
760NXXXXXX/1000
762NXXXXXX/1000
763NXXXXXX/1000
765NXXXXXX/1000
769NXXXXXX/1000
770NXXXXXX/1000
772NXXXXXX/1000
773NXXXXXX/1000
774NXXXXXX/1000
775NXXXXXX/1000
779NXXXXXX/1000
781NXXXXXX/1000
785NXXXXXX/1000
786NXXXXXX/1000
801NXXXXXX/1000
802NXXXXXX/1000
803NXXXXXX/1000
804NXXXXXX/1000
805NXXXXXX/1000
806NXXXXXX/1000
808NXXXXXX/1000
810NXXXXXX/1000
812NXXXXXX/1000
813NXXXXXX/1000
814NXXXXXX/1000
815NXXXXXX/1000
816NXXXXXX/1000
817NXXXXXX/1000
818NXXXXXX/1000
828NXXXXXX/1000
830NXXXXXX/1000
831NXXXXXX/1000
832NXXXXXX/1000
843NXXXXXX/1000
845NXXXXXX/1000
847NXXXXXX/1000
848NXXXXXX/1000
850NXXXXXX/1000
856NXXXXXX/1000
857NXXXXXX/1000
858NXXXXXX/1000
859NXXXXXX/1000
860NXXXXXX/1000
862NXXXXXX/1000
863NXXXXXX/1000
864NXXXXXX/1000
865NXXXXXX/1000
870NXXXXXX/1000
872NXXXXXX/1000
878NXXXXXX/1000
901NXXXXXX/1000
903NXXXXXX/1000
904NXXXXXX/1000
906NXXXXXX/1000
907NXXXXXX/1000
908NXXXXXX/1000
909NXXXXXX/1000
910NXXXXXX/1000
912NXXXXXX/1000
913NXXXXXX/1000
914NXXXXXX/1000
915NXXXXXX/1000
916NXXXXXX/1000
917NXXXXXX/1000
918NXXXXXX/1000
919NXXXXXX/1000
920NXXXXXX/1000
925NXXXXXX/1000
928NXXXXXX/1000
929NXXXXXX/1000
931NXXXXXX/1000
936NXXXXXX/1000
937NXXXXXX/1000
938NXXXXXX/1000
940NXXXXXX/1000
941NXXXXXX/1000
947NXXXXXX/1000
949NXXXXXX/1000
951NXXXXXX/1000
952NXXXXXX/1000
954NXXXXXX/1000
956NXXXXXX/1000
970NXXXXXX/1000
971NXXXXXX/1000
972NXXXXXX/1000
973NXXXXXX/1000
978NXXXXXX/1000
979NXXXXXX/1000
980NXXXXXX/1000
984NXXXXXX/1000
985NXXXXXX/1000
989NXXXXXX/1000

(Note the above does not include the “toll free” area codes nor Canadian area codes; I have separate routes for those).

Now THAT is bad enough, but then imagine having to duplicate this list for each of your extensions (changing only the extension number after the / character), because each will need its own outbound route in order to select its own trunk. In pre-2.8 versions of Asterisk, you could simply copy this list into a text editor, do a search and replace on the /1000 (replacing it with the next extension number), and paste the changed list into a new outbound route. However, with the new way of entering dial plans, you have to enter each line in each field manually, OR (in 2.9 and later) mess with .CSV files, which although easier than manual entry are still a lot harder to deal with than simple cut-and-paste.

But that is actually not the subject of this article; it just sets the stage for what I’m thinking SHOULD be part of Asterisk (or any other soft PBX that requires entering patterns in a similar manner, that is, one line for each pattern). There are actually TWO ways this could be handled, but neither will work at present, as far as I know.

1) Stacking Routes

Let’s suppose you had an outbound route that had all the USA patterns, but did NOT include the extension field. You could have it near the top of your Outbound Route list. And let’s say that you could make the destination of that trunk another “group” of outbound routes rather than a trunk. In that second group, you could have routes with just two patterns per extension:

1XXXXXXXXXX/1000
XXXXXXXXXX/1000

So the call would be effectively pre-screened in the first (primary) group of outbound routes, then sent to the second group (NOT part of the primary group) which would route by extension. That way, you’d only need ONE route with a list of USA patterns, one route with a list of Canada patterns, one route with a list of “toll free” patterns, etc. Each could go directly to a trunk, or to a secondary group of outbound routes.

I think Asterisk might actually be capable of doing something like this (though I’m uncertain of that), but FreePBX definitely is not. So some FreePBX users literally have THOUSANDS of lines of dial patterns in their configuration. Does this slow things down? You betcha, at least when making a configuration change! It takes forever for that darn frog to stop eating flies (if a real frog ate that many flies in that short a time, its gut would probably explode!).

2) Macros

Now here we have a solution that would likely need to be implemented in Asterisk itself. The basic idea is to allow macros in dial patterns. For example, you create a list such as the one above (but without the /extension field – just the number patterns only) and call it [pattern-USA]. Then in your outbound routes, you do something like this:

[pattern-USA]/1000

Changing the extension as needed for each Outbound Route. As noted, this would require implementing this type of macro feature in Asterisk, but it would also necessitate a way to turn off the syntax checking in FreePBX, which is currently impossible.

EDIT: For another way to handle this that probably will work, see How to use the FreePBX [macro-dialout-trunk-predial-hook] macro and regular expressions to blacklist or whitelist outgoing calls on all trunks.