Skip to content

Instantly share code, notes, and snippets.

@maddingue
Created August 30, 2017 09:23
Show Gist options
  • Save maddingue/174dc80eecc3483dc0e269b143cf2f4f to your computer and use it in GitHub Desktop.
Save maddingue/174dc80eecc3483dc0e269b143cf2f4f to your computer and use it in GitHub Desktop.
skeleton program for procesing incoming messages, suitable for being used as a Gammu RunOnReceive handler
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
no warnings qw< void >;
use Getopt::Long;
$::PROGRAM = "incoming-sms-handler";
$::VERSION = "0.1.5";
my @raspis = qw< ... >;
my @zabbix = qw< ... >;
MAIN: {
main() unless caller();
}
#
# main()
# ----
sub main {
# default options
my %options = (
debug => 0,
errfile => "/var/log/$::PROGRAM.err",
logfile => "/var/log/$::PROGRAM.log",
);
# parse options
Getopt::Long::Configure(qw< no_auto_abbrev no_ignore_case >);
GetOptions(\%options, qw{
help|usage|h! man! version|V!
debug|d! errfile=s logfile=s
}) or pod2usage(0);
if (not $options{debug}) {
# redirect standard output & error
open STDOUT, ">>", $options{errfile};
open STDERR, ">&", \*STDOUT;
STDOUT->autoflush(1);
STDERR->autoflush(1);
}
# create dispatcher object
my $dispatcher = SMS::Dispatcher->new(\%options);
# process each message
for my $n (1 .. $ENV{SMS_MESSAGES}) {
my $from = $ENV{"SMS_${n}_NUMBER"};
my $text = $ENV{"SMS_${n}_TEXT"};
$text =~ s/^\s+|\s+$//g; # trim spaces
$dispatcher->log("from=[$from] text=[$text]");
# check sender number
$dispatcher->log("invalid sender number [$from]") and next
unless $from =~ /^\+?[0-9]+$/;
# extract the command from the text
my ($cmd, @args) = split /\s+/, $text;
$cmd = lc $cmd;
# find the corresponding method
my $method = SMS::Dispatcher::Handler->can($cmd);
$dispatcher->log("no handler for command [$cmd]") and next
unless $method;
# execute the handler
$dispatcher->$method({
from => $from,
command => $cmd,
args => \@args,
});
}
}
# ==============================================================================
package SMS::Dispatcher;
use File::Temp qw< tempfile >;
use POSIX qw< strftime >;
# This namespace contains utility methods for the handlers.
#
# new()
# ---
sub new {
return bless $_[1], $_[0]
}
#
# log()
# ---
sub log {
my $self = shift;
my $ts = strftime "%F %T", localtime;
my ($func) = map { s/^SMS::Dispatcher:://; $_ } (caller(1))[3];
my $msg = "$ts [$func] @_\n";
if ($self->{debug}) {
print STDERR $msg;
}
else {
if (open my $fh, ">>", $self->{logfile}) {
print {$fh} $msg;
close $fh;
}
}
return 1
}
#
# furl()
# ----
# Create and return a Furl object, configured for the particular uses
# within this program
#
sub furl {
require Furl;
require IO::Socket::SSL;
import IO::Socket::SSL qw< SSL_VERIFY_NONE >;
my $furl = Furl->new(
timeout => 1,
ssl_opts => { SSL_verify_mode => SSL_VERIFY_NONE() },
);
return $furl
}
#
# send_sms()
# --------
# Send a SMS; when in debug mode, write it to standard output
# Args:
# - recipient number
# - text message, either as a plain string or as an arrayref
#
sub send_sms {
my ($self, $to, $msg) = @_;
my $text;
if (ref $msg) {
if (ref $msg eq "ARRAY") {
$text = join "\n", @$msg;
}
}
else {
$text = $msg;
}
# write the text in a file
my ($fh, $path) = tempfile("sms-answer-XXXXXX", TMPDIR => 1, UNLINK => 1);
print {$fh} $text;
close $fh;
# execute the script to send the SMS
if ($self->{debug}) {
$self->log("exec: gammu-smsd-inject TEXT $to -unicode < $path");
$self->log("$path:");
system "/bin/cat $path";
print STDERR "\n";
}
else {
system "gammu-smsd-inject TEXT $to -unicode < $path";
}
}
# ==============================================================================
package SMS::Dispatcher::Handler;
# Add in this namespace the command handlers, with the method name being the
# command name. If you want to create command aliases, you can do this with
# method aliases:
#
# *meep = \&ping; # "meep" now is an alias for "ping"
#
# ping()
# ----
sub ping {
my ($self, $params) = @_;
$self->send_sms($params->{from}, "pong");
}
#
# status()
# ------
# Answer a text with the status of the RaspiSMS & the Zabbix servers
#
sub status {
my ($self, $params) = @_;
my $from = $params->{from};
my $furl = $self->furl();
my @status;
for my $rpi (@raspis) {
my $res = $furl->get("http://$rpi/status.txt");
my ($signal) = $res->content =~ /([0-9]+)%/;
push @status, sprintf "%s $rpi 📶 %d%%",
$res->is_success ? "✔️" : "❌", $signal;
}
for my $zbx (@zabbix) {
my $res = $furl->get("https://$zbx/");
push @status, sprintf "%s $zbx",
$res->is_success ? "✔️" : "❌";
}
$self->send_sms($from, \@status);
}
__PACKAGE__
__END__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment