Thursday, February 11, 2021

asterisk pjsip extension conf for sending offline messages

 Hy!

Hard to find some good documentation about queuing offline voip/im text messages in asterisk voip software.

But, even if that was just a short hobby, i searched and i searched .... and i found one script using outgoing folder in /var/spool/asterisk and .call files.

I grabbed the script and i modified according to my software and environment.

This is a very basic script and such is not intended for complex scenarios, but is neat and do the job.

The script, together with some snippets from my pjsip.conf file and extensions.conf file are distributed and published here AS IS -  whitout any responsability and guarantee. 

If the message was sent successfuly, then you will receive a short message informing you about that. Of course, your corespondent will receive the message.


The script verifies if the number dialed exists using pjsip cli command - pjsip list auths.

I do not know if that command is listing ALL the users(extensions) as defined in pjsip.conf, or merely is listing all the users who are logged in at a certain time. And i do not care! :) 

If the number (extension) doesn't exist then you will receive a message saying that.

 If your corespondent is not online, then you will receive a short informing message.

The message will be sent to queue. The script will retry to transmit the message for about 1 day. This interval is editable. The script will do his job even for multiple failed messages, but i do not know if will keep the order of messages when the client goes online again.

 

If your queued message has been transmitted successfuly, then you will receive a short message informing you about that.

Bellow you will find all the lines. Atention, blogger has no "code" edit, so be aware of possible line breaks - those might render the script useless if you just copy-paste and do not perform some basic edit on it!

 #!/bin/bash
# v0.2
# copyleft Sanjay Willie sanjayws@gmail.com
# SCRIPT PURPOSE: GENERATE SMS OFFLINE QUEUE
# GEN INFO: Change variables sections
###########################################

######            NOTE  ###############################
# above script was modified by l u c i a n _ g e   0 u t l 0 0 k  and you know what is following.......
# for lubuntu 18.04, asterisk 18 compiled from source
# attention: this script do not cover every scenario
# for your needs it might be necessary to modify again this script
# this script is free and is distributed AS IS, with absolutely no guaranty and
# no responsabillity involved from creator
# this script is not intended for production scenarios but is merely an example
# i'm, but no mean, an expert in voip, this was just a 2 days hobby
# i must say, the documentation regarding this subject was awfully hard to find
# and fuck every knukledragger and monkey washed brain inbreed  out there on #net who doesn't know anything else but "search google"!!!!!!! this is not an #answer, nor a constructive discussion, you filthy big poo inbreed!
# fuck you, first i do not use google, second - you are an inbreed motherfucker
# communist. if you are so smart, why don't you answer and post something
# constructive, you braind dead knuckledragger inbreed monkey?

####################################################
maxretry=1000
retryint=60
# aprox. 1 day for retrying to transmit
ERRORCODE=0
#astbin='which asterisk' # no need of extra here, will use absolute path /usr/sbin/asterisk
# yes, this is a hardcoded command path. atention here, use

#your own environment path.
myrandom=$[ ( $RANDOM % 1000 )  + 1 ]

#random number for naming the queued .call files

echo "begin"

#exista=$(/usr/sbin/asterisk -rx pjsip show auths | grep -c '903')
#echo 'exista'
#echo $exista, too lazy to use bash debugger
#
function error_exit(){
      echo "error: $1"      
      exit
}

source=$1
dest=$2
message=$3
# arguments : source of message, destination of message and message body
# those are c U ming from extension.conf
#source is like pjsip:901, destination is like pjsip:903@somedomain.com

##################### ATTENTION #########################
##################   ATTENTION

#                A T T E N T I O N


# attention: message body is not filtered, need some filtering
# NOT TESTED
# NOT TESTED
# NOT TESTED, NOT IMPLEMENTED
# RIGHT NOW IS TESTED WITH MESSAGES WHITOUT ANY SPECIAL CHAR
# MESSAGE BODY NEED SOME BASIC FILTERING
#################################
#echo $source
#echo $dest
#echo $message
#lazy bastard, why don't u use debbuger?!
if [[ "$source" == "" ]]; then
    error_exit "message has no source"
fi
if [[ "$dest" == "" ]]; then
    error_exit "message has nodestination"
fi
if [[ "$message" == "" ]]; then
    error_exit "message has no body"
fi
#

# Check to see if pjsip extension is in auths list
#if is not, then the number is wrong - some typo
#destination_number='echo "$dest"' | cut -d @ -f1 | cut -d : -f2
 # example: from pjsip:901@somedomain.com we get 901 (extension number)
 destination_number=$(echo $dest | cut -d\@ -f1 | cut -d\: -f2)
 #echo "destinatie"
 #echo $destination_number

###################################### 1 wrong number

function destination_exist(){
    /usr/sbin/asterisk -rx 'pjsip show auths' | grep -c $destination_number

}

ifexist=$(destination_exist)
#echo "ifexist"
#echo $ifexist


 if [[ "$ifexist" == "0" ]]; then
    echo "Destination does not exist in dialplan"
    #send a message back to inform that destination does not exist in server dialplan
    filename="$destination_number-$myrandom.call"

#attention, random file name is not random enough, need more

    number=$(echo $source | cut -d\: -f2)

#atention here, the following lines in .call file must be set w/out any spaces

#at the beginning of the new lines
    echo "Channel: Local/$number@app-fakeanswer
