Asterisk 10 or 11 Messaging (SMS/SIP Messaging) with offline message sending

Article revision: 3
If you read and tried my post here, you would have probably got the AstSMS working. It is important to get that part working first then read the rest of this article. This article enhances that dialplan to add offline sending of SIP message (or SMS) even if a host/SIP peer is offline and they will get the message once they come back online.
HOW IT WORKS

  • It uses Asterisk .call files to requeue
  • It checks device state using the function DEVICE_STATE to determine if a call is successful or not so that it can start/stop the queuing of the .call file
  • Runs a script within dialplan using the SYSTEM application to call a bash file with certain variables that will then auto generate the .call files

IMPORTANT

  • Since the messaging feature will automatically return OK if a device status is seen as online on Asterisk, irregarless of its actual state (probably qualify has not run yet to let asterisk know its offline). So to mitigate, reduce the qualifyfreq value on extensions sip.conf or general section.
  • I’ve not tested this in large setups or production environment. So please take caution and test.
  • DEVICE_STATE must exist/work in order for you to make this work too. E,g SIP/1000 should show me state of NOT_INUSE or UNAVAILABLE etc…Read more on DEVICE_STATES .
  • When sending messages, if there’s a break character, e.g. apostrophe, it will break the script since it runs on CLI and uses BASH, so you need to clean it up through Asterisk dialplan before sending to CLI/SYSTEM app
  • If multiple offline messages are bombarded at once, it may not be received in that order.
  • I don’t regard this as a final final solution, this would just give you an idea how to get to your final solution
  • I don’t enable CDR logs, but if you have CEL they may appear, you can re-enable it by modding NoCDR accordingly and change the priority accordingly too
  • Modify the bash script to suite your operating system, mine was designed for Debian

ENVIRONMENT (that worked for me)

  • Debian 6.0.x
  • Asterisk 10/11
  • FreePBX 2.10/2.11(Beta2) – If you don’t use FBX, modify and code accordingly
  • I had working DEVICE_STATES for SIP extensions
  • PortGo softphone as sender
  • CSIPSimple softphone on Android as recipient


