From: Steven Schubiger Date: Thu, 31 Jan 2013 16:57:42 +0000 (+0100) Subject: lreminder: initial commit X-Git-Url: http://git.refcnt.org/?p=lugs.git;a=commitdiff_plain;h=11bd7a1a30f39afa0a880a99de22d87efb87fb6d;ds=sidebyside lreminder: initial commit --- 11bd7a1a30f39afa0a880a99de22d87efb87fb6d diff --git a/lreminder/reminder.pl b/lreminder/reminder.pl new file mode 100755 index 0000000..aa0bc9e --- /dev/null +++ b/lreminder/reminder.pl @@ -0,0 +1,271 @@ +#!/usr/bin/perl + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# Author: Steven Schubiger +# Last modified: Thu Jan 31 11:12:22 CET 2013 + +use strict; +use warnings; +use lib qw(lib); +use constant true => 1; +use constant false => 0; + +use DBI (); +use File::Basename (); +use File::Spec (); +use FindBin qw($Bin); +use Getopt::Long qw(:config no_auto_abbrev no_ignore_case); +use Hook::Output::File (); +use LUGS::Events::Parser (); +use Mail::Sendmail qw(sendmail); +use Text::Wrap::Smart::XS qw(fuzzy_wrap); +use WWW::Mechanize (); + +my $VERSION = '0.41'; + +#----------------------- +# Start of configuration +#----------------------- + +my $Config = { + events_url => 'http://www.lugs.ch/lugs/termine/termine.txt', + form_url => 'http://lists.lugs.ch/reminder.cgi', + mail_from => 'reminder@lugs.ch', + dbase_name => '', + dbase_user => '', + dbase_pass => '', +}; + +#--------------------- +# End of configuration +#--------------------- + +my $dbh = DBI->connect("dbi:mysql(RaiseError=>1):$Config->{dbase_name}", $Config->{dbase_user}, $Config->{dbase_pass}); +my $file = File::Basename::basename($Config->{events_url}); + +my ($test, $run) = (false, false); + +{ + getopts(\$test, \$run); + my $hook = Hook::Output::File->redirect( + stdout => File::Spec->catfile($Bin, 'stdout.out'), + stderr => File::Spec->catfile($Bin, 'stderr.out'), + ); + fetch_and_write_events(); + process_events(); +} + +sub getopts +{ + my ($test, $run) = @_; + + GetOptions(test => $test, run => $run) or exit; + + if (not $$test || $$run) { + die "$0: neither --test nor --run specified, exiting\n"; + } + elsif ($$test && $$run) { + die "$0: both --test and --run specified, exiting\n"; + } + return; # --test or --run specified +} + +sub fetch_and_write_events +{ + my $mech = WWW::Mechanize->new; + my $http = $mech->get($Config->{events_url}); + + open(my $fh, '>', $file) or die "Cannot open $file: $!\n"; + print {$fh} $http->content; + close($fh); +} + +sub init +{ + my ($parser, $month_days, $current) = @_; + + $$parser = LUGS::Events::Parser->new($file, { + filter_html => true, + tag_handlers => { + 'a href' => [ { + rewrite => '$TEXT - <$HREF>', + fields => [ qw(responsible) ], + }, { + rewrite => '$TEXT - $HREF', + fields => [ qw(location more) ], + } ], + 'br' => [ { + rewrite => '', + fields => [ qw(more) ], + } ], + }, + strip_text => [ 'mailto:' ], + }); + unlink $file; + + %$month_days = ( + 1 => 31, 7 => 31, + 2 => 28, 8 => 31, + 3 => 31, 9 => 30, + 4 => 30, 10 => 31, + 5 => 31, 11 => 30, + 6 => 30, 12 => 31, + ); + + %$current = do { + my @time = (localtime)[3..5]; + $time[1]++; + $time[2] += 1900; + map { $_ => shift @time } qw(day month year); + }; +} + +sub process_events +{ + my ($parser, %month_days, %current); + init(\$parser, \%month_days, \%current); + + while (my $event = $parser->next_event) { + my %event = ( + year => $event->get_event_year, + month => $event->get_event_month, + day => $event->get_event_day, + color => $event->get_event_color, + ); + + my %sth; + + $sth{subscribers} = $dbh->prepare('SELECT mail, mode, notify FROM subscribers'); + $sth{subscribers}->execute; + + while (my $subscriber = $sth{subscribers}->fetchrow_hashref) { + next unless $subscriber->{mode} == 2; + + $sth{subscriptions} = $dbh->prepare('SELECT * FROM subscriptions WHERE mail = ?'); + $sth{subscriptions}->execute($subscriber->{mail}); + + my $subscriptions = $sth{subscriptions}->fetchrow_hashref; + next unless $subscriptions->{$event{color}}; + + my %notify = %current; + + $subscriber->{notify} ||= 0; + + my $day = $current{day} + $subscriber->{notify}; + my $days_in_month = $month_days{$current{month}}; + + if ($day > $days_in_month) { + $notify{day} = $day - $days_in_month; + $notify{month}++; + } + else { + $notify{day} += $subscriber->{notify}; + } + + if ($event{year} == $notify{year} + && $event{month} == $notify{month} + && $event{day} == $notify{day} + ) { + send_mail($event, $subscriber->{mail}); + } + } + } +} + +sub send_mail +{ + my ($event, $mail_subscriber) = @_; + + my $year = $event->get_event_year; + my $month = $event->get_event_month; + my $simple_day = $event->get_event_simple_day; + my $wday = $event->get_event_weekday; + my $time = $event->get_event_time; + my $title = $event->get_event_title; + my $color = $event->get_event_color; + my $location = $event->get_event_location; + my $responsible = $event->get_event_responsible; + my $more = $event->get_event_more || ''; + + wrap_text(\$more); + chomp $more; + wrap_text(\$location); + + my $i; + my %month_names = map { sprintf('%02d', ++$i) => $_ } + qw(Januar Februar Maerz April Mai Juni Juli August + September Oktober November Dezember); + + my $month_name = $month_names{$month}; + +my $message = (<{form_url} + +${\info_string()} +MSG + + if ($run) { + sendmail( + From => $Config->{mail_from}, + To => $mail_subscriber, + Subject => "LUGS Reminder - $title", + Message => $message, + ) or die $Mail::Sendmail::error; + } + elsif ($test) { + printf "[%s] <$mail_subscriber> ($color)\n", scalar localtime; + } +} + +sub wrap_text +{ + my ($text) = @_; + + return unless length $$text; + + my @chunks = fuzzy_wrap($$text, 70); + + my $wrapped; + foreach my $chunk (@chunks) { + $wrapped .= ' ' x (defined $wrapped ? 8 : 0); + $wrapped .= "$chunk\n"; + } + chomp $wrapped; + + $$text = $wrapped; +} + +sub info_string +{ + my $script = File::Basename::basename($0); + my $modified = localtime((stat($0))[9]); + + $modified =~ s/(?<=\b) (?:\d{2}\:?){3} (?=\b)//x; + $modified =~ s/\s+/ /g; + + my $info = <