CallerID: $source
Maxretries: $maxretry
RetryTime: $retryint
Context: mess
Extension: $destination_number
Priority: 1
Set: MESSAGE(body)='error the dialed number does not exist in server dialplan'
Set: MESSAGE(to)=$source
Set: MESSAGE(from)=MessageCenter
Set: INQUEUE=1 " | tee /var/spool/asterisk/outgoing/$filename
          error_exit "error: destination number does not exist in auths list for pjsip"
fi


############################################################### 1





#################################### 2 connectivity loss
#
#######################  A T E N T I O N
# Interesting situation:
# device is disconnected but in CLI > "pjsip show contacts" command output  is available
#i guess this is because time expire of registration or something similar
#like a session expire (qualify_frequency?)
# in this case the message is lost, unless is there
#some sort of check if the device is REALLY online BEFORE sending the message

########### ATENTION  

#sometimes the device goes offline for some time
#this is the second scenario
#maybe are more possibillities but for now
#this is it

#so, device is offline and we know that already
#SEND THE MESSAGE STRAIGHT TO QUEUE


source=$(echo $source | cut -d\: -f2)

echo "Channel: Local/$destination_number@app-fakeanswer
CallerID: $source
Maxretries: $maxretry
RetryTime: $retryint
Context: mesajetext
Extension: $destination_number
Priority: 1
Set: MESSAGE(body)=$message
Set: MESSAGE(to)=$dest
Set: MESSAGE(from)=$source
Set: INQUEUE=1 "


filename="$destination_number-$myrandom.call"
echo "Channel: Local/$destination_number@app-fakeanswer
CallerID: $source
Maxretries: $maxretry
RetryTime: $retryint
Context: mess
Extension: $destination_number
Priority: 1
Set: MESSAGE(body)=$message
Set: MESSAGE(to)=pjsip:$destination_number
Set: MESSAGE(from)=$source
Set: INQUEUE=1 " | tee /var/spool/asterisk/outgoing/$filename

exit 


this was the script (in folder /var/lib/asterisk/agi-bin).

Bellow you will find some fragments from pjsip.conf and extensions.conf matched with the above script. if you do not match what is needed, the whole thing will be useless.

[app-fakeanswer]
exten => _X.,1,NoCDR
exten => _X.,n,Set(DESTDEV=${EXTEN})
exten => _X.,n,Set(THISDEVSTATE=${DEVICE_STATE(PJSIP/${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()


[mess]
exten => _X.,1,Set(DEVSTATE=${DEVICE_STATE(PJSIP/${EXTEN})})
same => n,GotoIf($["${DEVSTATE}" = "UNAVAILABLE"]?errormsg)
same => n,GotoIf($["${DEVSTATE}" = "INVALID"]?errormsg)
same => n,GotoIf($["${DEVSTATE}" = "UNKNOWN"]?errormsg)
same => n,MessageSend(pjsip:${EXTEN},${MESSAGE(from)})
same => n,NoOp(Send status is ${MESSAGE_SEND_STATUS})
same => n,GotoIf($[“${MESSAGE_SEND_STATUS}” = “SUCCESS”]?sendok)
same => n,Hangup()
same => n(sendok),NoOp(Sending ok message transmitted to user)
same => n,Set(MESAJORIGINAL=${MESSAGE(body)})
same => n,Set(MESSAGE(body)=”[${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)}] Your message to ${EXTEN} has been sent.”)
same => n,Set(ME_1=${CUT(MESSAGE(from),<,2)})
same => n,Set(ACTUALFROM=${CUT(ME_1,@,1)})
same => n,Set(EU=${CUT(ACTUALFROM,:,2)})
same => n,MessageSend(pjsip:${EU},MessageCenter)
same => n,Hangup()
same => n(errormsg),NoOp(Sending error to user)
same => n,Set(MESAJORIGINAL=${MESSAGE(body)})
same => n,Set(MESSAGE(body)=”[${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)}] Your message to ${EXTEN} has failed. Sending when available”)
same => n,Set(ME_1=${CUT(MESSAGE(from),<,2)})
same => n,Set(ACTUALFROM=${CUT(ME_1,@,1)})
same => n,Set(EU=${CUT(ACTUALFROM,:,2)})
same => n,MessageSend(pjsip:${EU},MessageCenter)
same => n,GotoIf($[“${INQUEUE}” != “1”]?startq)
same => n,Hangup()
 exten => _X.,n(startq),NoOp(Queueing messaging for offline)
 same => n,Set(MSGTIME=${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)})
 same => n,Set(SRC=pjsip:${EU})
 same => n,Set(DST=${MESSAGE(to)})
 same => n,Set(MSG=${MESAJORIGINAL})
 same => n,SYSTEM(/var/lib/asterisk/agi-bin/sendlater.sh  "${SRC}" "${DST}" "${MSG}")
 same => n,Hangup()

###############

bellow is a fragment from pjsip.conf
[901]
type=aor
max_contacts=1
remove_existing=yes
qualify_frequency=600
qualify_timeout=6.0

[901]
type=auth
auth_type=userpass
username=901
password=somepassword
 
[901]
type=endpoint
aors=901
auth=901
callerid="xxx" <901>
context=internal
disallow=all
allow=g722
allow=ulaw
allow=alaw
allow=vp8
;dtmf_mode=rfc4733

;above line with dtmf will break some mobile clients, i guess dtmf type need to ;match those types accepted by the client software

;media_encryption=sdes ;
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
;transport=transport-tls
message_context=mess
allow_subscribe=yes
subscribe_context=subscribe


I hope it will be useful someday to someone! Do not forget, this was just for fun, a short 2 days hobby. Do not expect miracles, but do expect something functioning and useful for a start!