* work on FreeBSD (like FreeNAS 9)
* support different ESSD commands for account balance and expiration date (like Aldi Talk)
* accept character encodings with German umlauts
* fix some misleading debug output
German users: check out the smsd.config file with German provider settings in this forum.
#!/usr/local/bin/bash
###########################################################################
# Global settings:
get_balance_after=5 # 1 = get the balance after at least one message is sent.
max_age=24 # an integer for hours, 0 = disable periodic checking.
alert_balance=5 # an integer for "euros".
alert_expiration=14 # an integer for days.
# set to "" for no notification
alert_to="491771401303"
###########################################################################
# Device depended settings:
# Comment out the next command if a DEVICENAME setting should be taken from
# the name of this file (which can be a symbolic link). For example:
# /var/spool/sms/regular_run/GSM1.sh
#DEVICENAME="GSM1"
[ -z "$DEVICENAME" ] && tmp=${0##*/} && DEVICENAME=${tmp%%.*}
balance_file="/var/spool/sms/stats/${DEVICENAME}.balance"
counter_file="/var/spool/sms/stats/${DEVICENAME}.counter"
get_balance_flagfile="/var/spool/sms/stats/get.${DEVICENAME}.balance"
regular_run_cmdfile="/var/spool/sms/regular_run/${DEVICENAME}.cmdfile"
regular_run_statfile="$2"
outgoing="/var/spool/sms/outgoing"
###########################################################################
# START OF OPERATOR SPECIFIC SETTINGS
# If more than one modem is used and they have different operator specific
# settings, move this section to the files for each modem and include the
# file here using the following command:
# . /var/spool/sms/regular_run/${DEVICENAME}.operator_settings.sh
# The USSD commands to output to the modem
# several commands may be concatenated with "\n"
# first command should be for balance, optional second for expiration date
ussd_command="AT+CUSD=1,\"*100#\",15\nAT+CUSD=1,\"*102#\",15"
# This is an example result for the query:
# 2010-05-09 14:16:11,5, GSM1: CMD: AT+CUSD=1,"*100#": OK +CUSD: 2, ...
# ... "Liittym�si saldo on 22.36 EUR. Puheaikasi vanhenee 27.04.2011.",15
# Defines how balance can be found from the result:
balance_prefix="Ihr Guthaben beträgt: "
# Defines how the balance amount part ends (NOT the radix symbol):
balance_suffix=" ."
# Defines how the expiration date can be found.
# With an empty setting the expiration is not checked.
balance_expiration="endet am "
# Helper function for converting date from the answer to the format yyyy-mm-dd
# Modify this if necessary.
extract_expiration()
{
# datestamp to format yyyy-mm-dd
echo "${1:6:4}-${1:3:2}-${1:0:2}"
}
: <<COMMENTBLOCK
Example 2:
..."Balance subscription is EUR 22.36. Your allotted time expires 04/27/2011."
balance_prefix="EUR "
balance_suffix="."
balance_expiration="expires "
Command in extract_expiration(): echo "${1:6:4}-${1:0:2}-${1:3:2}"
Example 3:
..."The balance is 34.84 B. & valid until 26/07/10 "
balance_prefix="is "
balance_suffix="."
balance_expiration="until "
Command in extract_expiration(): echo "20${1:6:2}-${1:3:2}-${1:0:2}"
Example 4:
...Ihr Restguthaben ist CHF 18,87
balance_prefix="CHF "
balance_suffix=","
balance_expiration=""
COMMENTBLOCK
# END OF OPERATOR SPECIFIC SETTINGS
###########################################################################
substr()
{
local string=$1
local prefix=$2
local suffix=$3
local ppref=${string%${prefix}*}
# Changed 2010-11-02:
if [[ "$ppref" == *${prefix}* ]]
then
string=${string//\"/}
local position=$(echo | awk '{
print index("'"${string}"'", "'"${prefix}"'")
}')
if [ $position -gt 0 ]; then
ppref=${string:0:$(($position - 1))}
fi
fi
# -------------------
local ssuff=${string#*${suffix}}
local nopref=${string#${ppref}${prefix}}
echo ${nopref%${suffix}${ssuff}}
}
#--------------------------------------------------------------------------
date2stamp()
{
case `uname` in
FreeBSD)
if [ ${#1} -gt 10 ]; then
date -juf "%Y-%m-%d %H:%M:%S" "$1" +%s
else
date -juf "%Y-%m-%d" "$1" +%s
fi
;;
Darwin)
date -juf "%Y-%m-%d %H:%M:%S" "$1" +%s
;;
*)
date --utc --date "$1" +%s
;;
esac
}
#--------------------------------------------------------------------------
dateDiff()
{
local sec=0
case $1 in
-s) sec=1; shift;;
-m) sec=60; shift;;
-h) sec=3600; shift;;
-d) sec=86400; shift;;
*) sec=86400;;
esac
local dte1=$(date2stamp "$1")
local dte2=$(date2stamp "$2")
local diffSec=$((dte2-dte1))
echo $((diffSec/sec))
}
###########################################################################
test -e "$counter_file" || exit 1
# triggering balance based on number of SMSes sent requires stats: in smsd.conf place
# stats = <directory of this script>
messages=$(formail -zx ${DEVICENAME}: < "$counter_file")
if [ "$1" = "PRE_RUN" ]; then
get_balance=0
# Get the balance if it was requested:
[ -w "$get_balance_flagfile" ] && get_balance=1 && \
unlink "$get_balance_flagfile"
if [ -r "$balance_file" ]; then
# Get the balance if a defined number of messages were sent.
messagesb=$(formail -zx Messages: < "$balance_file")
[ $(($messages - $messagesb)) -ge $get_balance_after ] && get_balance=1
else
# Get the balance because the previous value is not known.
get_balance=1
fi
# Check the age of the last query if necessary:
if [ $get_balance = 0 ] && [ $max_age -gt 0 ]; then
last_query=$(formail -zx Last_query: < "$balance_file")
age=$(dateDiff -h "$last_query" "$(date +"%Y-%m-%d %T")")
[ $age -ge $max_age ] && get_balance=1
fi
# NOTE: option -e necessary for bash on FreeBSD to process '\n' between commands
[ $get_balance -gt 0 ] && echo -e "$ussd_command" > "$regular_run_cmdfile"
else
# 2011-06-29: Check that USSD command is found:
result=""
if [ -r "$regular_run_statfile" ]; then
tmp=${ussd_command%%"\n"*} # throw away all but first ussd command
tmp=${tmp//\"/\\\"} # escape double quotes
tmp=${tmp//\*/\\*} # escape asterisks
# concatenate responses and check if it contains the first command
result=$(tr -d "\n\r" < "$regular_run_statfile" | grep "$tmp")
fi
if [ -n "$result" ]; then
balance_low=-1 # Initial value means unknown.
balance=-1
balance_alerted=""
expiration_low=-1
expiration=-1
expiration_alerted=""
sms_alert=""
current_alert=""
# 2010-11-02: Change Ctrl-B to $
result=$(echo "${result//$'\x02'/$}")
# Check that required words exists:
if [[ "$result" == *${balance_prefix}* ]] && \
[[ "$result" == *${balance_suffix}* ]]
then
# Get the balance and check it:
balance=$(substr "$result" "$balance_prefix" "$balance_suffix")
balance_low=0
if [ $(expr "$balance" + 1 2> /dev/null) ]; then
[ $balance -le $alert_balance ] && balance_low=1
else
#echo "Error while parsing an integer: $balance"
echo "Balance $balance is above warning threshold $alert_balance"
fi
else
echo "Error while parsing the answer (balance): $result"
fi
# Get the expiration date if defined, and check it:
if [ -n "$balance_expiration" ]; then
if [[ "$result" == *${balance_expiration}* ]]; then
expiration_low=0
expiration=$(substr "$result" "$balance_expiration" "")
expiration=$(extract_expiration "$expiration")
expiration=$(dateDiff -d "$(date +"%Y-%m-%d")" "$expiration")
echo "time until expiration in days is $expiration days"
[ $expiration -le $alert_expiration ] && expiration_low=1
else
echo "Error while parsing the answer (expiration): $result"
fi
fi
if [ -e "$balance_file" ]; then
# Get previous values:
current_alert=$(formail -zx Current_alert: < "$balance_file")
balance_alerted=$(formail -zx Balance_alerted: < "$balance_file")
expiration_alerted=$(formail -zx Expiration_alerted: < "$balance_file")
fi
balance_gone_low=0 # added 2010-11-03
# If not yet alerted and the balance has gone low, alert now:
if [ -z "$balance_alerted" ] && [ $balance_low = 1 ]; then
balance_gone_low=1
balance_alerted=$(date +"%Y-%m-%d %T")
tmp="The balance has gone low ($balance)."
echo "$tmp" # This message goes to the smsd.log
[ -z "$sms_alert" ] && sms_alert="Alert:"
sms_alert="${sms_alert} ${tmp}"
# If the expiration is already alerted, but the issue is still active,
# include it in the message:
if [ -n "$expiration_alerted" ] && [ $expiration_low = 1 ]; then
sms_alert="${sms_alert} The expiration is also near ($expiration days)."
fi
fi
# Remove an outdated alert:
[ -n "$balance_alerted" ] && [ $balance_low = 0 ] && balance_alerted=""
# If not yet alerted and the expiration is near, alert now:
if [ -z "$expiration_alerted" ] && [ $expiration_low = 1 ]; then
expiration_alerted=$(date +"%Y-%m-%d %T")
tmp="The expiration is near ($expiration days)."
echo "$tmp" # This message goes to the smsd.log
[ -z "$sms_alert" ] && sms_alert="Alert:"
sms_alert="${sms_alert} ${tmp}"
# If the balance is already alerted, but the issue is still active,
# include it in the message:
if [ -n "$balance_alerted" ] && [ $balance_low = 1 ]; then
if [ $balance_gone_low -eq 0 ]; then
sms_alert="${sms_alert} The balance is also low ($balance)."
fi
fi
fi
# Remove an outdated alert:
[ -n "$expiration_alerted" ] && [ $expiration_low = 0 ] && \
expiration_alerted=""
[ -z "$balance_alerted" ] && [ -z "$expiration_alerted" ] && \
current_alert=""
if [ -n "$sms_alert" ] && [ -n "$alert_to" ]; then
# Send the SMS:
FILE=$(mktemp /tmp/alert_XXXXXX)
echo "To: $alert_to" >> $FILE
echo "" >> $FILE
echo "$DEVICENAME $sms_alert" >> $FILE
FILE2=$(mktemp "${outgoing}/send_XXXXXX")
mv $FILE "$FILE2"
fi
# Save the details:
DATE=$(date +"%Y-%m-%d %T")
echo "Last_query: $DATE" > "$balance_file"
[ -n "$sms_alert" ] && current_alert="${DATE}, $sms_alert"
[ -n "$current_alert" ] && \
echo "Current_alert: $current_alert" >> "$balance_file"
echo "Messages: $messages" >> "$balance_file"
echo "Balance: $balance" >> "$balance_file"
[ -n "$balance_expiration" ] && \
echo "Expiration: $expiration" >> "$balance_file"
[ -n "$balance_alerted" ] && \
echo "Balance_alerted: $balance_alerted" >> "$balance_file"
[ -n "$expiration_alerted" ] && \
echo "Expiration_alerted: $expiration_alerted" >> "$balance_file"
#echo "" >> "$balance_file"
#echo "$result" >> "$balance_file"
fi
fi
exit 0
###########################################################################
# Global settings:
get_balance_after=5 # 1 = get the balance after at least one message is sent.
max_age=24 # an integer for hours, 0 = disable periodic checking.
alert_balance=5 # an integer for "euros".
alert_expiration=14 # an integer for days.
# set to "" for no notification
alert_to="491771401303"
###########################################################################
# Device depended settings:
# Comment out the next command if a DEVICENAME setting should be taken from
# the name of this file (which can be a symbolic link). For example:
# /var/spool/sms/regular_run/GSM1.sh
#DEVICENAME="GSM1"
[ -z "$DEVICENAME" ] && tmp=${0##*/} && DEVICENAME=${tmp%%.*}
balance_file="/var/spool/sms/stats/${DEVICENAME}.balance"
counter_file="/var/spool/sms/stats/${DEVICENAME}.counter"
get_balance_flagfile="/var/spool/sms/stats/get.${DEVICENAME}.balance"
regular_run_cmdfile="/var/spool/sms/regular_run/${DEVICENAME}.cmdfile"
regular_run_statfile="$2"
outgoing="/var/spool/sms/outgoing"
###########################################################################
# START OF OPERATOR SPECIFIC SETTINGS
# If more than one modem is used and they have different operator specific
# settings, move this section to the files for each modem and include the
# file here using the following command:
# . /var/spool/sms/regular_run/${DEVICENAME}.operator_settings.sh
# The USSD commands to output to the modem
# several commands may be concatenated with "\n"
# first command should be for balance, optional second for expiration date
ussd_command="AT+CUSD=1,\"*100#\",15\nAT+CUSD=1,\"*102#\",15"
# This is an example result for the query:
# 2010-05-09 14:16:11,5, GSM1: CMD: AT+CUSD=1,"*100#": OK +CUSD: 2, ...
# ... "Liittym�si saldo on 22.36 EUR. Puheaikasi vanhenee 27.04.2011.",15
# Defines how balance can be found from the result:
balance_prefix="Ihr Guthaben beträgt: "
# Defines how the balance amount part ends (NOT the radix symbol):
balance_suffix=" ."
# Defines how the expiration date can be found.
# With an empty setting the expiration is not checked.
balance_expiration="endet am "
# Helper function for converting date from the answer to the format yyyy-mm-dd
# Modify this if necessary.
extract_expiration()
{
# datestamp to format yyyy-mm-dd
echo "${1:6:4}-${1:3:2}-${1:0:2}"
}
: <<COMMENTBLOCK
Example 2:
..."Balance subscription is EUR 22.36. Your allotted time expires 04/27/2011."
balance_prefix="EUR "
balance_suffix="."
balance_expiration="expires "
Command in extract_expiration(): echo "${1:6:4}-${1:0:2}-${1:3:2}"
Example 3:
..."The balance is 34.84 B. & valid until 26/07/10 "
balance_prefix="is "
balance_suffix="."
balance_expiration="until "
Command in extract_expiration(): echo "20${1:6:2}-${1:3:2}-${1:0:2}"
Example 4:
...Ihr Restguthaben ist CHF 18,87
balance_prefix="CHF "
balance_suffix=","
balance_expiration=""
COMMENTBLOCK
# END OF OPERATOR SPECIFIC SETTINGS
###########################################################################
substr()
{
local string=$1
local prefix=$2
local suffix=$3
local ppref=${string%${prefix}*}
# Changed 2010-11-02:
if [[ "$ppref" == *${prefix}* ]]
then
string=${string//\"/}
local position=$(echo | awk '{
print index("'"${string}"'", "'"${prefix}"'")
}')
if [ $position -gt 0 ]; then
ppref=${string:0:$(($position - 1))}
fi
fi
# -------------------
local ssuff=${string#*${suffix}}
local nopref=${string#${ppref}${prefix}}
echo ${nopref%${suffix}${ssuff}}
}
#--------------------------------------------------------------------------
date2stamp()
{
case `uname` in
FreeBSD)
if [ ${#1} -gt 10 ]; then
date -juf "%Y-%m-%d %H:%M:%S" "$1" +%s
else
date -juf "%Y-%m-%d" "$1" +%s
fi
;;
Darwin)
date -juf "%Y-%m-%d %H:%M:%S" "$1" +%s
;;
*)
date --utc --date "$1" +%s
;;
esac
}
#--------------------------------------------------------------------------
dateDiff()
{
local sec=0
case $1 in
-s) sec=1; shift;;
-m) sec=60; shift;;
-h) sec=3600; shift;;
-d) sec=86400; shift;;
*) sec=86400;;
esac
local dte1=$(date2stamp "$1")
local dte2=$(date2stamp "$2")
local diffSec=$((dte2-dte1))
echo $((diffSec/sec))
}
###########################################################################
test -e "$counter_file" || exit 1
# triggering balance based on number of SMSes sent requires stats: in smsd.conf place
# stats = <directory of this script>
messages=$(formail -zx ${DEVICENAME}: < "$counter_file")
if [ "$1" = "PRE_RUN" ]; then
get_balance=0
# Get the balance if it was requested:
[ -w "$get_balance_flagfile" ] && get_balance=1 && \
unlink "$get_balance_flagfile"
if [ -r "$balance_file" ]; then
# Get the balance if a defined number of messages were sent.
messagesb=$(formail -zx Messages: < "$balance_file")
[ $(($messages - $messagesb)) -ge $get_balance_after ] && get_balance=1
else
# Get the balance because the previous value is not known.
get_balance=1
fi
# Check the age of the last query if necessary:
if [ $get_balance = 0 ] && [ $max_age -gt 0 ]; then
last_query=$(formail -zx Last_query: < "$balance_file")
age=$(dateDiff -h "$last_query" "$(date +"%Y-%m-%d %T")")
[ $age -ge $max_age ] && get_balance=1
fi
# NOTE: option -e necessary for bash on FreeBSD to process '\n' between commands
[ $get_balance -gt 0 ] && echo -e "$ussd_command" > "$regular_run_cmdfile"
else
# 2011-06-29: Check that USSD command is found:
result=""
if [ -r "$regular_run_statfile" ]; then
tmp=${ussd_command%%"\n"*} # throw away all but first ussd command
tmp=${tmp//\"/\\\"} # escape double quotes
tmp=${tmp//\*/\\*} # escape asterisks
# concatenate responses and check if it contains the first command
result=$(tr -d "\n\r" < "$regular_run_statfile" | grep "$tmp")
fi
if [ -n "$result" ]; then
balance_low=-1 # Initial value means unknown.
balance=-1
balance_alerted=""
expiration_low=-1
expiration=-1
expiration_alerted=""
sms_alert=""
current_alert=""
# 2010-11-02: Change Ctrl-B to $
result=$(echo "${result//$'\x02'/$}")
# Check that required words exists:
if [[ "$result" == *${balance_prefix}* ]] && \
[[ "$result" == *${balance_suffix}* ]]
then
# Get the balance and check it:
balance=$(substr "$result" "$balance_prefix" "$balance_suffix")
balance_low=0
if [ $(expr "$balance" + 1 2> /dev/null) ]; then
[ $balance -le $alert_balance ] && balance_low=1
else
#echo "Error while parsing an integer: $balance"
echo "Balance $balance is above warning threshold $alert_balance"
fi
else
echo "Error while parsing the answer (balance): $result"
fi
# Get the expiration date if defined, and check it:
if [ -n "$balance_expiration" ]; then
if [[ "$result" == *${balance_expiration}* ]]; then
expiration_low=0
expiration=$(substr "$result" "$balance_expiration" "")
expiration=$(extract_expiration "$expiration")
expiration=$(dateDiff -d "$(date +"%Y-%m-%d")" "$expiration")
echo "time until expiration in days is $expiration days"
[ $expiration -le $alert_expiration ] && expiration_low=1
else
echo "Error while parsing the answer (expiration): $result"
fi
fi
if [ -e "$balance_file" ]; then
# Get previous values:
current_alert=$(formail -zx Current_alert: < "$balance_file")
balance_alerted=$(formail -zx Balance_alerted: < "$balance_file")
expiration_alerted=$(formail -zx Expiration_alerted: < "$balance_file")
fi
balance_gone_low=0 # added 2010-11-03
# If not yet alerted and the balance has gone low, alert now:
if [ -z "$balance_alerted" ] && [ $balance_low = 1 ]; then
balance_gone_low=1
balance_alerted=$(date +"%Y-%m-%d %T")
tmp="The balance has gone low ($balance)."
echo "$tmp" # This message goes to the smsd.log
[ -z "$sms_alert" ] && sms_alert="Alert:"
sms_alert="${sms_alert} ${tmp}"
# If the expiration is already alerted, but the issue is still active,
# include it in the message:
if [ -n "$expiration_alerted" ] && [ $expiration_low = 1 ]; then
sms_alert="${sms_alert} The expiration is also near ($expiration days)."
fi
fi
# Remove an outdated alert:
[ -n "$balance_alerted" ] && [ $balance_low = 0 ] && balance_alerted=""
# If not yet alerted and the expiration is near, alert now:
if [ -z "$expiration_alerted" ] && [ $expiration_low = 1 ]; then
expiration_alerted=$(date +"%Y-%m-%d %T")
tmp="The expiration is near ($expiration days)."
echo "$tmp" # This message goes to the smsd.log
[ -z "$sms_alert" ] && sms_alert="Alert:"
sms_alert="${sms_alert} ${tmp}"
# If the balance is already alerted, but the issue is still active,
# include it in the message:
if [ -n "$balance_alerted" ] && [ $balance_low = 1 ]; then
if [ $balance_gone_low -eq 0 ]; then
sms_alert="${sms_alert} The balance is also low ($balance)."
fi
fi
fi
# Remove an outdated alert:
[ -n "$expiration_alerted" ] && [ $expiration_low = 0 ] && \
expiration_alerted=""
[ -z "$balance_alerted" ] && [ -z "$expiration_alerted" ] && \
current_alert=""
if [ -n "$sms_alert" ] && [ -n "$alert_to" ]; then
# Send the SMS:
FILE=$(mktemp /tmp/alert_XXXXXX)
echo "To: $alert_to" >> $FILE
echo "" >> $FILE
echo "$DEVICENAME $sms_alert" >> $FILE
FILE2=$(mktemp "${outgoing}/send_XXXXXX")
mv $FILE "$FILE2"
fi
# Save the details:
DATE=$(date +"%Y-%m-%d %T")
echo "Last_query: $DATE" > "$balance_file"
[ -n "$sms_alert" ] && current_alert="${DATE}, $sms_alert"
[ -n "$current_alert" ] && \
echo "Current_alert: $current_alert" >> "$balance_file"
echo "Messages: $messages" >> "$balance_file"
echo "Balance: $balance" >> "$balance_file"
[ -n "$balance_expiration" ] && \
echo "Expiration: $expiration" >> "$balance_file"
[ -n "$balance_alerted" ] && \
echo "Balance_alerted: $balance_alerted" >> "$balance_file"
[ -n "$expiration_alerted" ] && \
echo "Expiration_alerted: $expiration_alerted" >> "$balance_file"
#echo "" >> "$balance_file"
#echo "$result" >> "$balance_file"
fi
fi
exit 0
Enjoy!