Saturday, January 12, 2013

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 sanjayws@gmail.com
    # 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/$destexten@app-fakeanswer
    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.
Post a Comment