HOWTO

  1. Create a temp spool folder wherever your Asterisk spool folder is at, this here is default, this will be used to spool the .call file temporarily
    # mkdir /var/spool/asterisk/temp
    # chown -R asterisk:asterisk /var/spool/asterisk/temp
  2. Create or modify your astsms context, to use this new one.  Everything should start with exten on single lines.

    [astsms]
    exten => _X.,1,NoOp(SMS receiving dialplan invoked)
    exten => _X.,n,NoOp(To ${MESSAGE(to)})
    exten => _X.,n,NoOp(From ${MESSAGE(from)})
    exten => _X.,n,NoOp(Body ${MESSAGE(body)})
    exten => _X.,n,Set(ACTUALTO=${CUT(MESSAGE(to),@,1)})
    exten => _X.,n,ExecIf($[“${ACTUALTO}” != “sip:${EXTEN}”]?Set(ACTUALTO=sip:${EXTEN}))
    exten => _X.,n,MessageSend(${ACTUALTO},${MESSAGE(from)})
    exten => _X.,n,NoOp(Send status is ${MESSAGE_SEND_STATUS})
    exten => _X.,n,GotoIf($[“${MESSAGE_SEND_STATUS}” != “SUCCESS”]?sendfailedmsg)
    exten => _X.,n,Hangup()
    ;
    ; Handle failed messaging
    exten => _X.,n(sendfailedmsg),NoOp(Sending error to user)
    exten => _X.,n,Set(SRC=${MESSAGE(from)})
    exten => _X.,n,Set(DST=${MESSAGE(to)})
    exten => _X.,n,Set(MSG=${MESSAGE(body)})
    exten => _X.,n,Set(MESSAGE(body)=”[${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)}] Your message to ${EXTEN} has failed. Sending when available”)
    exten => _X.,n,Set(ME_1=${CUT(MESSAGE(from),<,2)})
    exten => _X.,n,Set(ACTUALFROM=${CUT(ME_1,@,1)})
    exten => _X.,n,MessageSend(${ACTUALFROM},ServiceCenter)
    exten => _X.,n,GotoIf($[“${INQUEUE}” != “1”]?startq)
    exten => _X.,n,Hangup()
    ;
    exten => _X.,n(startq),NoOp(Queueing messaging for offline)
    exten => _X.,n,Set(MSGTIME=${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)})
    exten => _X.,n,SYSTEM(/var/lib/asterisk/agi-bin/astqueue.sh –SRC ‘${SRC}’ –DST ‘${DST}’ –MSG ‘${MSG}’)
    exten => _X.,n,Hangup()[app-fakeanswer]
    exten => _X.,1,NoCDR
    exten => _X.,n,Set(DESTDEV=${EXTEN})
    exten => _X.,n,Set(THISDEVSTATE=${DEVICE_STATE(SIP/${DESTDEV})})
    exten => _X.,n,GotoIf($[“${THISDEVSTATE}” = “UNAVAILABLE”]?hang)
    exten => _X.,n,GotoIf($[“${THISDEVSTATE}” = “UNKNOWN”]?hang)
    exten => _X.,n,Answer
    exten => _X.,n,Hangup()
    exten => _X.,n(hang),Hangup()

  3. Now, here’s the magic script called astqueue.sh, create it like this
    # nano /var/lib/asterisk/agi-bin/astqueue.sh
    Paste this below, adjust the maxretry and retryint so that your Asterisk wont blow up in smokes and act like on steroids retrying like crazy

    #!/bin/bash
    # v0.2
    # copyleft Sanjay Willie [email protected]
    # SCRIPT PURPOSE: GENERATE SMS OFFLINE QUEUE
    # GEN INFO: Change variables sections
    ###########################################
    #
    #VARIABLES
    maxretry=100
    retryint=30
    #
    #CONSTANTS
    ERRORCODE=0
    d_unique=`date +%s`
    d_friendly=`date +%T_%D`
    astbin=`which asterisk`
    myrandom=$[ ( $RANDOM % 1000 )  + 1 ]
    #
    function bail(){
          echo “SMS:[$ERRORCODE] $MSGOUT. Runtime:$d_friendly. UniqueCode:$d_unique”
        exit $ERRORCODE
    }
    while test -n “$1”; do
        case “$1” in
            -SRC)
                source=”$2″
                shift
               ;;
            -DST)
                dest=”$2″
                shift
               ;;
            -MSG)
                message=”$2″
                shift
               ;;
            -TIME)
                originaltime=”$2″
                shift
               ;;             
    esac
    shift
    done
    #
    #
    if [[ “$source” == “” ]]; then
        echo “ERROR: No source. Quitting.”
        ERRORCODE=1
        bail
    fi
    if [[ “$dest” == “” ]]; then
        echo “ERROR: No usable destination. Quitting.”
        ERRORCODE=1
        bail
    fi
    if [[ “$message” == “” ]]; then
        echo “ERROR: No message specified.Quitting.”
        ERRORCODE=1
        bail
    fi
    #
    # generate call file
    mydate=`date +%d%m%y`
    logdate=`date`
    #
    # Check to see if extension exist
    destexten=`echo $dest | cut -d @ -f1 | cut -d : -f2`
    ifexist=`$astbin -rx “sip show peers” | grep -c $destexten`if [[ “$ifexist” == “0” ]]; then
        echo “Destination extension don’t exist, exiting..”
        ERRORCODE=1
        bail
    fi
    # If that conditions pass, then we will queue,
    # you can write other conditions too to keep the sanity of the looping
    filename=”$destexten-$d_unique.$myrandom.call”
    echo -e “Channel: Local/[email protected]
    CallerID: $source
    Maxretries: $maxretry
    RetryTime: $retryint
    Context: astsms
    Extension: $destexten
    Priority: 1 
    Set: MESSAGE(body)=$message
    Set: MESSAGE(to)=$dest
    Set: MESSAGE(from)=$source
    Set: INQUEUE=1 “> /var/spool/asterisk/temp/$filename
    # move files
    chown asterisk:asterisk /var/spool/asterisk/temp/$filename
    chmod 777 /var/spool/asterisk/temp/$filename
    sleep 3
    mv /var/spool/asterisk/temp/$filename /var/spool/asterisk/outgoing/
    #
    ERRORCODE=0
    bail

  4. Change permission and ownership accordingly
    # chown asterisk:asterisk /var/lib/asterisk/agi-bin/astqueue.sh
    #chmod +X /var/lib/asterisk/agi-bin/astqueue.sh
  5. Try sending a message when the device is online, then try again offline. Then go back online and you should get the message.
  6. Do modify and enhance and do share if you found something interesting or bugs..er

Its already 4am here in Malaysia, …sleep now. Good luck and enjoy your weekend.

