summaryrefslogtreecommitdiff
path: root/localmarkdown2sms
blob: ac8e6994efa87f3a6fd29b73bc9ad407be300744 (plain)
  1. #!/usr/bin/perl
  2. #
  3. # /usr/local/sbin/localmarkdown2sms
  4. # Copyright 2009 Jonas Smedegaard <dr@jones.dk>
  5. #
  6. # Send series of messages through Kannel from simplified Markdown files
  7. # * Lines starting with "#" are "keywords" activating a message series
  8. # * write only a single word
  9. # * use each keyword only once across the whole system
  10. # * use only minuscles (not majuscles, i.e. CAPITAL LETTERS)
  11. # * Lines starting with "##" express pauses
  12. # * a pause is a number + a single letter, without spaces between
  13. # * a pause line can contain multiple pauses, separated by space
  14. # Suggestion for writing style:
  15. #
  16. # * Write explicitly how to activate next series
  17. # * pick keywords tied to nex series rather than the previous
  18. # * use same instruction jargon across all series in the system
  19. use strict;
  20. use warnings;
  21. use Env qw[$DEBUG $DUMMY $NOSLEEP];
  22. use File::Spec;
  23. use File::Slurp;
  24. use Time::Duration::Parse;
  25. #use Proc::Daemon;
  26. use Proc::Fork;
  27. #use IO::Pipe;
  28. #Proc::Daemon::Init;
  29. my (%file, %delay, %reply);
  30. my ($path) = shift @ARGV;
  31. foreach my $file (read_dir( $path )) {
  32. my ($key, $i, $skipkey, $skipcontent);
  33. # suppress repeated warnings for same issue
  34. my ($warn_nonkey_delay, $warn_nonkey_content);
  35. next unless ($file =~ /\.mdwn$/);
  36. foreach my $line (read_file( File::Spec->catfile($path, $file))) {
  37. chomp $line;
  38. my $content;
  39. # headline
  40. if ($line =~ /^(#+)\s*(.*?)\s*$/) {
  41. # tidy latest reply (TODO: use sub)
  42. if (defined($key) and defined($reply{$key}[$i])) {
  43. $reply{$key}[$i] =~ s/^\s*(\w.*?)\s*$/$1/s || delete $reply{$key}[$i];
  44. }
  45. my $level = length($1);
  46. $content = $2;
  47. # key
  48. if ($level == 1 and $content =~ /(\w+)/) {
  49. $key = lc($1);
  50. $i = 0;
  51. $skipkey = undef;
  52. $skipcontent = undef;
  53. if (lc($content) ne $key) {
  54. print STDERR "key \"$key\" extracted from fuzzy string \"$content\" in file \"$file\"\n";
  55. }
  56. if (!defined( $delay{$key})) {
  57. $delay{$key}[0] = 0;
  58. $warn_nonkey_delay = undef;
  59. $warn_nonkey_content = undef;
  60. } else {
  61. print STDERR "skipping non-unique key \"$key\" in file \"$file\"\n";
  62. $key = undef;
  63. $skipkey = 1;
  64. $skipcontent = 1;
  65. }
  66. # delay
  67. } elsif ($level == 2 and $content =~ /((\d+[sm](\s+|\Z))+)/) {
  68. $skipcontent = undef;
  69. if (defined( $key)) {
  70. my $delay = parse_duration($1);
  71. if (defined($reply{$key}[$i])) {
  72. $i++;
  73. $delay{$key}[$i] = $delay{$key}[$i - 1];
  74. }
  75. $delay{$key}[$i] += $delay;
  76. if ($content ne $1) {
  77. print STDERR "delay (${delay}s) resolved from fuzzy string \"$content\" in file \"$file\"\n";
  78. }
  79. } elsif ($skipkey) {
  80. # skipping - already warned about it...
  81. } else {
  82. print STDERR "ignoring non-key'ed delay line \"$1\" in file \"$file\"\n" unless ($warn_nonkey_delay);
  83. $warn_nonkey_delay = 1;
  84. $skipcontent = 1;
  85. }
  86. } else {
  87. print STDERR "ignoring non-parsable headline \"$line\" in file \"$file\"\n";
  88. $skipcontent = 1;
  89. }
  90. # reply
  91. } else {
  92. $content = $line . "\n";
  93. # ikiwiki directives - strip from content and parse for tags
  94. $content =~ s/(?<!\\)\[\[([^\[\]]*)(?<!\\)\]\]//gs and do {
  95. my $directive_string = $1;
  96. my ($directive, $directive_content);
  97. $directive_string =~ /^\s*\!(tag|taglink)\s*((\s*?\b\w+)+)/ and $file{$file}{'directive'}{'tag'} = [ split /\s+/, $2 ];
  98. };
  99. if ( defined( $key ) and not ($skipcontent)) {
  100. $content = $reply{$key}[$i] . $content if (defined($reply{$key}[$i]));
  101. $content =~ s/^\h*$//g; # clean virtually empty lines
  102. $content =~ s/(\S)\h$/$1/g; # strip single trailing space
  103. $content =~ s/\n\n+/\n\n/g; # strip excess newlines
  104. $content =~ s/(\S)\n([^\n])/$1 $2/g; # convert newline to space
  105. $content =~ s/\h*$//g; # strip all trailing spaces
  106. $reply{$key}[$i] = $content;
  107. } elsif ($skipkey or $skipcontent) {
  108. # skipping - already warned about it...
  109. } else {
  110. print STDERR "skipping non-key'ed content \"$line\" in file \"$file\"\n" unless ($warn_nonkey_content);
  111. $warn_nonkey_content = 1;
  112. }
  113. }
  114. }
  115. # tidy latest reply (TODO: use sub)
  116. if (defined($key) and defined($reply{$key}[$i])) {
  117. $reply{$key}[$i] =~ s/^\s*(\w.*?)\s*$/$1/s || delete $reply{$key}[$i];
  118. }
  119. }
  120. sub sendmsg {
  121. my ($phone, $desc, $msg) = @_;
  122. unless ($DUMMY) {
  123. print STDERR "Exec'ing \"...sendsms $phone ...\"\n" if ($DEBUG);
  124. system {'/usr/share/kannel/contrib/sendsms' } $phone, $msg;
  125. system {'/usr/share/kannel/contrib/sendsms' } $phone, $msg;
  126. print STDERR "Done $desc\n" if ($DEBUG);
  127. } else {
  128. print STDERR "\n --> $phone: $desc\n";
  129. print STDERR $msg . "\n";
  130. }
  131. }
  132. my ($phone) = shift @ARGV;
  133. my ($key) = lc (shift @ARGV);
  134. my $num_children = $#{ $reply{$key} } + 1; # How many children we'll create
  135. if (0 == $num_children) {
  136. &sendmsg($phone, "fallback message", "Hmmm, strange, the word \"$key\" is unknown. Perhaps you typed it wrong?\n\nPlease try again.");
  137. exit;
  138. }
  139. if (0 == $num_children) {
  140. }
  141. $SIG{CHLD} = 'IGNORE'; # Don't worry about reaping zombies
  142. # Spawn off some children
  143. if ($DEBUG) {
  144. print STDERR "queueing $num_children replies:";
  145. for my $num ( 0 .. $num_children - 1 ) {
  146. print STDERR " [" . $delay{$key}[$num] . "s]";
  147. }
  148. print STDERR "\n";
  149. }
  150. for my $num ( 0 .. $num_children - 1 ) {
  151. run_fork {
  152. child {
  153. sleep($delay{$key}[$num]) unless ($NOSLEEP);
  154. &sendmsg($phone, "reply #$num [" . $delay{$key}[$num] . "s]", $reply{$key}[$num]);
  155. exit;
  156. }
  157. parent {
  158. if ($DEBUG) {
  159. my $child_pid = shift;
  160. waitpid $child_pid, 0;
  161. }
  162. }
  163. }
  164. }
  165. 1;