#!/usr/bin/perl # # /usr/local/sbin/localmarkdown2sms # Copyright 2009 Jonas Smedegaard # # Send series of messages through Kannel from simplified Markdown files # * Lines starting with "#" are "keywords" activating a message series # * write only a single word # * use each keyword only once across the whole system # * use only minuscles (not majuscles, i.e. CAPITAL LETTERS) # * Lines starting with "##" express pauses # * a pause is a number + a single letter, without spaces between # * a pause line can contain multiple pauses, separated by space # Suggestion for writing style: # # * Write explicitly how to activate next series # * pick keywords tied to nex series rather than the previous # * use same instruction jargon across all series in the system use strict; use warnings; use Env qw[$DEBUG $DUMMY $NOSLEEP]; use File::Spec; use File::Slurp; use Time::Duration::Parse; #use Proc::Daemon; use Proc::Fork; #use IO::Pipe; #Proc::Daemon::Init; my (%delay, %reply); my ($path) = shift @ARGV; foreach my $file (read_dir( $path )) { my ($key, $i); # suppress repeated warnings for same issue my ($warn_nonkey_delay, $warn_nonkey_content); next unless ($file =~ /\.mdwn$/); foreach my $line (read_file( File::Spec->catfile($path, $file))) { chomp $line; my $content; # headline if ($line =~ /^(#+)\s*(.*?)\s*$/) { # tidy latest reply (TODO: use sub) if (defined($key) and defined($reply{$key}[$i])) { $reply{$key}[$i] =~ s/^\s*(\w.*?)\s*$/$1/s || delete $reply{$key}[$i]; } my $level = length($1); $content = $2; # key if ($level == 1 and $content =~ /(\w+)/) { $key = lc($1); $i = 0; if (lc($content) ne $key) { warn( "key \"$key\" extracted from fuzzy string \"$content\" in file \"$file\"" ); } if (!defined( $delay{$key})) { $delay{$key}[0] = 0; $warn_nonkey_delay = undef; $warn_nonkey_content = undef; } else { warn( "skipping non-unique key \"$key\" in file \"$file\"" ); $key = undef; } # delay } elsif ($level == 2 and $content =~ /(\d+[sm](\s+|\Z))+/) { if (defined( $key)) { my $delay = parse_duration($1); if (defined($reply{$key}[$i])) { $i++; $delay{$key}[$i] = $delay{$key}[$i - 1]; } $delay{$key}[$i] += $delay; if ($content ne $1) { warn( "delay (${delay}s) resolved from fuzzy string \"$content\" in file \"$file\"" ); } } else { warn( "ignoring non-key'ed delay line \"$1\" in file \"$file\"" ) unless ($warn_nonkey_delay); $warn_nonkey_delay = 1; } } else { warn( "ignoring non-parsable headline \"$line\" in file \"$file\""); } # reply } elsif ( defined( $key )) { $content = $reply{$key}[$i] if (defined($reply{$key}[$i])); $content .= $line . "\n"; $content =~ s/^\h*$//g; # clean virtually empty lines $content =~ s/(\S)\h$/$1/g; # strip single trailing space $content =~ s/\n\n+/\n\n/g; # strip excess newlines $content =~ s/(\S)\n([^\n])/$1 $2/g; # convert newline to space $content =~ s/\h*$//g; # strip all trailing spaces $reply{$key}[$i] = $content; } else { warn( "skipping non-key'ed content \"$line\" in file \"$file\"" ) unless ($warn_nonkey_content); $warn_nonkey_content = 1; } } # tidy latest reply (TODO: use sub) if (defined($key) and defined($reply{$key}[$i])) { $reply{$key}[$i] =~ s/^\s*(\w.*?)\s*$/$1/s || delete $reply{$key}[$i]; } } my ($phone) = shift @ARGV; my ($key) = shift @ARGV; my $num_children = $#{ $reply{$key} } + 1; # How many children we'll create $SIG{CHLD} = 'IGNORE'; # Don't worry about reaping zombies # Spawn off some children if ($DEBUG) { print STDERR "queueing $num_children replies:"; for my $num ( 0 .. $num_children - 1 ) { print STDERR " [" . $delay{$key}[$num] . "s]"; } print STDERR "\n"; } for my $num ( 0 .. $num_children - 1 ) { run_fork { child { sleep($delay{$key}[$num]) unless ($NOSLEEP); unless ($DUMMY) { system {'/usr/share/kannel/contrib/sendsms' } $phone, $reply{$key}[$num]; print STDERR "Done reply #$num [" . $delay{$key}[$num] . "s]\n" if ($DEBUG); } else { print STDERR "\n[" . $delay{$key}[$num] . "s]\n"; print STDERR $reply{$key}[$num] . "\n"; } exit; } parent { if ($DEBUG) { my $child_pid = shift; waitpid $child_pid, 0; } } } } 1;