38 Comments

  1. Yes, I change "Set: MESSAGE(body)=$message" to "Set: BASE64MSG=$message" and add "ExecIf($["${INQUEUE}" = "1"]?Set(MESSAGE(body)=${BASE64_DECODE(${BASE64MSG})}))" before calling MessageSend.

    Also I'm using openwrt, the busybox came with it don't have '$RANDOM' support unless recompiling the firmware, so I have to use "$(date +%s).$$" as unique filename. Since this script will sleep 3 seconds at the end, that makes "$(date +%s).$$" truly unique.

  2. Hi Sanjay Willie, i follow your blog regularly and i am trying to do the SMS in asterisk as per the current page. i have done changes as per the asterisk and i am not using free pbx. i am using VOIP mobile phones(seowonintech-SWP-1000).

    Kindly advise

  3. Hi, I have working when both phones are online, but offline msgs are not working.
    I tried to run astqueue.sh manually and I am getting this error:

    ERROR: No source. Quitting.
    SMS:[1] . Runtime:10:33:38_04/18/13. UniqueCode:1366277618

    Can you help please.

    thank you

  4. You can't run it manually without specifying certain things.

    Show me your asterisk full log after sending an offline sms.

  5. Hi sanjay i got SMS as you said but when i am trying to do it with VoIP phones i am unable to receive in other VoIP phone. But VoIP phone can send SMS to CSipSimple.
    How can i resolve this issue please advice me.

  6. Hi sanjay,
    i did it with your code and i am able to send SMS between two CSipSimple clients and also from Voip phone to CSipSimple Client. But if i send message to VoIP mobile(either from CSipSimple or from VoIP phone) it was reaching asterisk server (showing accepted 202) but not delivered to the Voip phone.

    Please advice me

  7. Hi Sanjay, sorry for the delay. here is my log when sending the offline message.

    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:16] Set("Message/ast_msg_queue", "ME_1=sip:[email protected]>") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:17] Set("Message/ast_msg_queue", "ACTUALFROM=sip:100") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:18] MessageSend("Message/ast_msg_queue", "sip:100,ServiceCenter") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:19] GotoIf("Message/ast_msg_queue", "1?startq") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Goto (astsms,101,21)
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:21] NoOp("Message/ast_msg_queue", "Queueing messaging for offline") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:22] Set("Message/ast_msg_queue", "MSGTIME=08052013-00:02:08") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:23] System("Message/ast_msg_queue", "/var/lib/asterisk/agi-bin/astqueue.sh .SRC ."Alex" . .DST .sip:[email protected] .MSG .off2.") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: — Executing [[email protected]:24] Hangup("Message/ast_msg_queue", "") in new stack
    [2013-05-08 00:02:08] VERBOSE[1502][C-00000000] pbx.c: == Spawn extension (astsms, 101, 24) exited non-zero on 'Message/ast_msg_queue'
    [2013-05-08 00:02:45] VERBOSE[1529] chan_sip.c: — Registered SIP '101' at 192.168.1.204:48985

  8. Online message running on the xlite to another xlite working. Im using Deb6, Ast11 and Freepbx2.1. The offline message are not working. Run the astqueue manually. ERROR: No source. Quitting.
    SMS:[1] . Runtime:17:16:46_05/15/13. UniqueCode:1368609406
    Please advice me

  9. online message works on the xlite to another xite. My use Deb6, Ast11 and Freepbx2.x. offline msgs are not working.
    Running manually astqueue ERROR: No source. Quitting.
    SMS:[1] . Runtime:17:16:46_05/15/13. UniqueCode:1368609406, Please advice me

  10. Hi Catwong,

    I had the same issue. You can not copy/paste the source code from this blog just like that. There are some strange characters, missing EOL and that's why the dialplan as well as the .sh file is failing to work.

    Would be better if Sanjay would attach the source code in a txt file as well.

  11. good work i understand the code..
    but how i can use this in my dialplan should i call this macro astsms below the line answer() in my dialplan e.g:

    exten => s,1,Dial(SIP/${EXTEN})
    exten => s,2,Answer
    exten => s,3,Macro(astsms)

    that works or i have to do something else or just put all this code same as given and that would be enough.
    plz do reply thanx…

  12. Hi Sanjay
    Really nice article and great commendation from me.
    I wanted to know if there's a way to get the messages use a database instead and also, the offline messages as well.
    I know there're some products that do that like kamailio, but seems to complicated and i'll prefer the asterisk SMS solution if one can log SMS in database instead.

  13. I was searching for a hook/trigger in asterisk for this but with no luck.
    Is there a way to react on state change or on sip register / unregister?!
    Is there a way for asterisk to react on state change or on sip register / unregister?!
    For example, is there a way for asterisk to run a custom part of dialplan, a hint or something on device change or on sip register / unregister.

  14. Hi Sanjay,

    Thanx for the code, 🙂
    is there a way to get message delivery report or seen status when the message actually delivered on the other end device?
    using asterisk 10, freepbx 2, Bria 3 on mobiles and Xlite-4 on PCs,

    and what about group sms? is that possible and how?

    Your kind words are needed.

  15. Hi Sanjay,

    Thanks for the code :).

    Is it possible to group message and to get message delivery report (when it actually delivered to device)

    we are using Bria on phones, and Xlite-4 on PCs,

    Your advice is needed.

  16. Hi,
    I found this solution a great thing and I want to adapt it for running on Alpine Linux platform which uses busybox binary. When calls the script I get the error /bin/bash: can't open sip:[email protected]: no such file. I tried to adapt the script myself running it separately but is above my knowledge.
    Please, if is there someone who can help me with script adapting I will be more than happy.
    Thank you!

  17. Could you please post instruction on how to get the DEVICE_STATE working in FreePBX v 5.211.65-12 Asterisk v11.9? I have followed your instructions but the offline messaging is not working.

    I have set Enable Custom Device States to "True".

    below is the output of asterisk -vvvvvvvvr when I send a messaage to a offline client. I have edited a little for security:

    — Executing [[email protected]:1] NoOp("Message/ast_msg_queue", "SMS receiving dialplan invoked") in new stack
    — Executing [[email protected]:2] NoOp("Message/ast_msg_queue", "To sip:[email protected]") in new stack
    — Executing [[email protected]:3] NoOp("Message/ast_msg_queue", "From "Michael" ") in new stack
    — Executing [[email protected]:4] NoOp("Message/ast_msg_queue", "Body This is a test message to a offline client.") in new stack
    — Executing [[email protected]:5] Set("Message/ast_msg_queue", "ACTUALTO=sip:5552002") in new stack
    — Executing [[email protected]:6] ExecIf("Message/ast_msg_queue", "0?Set(ACTUALTO=sip:5552002)") in new stack
    — Executing [[email protected]:7] MessageSend("Message/ast_msg_queue", "sip:5552002,"Michael" ") in new stack
    — Executing [[email protected]:8] NoOp("Message/ast_msg_queue", "Send status is FAILURE") in new stack
    — Executing [[email protected]:9] GotoIf("Message/ast_msg_queue", "1?sendfailedmsg") in new stack
    — Goto (astsms,5552002,11)
    — Executing [[email protected]:11] NoOp("Message/ast_msg_queue", "Sending error to user") in new stack
    — Executing [[email protected]:12] Set("Message/ast_msg_queue", "SRC="Michael A. Gates" ") in new stack
    — Executing [[email protected]:13] Set("Message/ast_msg_queue", "DST=sip:[email protected]") in new stack
    — Executing [[email protected]:14] Set("Message/ast_msg_queue", "MSG=This is a test message to a offline client.") in new stack
    — Executing [[email protected]:15] Set("Message/ast_msg_queue", "MESSAGE(body)="[18052014-23:26:42] Your message to 5552002 has failed. Sending when available"") in new stack
    — Executing [[email protected]:16] Set("Message/ast_msg_queue", "ME_1=sip:[email protected]>") in new stack
    — Executing [[email protected]:17] Set("Message/ast_msg_queue", "ACTUALFROM=sip:5552000") in new stack
    — Executing [[email protected]:18] MessageSend("Message/ast_msg_queue", "sip:5552000,ServiceCenter") in new stack
    — Executing [[email protected]:19] GotoIf("Message/ast_msg_queue", "1?startq") in new stack
    — Goto (astsms,5552002,21)
    — Executing [[email protected]:21] NoOp("Message/ast_msg_queue", "Queueing messaging for offline") in new stack
    — Executing [[email protected]:22] Set("Message/ast_msg_queue", "MSGTIME=18052014-23:26:42") in new stack
    — Executing [[email protected]:23] System("Message/ast_msg_queue", "/var/lib/asterisk/agi-bin/astqueue.sh –SRC ‘"Michael" ’ –DST ‘sip:[email protected]’ –MSG ‘This is a test message to a offline client.’") in new stack
    — Executing [[email protected]:24] Hangup("Message/ast_msg_queue", "") in new stack
    == Spawn extension (astsms, 5552002, 24) exited non-zero on 'Message/ast_msg_queue'

  18. What I get is No such command 'core show states' (type 'core show help core show states' for other possible commands)

    Also seems like the file .call file is not being generated when I check the temp and the outgoing directories.

    I do have a DEVICE_STATE function for what it's worth.

  19. The output of core show hint 5552000
    [email protected] : SIP/5552000&Custom:D State:Idle Watchers 0
    1 hint matching extension 5552000

    The output of core show hint 5552002 is:

    core show hint 5552002
    [email protected] : SIP/5552002&Custom:D State:Unavailable Watchers 0
    1 hint matching extension 5552002

    In my test I tried to send a message from 5552000 to 5552002. When they are both online no issue, when 5552002 is offline the message never sends and I'm not sure if it is queuing.

    Hope this helps, I would really like to get this working.

  20. I just want to say that my knowledge of Linux and Asterisk is pretty low. With that being said I'm looking at your astqueue.sh script to see how the .call file is generated and I'm not seeing how the file is actually written to disk?

    This line:
    Set: INQUEUE=1 "> /var/spool/asterisk/temp/$filename

    not sure if I'm off base here? Should there be a " and the end of this line?

    Thanks for any help.

  21. I have fixed the issues for freepbx 5.211 with the centos 6.5.

    There were a couple of problem like the [app-fakeanswer] context was missing. I have also hacked the astqueue.sh script to not leak usage about a user when he/she is not online. If you are using Bria for Android if you try to text a non existent number you will get a message in the same windows that the extension doesn't exist. If you are using Zoiper you will get a message from a unknown user. Don't know why.

    You can get astqueue.sh from http://pastebin.com/9frJ8aBS

    you can get extensions_custom.conf from http://pastebin.com/RhM4kSwm

  22. Taking the code from Paul Walsh I have gotten the messages to queue in order and wanted to post the code a directions:

    1. create a folder /var/spool/asterisk/hold
    2. change owner using the command:
    chown -R asterisk:asterisk /var/spool/asterisk/hold
    3. get astqueue.sh from my pastebin
    http://http://pastebin.com/wdAi0AA7
    4. get astcron.sh from my pastebin
    http://pastebin.com/DviuuquB

    5. put them in your /var/lib/asterisk/agi-bin folder
    I did that by using a ftp server, the "get" command is very helpful otherwise you can just cut a paste into nano
    6. change permission to 775
    chmod 775 /var/lib/asterisk/agi-bin/ast*.sh
    7. use nano to add the following lines to your crontab:

    nano /etc/crontab

    #Below Handles sending of queued messages in order
    * * * * * asterisk /var/lib/asterisk/agi-bin/astcron.sh

    That's it your all done! Thanks everyone for helping me accomplish this.

  23. Hello All,

    Thanks to Sanjay and rest who contributed to this SMS – asterisk setup.
    Question: I am a newbie to texting/SMSing. I have a default Android 4.4 phone,with no data plan on it.
    I have went through the steps described here.
    Now how do I send a text message with the default Messaging app that is built into Android. I also have CSipSimple installed as my voip program along with an GoogleVoice account which does work.
    Can someone give me ,,,a
    1.
    2.
    3.
    steps to make this happen?

    Thank You

  24. To use the SMS system here you would need to have your own Asterisk based PBX system. If you have that then you will need a client like Zoiper or Bria for android. Once you have all that and you configure the PBX correctly you will only be able to text numbers connected to your PBX. If you want to text outside of your PBX then you will need a SMS gateway provider. This article is about configuring your own PBX for SMS on your local extensions.

  25. Everything works fine except the case statement within astqueue.sh
    Although it looks fine the problem exists.

    That's why people receive a message "ERROR: No source. Quitting."

    I use Debian 7.6 minimum install

    setting source, dest, message to $2, $4 and $6 respectively makes it work. Unfortunately I'm not able to identify the problem.

  26. Thank you guys for your work. I also add line "NoCDR" at the top because asterisk, after I send the message, records the call when the recepient comes back online.

    [astsms]
    exten => _X.,1,NoCDR
    exten => _X.,n,NoOp(SMS receiving dialplan invoked)
    exten => _X.,n,NoOp(To ${MESSAGE(to)})

    ..
    .

Comments are closed.