/[irc-logger]/trunk/bin/irc-logger.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/bin/irc-logger.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/irc-logger.pl revision 5 by dpavlin, Mon Feb 27 12:10:07 2006 UTC trunk/bin/irc-logger.pl revision 99 by dpavlin, Fri Mar 7 17:13:30 2008 UTC
# Line 10  irc-logger.pl Line 10  irc-logger.pl
10    
11  ./irc-logger.pl  ./irc-logger.pl
12    
13    =head2 Options
14    
15    =over 4
16    
17    =item --import-dircproxy=filename
18    
19    Import log from C<dircproxy> to C<irc-logger> database
20    
21    =item --log=irc-logger.log
22    
23    =back
24    
25  =head1 DESCRIPTION  =head1 DESCRIPTION
26    
27  log all conversation on irc channel  log all conversation on irc channel
# Line 18  log all conversation on irc channel Line 30  log all conversation on irc channel
30    
31  ## CONFIG  ## CONFIG
32    
33    my $HOSTNAME = `hostname -f`;
34    chomp($HOSTNAME);
35    
36  my $NICK = 'irc-logger';  my $NICK = 'irc-logger';
37    $NICK .= '-dev' if ($HOSTNAME =~ m/llin/);
38  my $CONNECT =  my $CONNECT =
39    {Server => 'irc.freenode.net',    {Server => 'irc.freenode.net',
40     Nick => $NICK,     Nick => $NICK,
41     Ircname => 'logger: ask dpavlin@rot13.org'     Ircname => "try /msg $NICK help",
42    };    };
43  my $CHANNEL = '#razmjenavjestina';  my $CHANNEL = '#razmjenavjestina';
44    $CHANNEL = '#irc-logger' if ($HOSTNAME =~ m/llin/);
45  my $IRC_ALIAS = "log";  my $IRC_ALIAS = "log";
46    
47  my %FOLLOWS =  my $DSN = 'DBI:Pg:dbname=' . $NICK;
   (  
    ACCESS => "/var/log/apache/access.log",  
    ERROR => "/var/log/apache/error.log",  
   );  
48    
49  my $DSN = 'DBI:Pg:dbname=irc-logger';  my $TIMESTAMP = '%Y-%m-%d %H:%M:%S';
50    
51  ## END CONFIG  my $sleep_on_error = 5;
52    
53    # number of last tags to keep in circular buffer
54    my $last_x_tags = 50;
55    
56    # don't pull rss feeds more often than this
57    my $rss_min_delay = 60;
58    
59    my $http_port = $NICK =~ m/-dev/ ? 8001 : 8000;
60    
61    my $url = "http://$HOSTNAME:$http_port";
62    
63  use POE qw(Component::IRC Wheel::FollowTail);  ## END CONFIG
64    
65    use POE qw(Component::IRC Component::Server::HTTP);
66    use HTTP::Status;
67  use DBI;  use DBI;
68    use Regexp::Common qw /URI/;
69    use CGI::Simple;
70    use HTML::TagCloud;
71    use POSIX qw/strftime/;
72    use HTML::CalendarMonthSimple;
73    use Getopt::Long;
74    use DateTime;
75    use URI::Escape;
76    use Data::Dump qw/dump/;
77    use DateTime::Format::ISO8601;
78    use Carp qw/confess/;
79    use XML::Feed;
80    use DateTime::Format::Flexible;
81    
82    my $use_twitter = 1;
83    eval { require Net::Twitter; };
84    $use_twitter = 0 if ($@);
85    
86    my $import_dircproxy;
87    my $log_path;
88    GetOptions(
89            'import-dircproxy:s' => \$import_dircproxy,
90            'log:s' => \$log_path,
91    );
92    
93    #$SIG{__DIE__} = sub {
94    #       confess "fatal error";
95    #};
96    
97    open(STDOUT, '>', $log_path) || warn "can't redirect log to $log_path: $!";
98    
99    sub _log {
100            print strftime($TIMESTAMP,localtime()) . ' ' . join(" ",@_) . $/;
101    }
102    
103    # HTML formatters
104    
105    my %escape = ('<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', '"'=>'&quot;');
106    my $escape_re  = join '|' => keys %escape;
107    
108    my $tag_regex = '\b([\w-_]+)//';
109    
110    my %nick_enumerator;
111    my $max_color = 0;
112    
113    my $filter = {
114            message => sub {
115                    my $m = shift || return;
116    
117                    # protect HTML from wiki modifications
118                    sub e {
119                            my $t = shift;
120                            return 'uri_unescape{' . uri_escape($t, '^a-zA-Z0-9') . '}';
121                    }
122    
123                    $m =~ s/($escape_re)/$escape{$1}/gs;
124                    $m =~ s#($RE{URI}{HTTP})#e(qq{<a href="$1">$1</a>})#egs;
125                    $m =~ s#\/(\w+)\/#<i>$1</i>#gs;
126                    $m =~ s#$tag_regex#e(qq{<a href="$url?tag=$1" class="tag">$1</a>})#egs;
127                    $m =~ s#\*(\w+)\*#<b>$1</b>#gs;
128                    $m =~ s#_(\w+)_#<u>$1</u>#gs;
129    
130                    $m =~ s#uri_unescape{([^}]+)}#uri_unescape($1)#egs;
131                    return $m;
132            },
133            nick => sub {
134                    my $n = shift || return;
135                    if (! $nick_enumerator{$n})  {
136                            my $max = scalar keys %nick_enumerator;
137                            $nick_enumerator{$n} = $max + 1;
138                    }
139                    return '<span class="nick col-' .
140                            ( $nick_enumerator{$n} % $max_color ) .
141                            '">' . $n . '</span>';
142            },
143    };
144    
145  my $dbh = DBI->connect($DSN,"","", { RaiseError => 1, AutoCommit => 1 }) || die $DBI::errstr;  my $dbh = DBI->connect($DSN,"","", { RaiseError => 1, AutoCommit => 1 }) || die $DBI::errstr;
146    $dbh->do( qq{ set client_encoding = 'UTF-8' } );
147    
148  =for SQL schema  my $sql_schema = {
149            log => qq{
 $dbh->do(qq{  
150  create table log (  create table log (
151          id serial,          id serial,
152          time timestamp default now(),          time timestamp default now(),
153          channel text not null,          channel text not null,
154            me boolean default false,
155          nick text not null,          nick text not null,
156          message text not null,          message text not null,
157          primary key(id)          primary key(id)
# Line 60  create table log ( Line 160  create table log (
160  create index log_time on log(time);  create index log_time on log(time);
161  create index log_channel on log(channel);  create index log_channel on log(channel);
162  create index log_nick on log(nick);  create index log_nick on log(nick);
163            },
164            meta => q{
165    create table meta (
166            nick text not null,
167            channel text not null,
168            name text not null,
169            value text,
170            changed timestamp default 'now()',
171            primary key(nick,channel,name)
172    );
173            },
174            feeds => qq{
175    create table feeds (
176            id serial,
177            url text not null,
178            name text,
179            delay interval not null default '5 min',
180            active boolean default true,
181            channel text not null,
182            nick text not null,
183            private boolean default false,
184            last_update timestamp default 'now()',
185            polls int default 0,
186            updates int default 0
187    );
188    create unique index feeds_url on feeds(url);
189    insert into feeds (url,name,channel,nick) values ('http://wiki.razmjenavjestina.org/feed/workspace/razmjenavjestina?category=Recent%20Changes','wiki','$CHANNEL','dpavlin');
190            },
191    };
192    
193  });  foreach my $table ( keys %$sql_schema ) {
194    
195            eval {
196                    $dbh->do(qq{ select count(*) from $table });
197            };
198    
199            if ($@) {
200                    warn "creating database table $table in $DSN\n";
201                    $dbh->do( $sql_schema->{ $table } );
202            }
203    }
204    
205    
206    =head2 meta
207    
208    Set or get some meta data into database
209    
210            meta('nick','channel','var_name', $var_value );
211    
212            $var_value = meta('nick','channel','var_name');
213            ( $var_value, $changed ) = meta('nick','channel','var_name');
214    
215  =cut  =cut
216    
217  my $sth = $dbh->prepare(qq{  sub meta {
218            my ($nick,$channel,$name,$value) = @_;
219    
220            # normalize channel name
221            $channel =~ s/^#//;
222    
223            if (defined($value)) {
224    
225                    my $sth = $dbh->prepare(qq{ update meta set value = ?, changed = now() where nick = ? and channel = ? and name = ? });
226    
227                    eval { $sth->execute( $value, $nick, $channel, $name ) };
228    
229                    # error or no result
230                    if ( $@ || ! $sth->rows ) {
231                            $sth = $dbh->prepare(qq{ insert into meta (value,nick,channel,name,changed) values (?,?,?,?,now()) });
232                            $sth->execute( $value, $nick, $channel, $name );
233                            warn "## created $nick/$channel/$name = $value\n";
234                    } else {
235                            warn "## updated $nick/$channel/$name = $value\n";
236                    }
237    
238                    return $value;
239    
240            } else {
241    
242                    my $sth = $dbh->prepare(qq{ select value,changed from meta where nick = ? and channel = ? and name = ? });
243                    $sth->execute( $nick, $channel, $name );
244                    my ($v,$c) = $sth->fetchrow_array;
245                    warn "## fetched $nick/$channel/$name = $v [$c]\n";
246                    return ($v,$c) if wantarray;
247                    return $v;
248    
249            }
250    }
251    
252    
253    
254    my $sth_insert_log = $dbh->prepare(qq{
255  insert into log  insert into log
256          (channel, nick, message)          (channel, me, nick, message, time)
257  values (?,?,?)  values (?,?,?,?,?)
258  });  });
259    
260    
261  my $SKIPPING = 0;               # if skipping, how many we've done  my $tags;
262  my $SEND_QUEUE;                 # cache  
263    =head2 get_from_log
264    
265     my @messages = get_from_log(
266            limit => 42,
267            search => '%what to stuff in ilike%',
268            fmt => {
269                    time => '{%s} ',
270                    time_channel => '{%s %s} ',
271                    nick => '%s: ',
272                    me_nick => '***%s ',
273                    message => '%s',
274            },
275            filter => {
276                    message => sub {
277                            # modify message content
278                            return shift;
279                    }
280            },
281            context => 5,
282            full_rows => 1,
283     );
284    
285    Order is important. Fields are first passed through C<filter> (if available) and
286    then throgh C<< sprintf($fmt->{message}, $message >> if available.
287    
288    C<context> defines number of messages around each search hit for display.
289    
290    C<full_rows> will return database rows for each result with C<date>, C<time>, C<channel>,
291    C<me>, C<nick> and C<message> keys.
292    
293    =cut
294    
295    sub get_from_log {
296            my $args = {@_};
297    
298            if ( ! $args->{fmt} ) {
299                    $args->{fmt} = {
300                            date => '[%s] ',
301                            time => '{%s} ',
302                            time_channel => '{%s %s} ',
303                            nick => '%s: ',
304                            me_nick => '***%s ',
305                            message => '%s',
306                    };
307            }
308    
309            my $sql_message = qq{
310                    select
311                            time::date as date,
312                            time::time as time,
313                            channel,
314                            me,
315                            nick,
316                            message
317                    from log
318            };
319    
320            my $sql_context = qq{
321                    select
322                            id
323                    from log
324            };
325    
326            my $context = $1 if ($args->{search} && $args->{search} =~ s/\s*\+(\d+)\s*/ /);
327    
328            my $sql = $context ? $sql_context : $sql_message;
329    
330            sub check_date {
331                    my $date = shift || return;
332                    my $new_date = eval { DateTime::Format::ISO8601->parse_datetime( $date )->ymd; };
333                    if ( $@ ) {
334                            warn "invalid date $date\n";
335                            $new_date = DateTime->now->ymd;
336                    }
337                    return $new_date;
338            }
339    
340            my @where;
341            my @args;
342    
343            if (my $search = $args->{search}) {
344                    $search =~ s/^\s+//;
345                    $search =~ s/\s+$//;
346                    push @where, 'message ilike ? or nick ilike ?';
347                    push @args, ( ( '%' . $search . '%' ) x 2 );
348                    _log "search for '$search'";
349            }
350    
351            if ($args->{tag} && $tags->{ $args->{tag} }) {
352                    push @where, 'id in (' . join(',', @{ $tags->{ $args->{tag} } }) . ')';
353                    _log "search for tags $args->{tag}";
354            }
355    
356            if (my $date = $args->{date} ) {
357                    $date = check_date( $date );
358                    push @where, 'date(time) = ?';
359                    push @args, $date;
360                    _log "search for date $date";
361            }
362    
363            $sql .= " where " . join(" and ", @where) if @where;
364    
365            $sql .= " order by log.time desc";
366            $sql .= " limit " . $args->{limit} if ($args->{limit});
367    
368            #warn "### sql: $sql ", dump( @args );
369    
370            my $sth = $dbh->prepare( $sql );
371            eval { $sth->execute( @args ) };
372            return if $@;
373    
374            my $last_row = {
375                    date => '',
376                    time => '',
377                    channel => '',
378                    nick => '',
379            };
380    
381            my @rows;
382    
383            while (my $row = $sth->fetchrow_hashref) {
384                    unshift @rows, $row;
385            }
386    
387            # normalize nick names
388            map {
389                    $_->{nick} =~ s/^_*(.*?)_*$/$1/
390            } @rows;
391    
392            return @rows if ($args->{full_rows});
393    
394            my @msgs = (
395                    "Showing " . ($#rows + 1) . " messages..."
396            );
397    
398            if ($context) {
399                    my @ids = @rows;
400                    @rows = ();
401    
402                    my $last_to = 0;
403    
404                    my $sth = $dbh->prepare( $sql_message . qq{ where id >= ? and id < ? } );
405                    foreach my $row_id (sort { $a->{id} <=> $b->{id} } @ids) {
406                            my $id = $row_id->{id} || die "can't find id in row";
407            
408                            my ($from, $to) = ($id - $context, $id + $context);
409                            $from = $last_to if ($from < $last_to);
410                            $last_to = $to;
411                            $sth->execute( $from, $to );
412    
413                            #warn "## id: $id from: $from to: $to returned: ", $sth->rows, "\n";
414    
415                            while (my $row = $sth->fetchrow_hashref) {
416                                    push @rows, $row;
417                            }
418    
419                    }
420            }
421    
422            # sprintf which can take coderef as first parametar
423            sub cr_sprintf {
424                    my $fmt = shift || return;
425                    if (ref($fmt) eq 'CODE') {
426                            $fmt->(@_);
427                    } else {
428                            sprintf($fmt, @_);
429                    }
430            }
431    
432            foreach my $row (@rows) {
433    
434                    $row->{time} =~ s#\.\d+##;
435    
436                    my $msg = '';
437    
438                    $msg = cr_sprintf($args->{fmt}->{date}, $row->{date}) . ' ' if ($last_row->{date} ne $row->{date});
439                    my $t = $row->{time};
440    
441                    if ($last_row->{channel} ne $row->{channel}) {
442                            $msg .= cr_sprintf($args->{fmt}->{time_channel}, $t, $row->{channel});
443                    } else {
444                            $msg .= cr_sprintf($args->{fmt}->{time}, $t);
445                    }
446    
447                    my $append = 1;
448    
449                    my $nick = $row->{nick};
450    #               if ($nick =~ s/^_*(.*?)_*$/$1/) {
451    #                       $row->{nick} = $nick;
452    #               }
453    
454                    if ($last_row->{nick} ne $nick) {
455                            # obfu way to find format for me_nick if needed or fallback to default
456                            my $fmt = $row->{me} ? ( $args->{fmt}->{me_nick} || $args->{fmt}->{nick} ) : $args->{fmt}->{nick};
457                            $fmt ||= '%s';
458    
459                            $nick = $args->{filter}->{nick}->($nick) if (ref($args->{filter}->{nick}) eq 'CODE');
460    
461                            $msg .= cr_sprintf( $fmt, $nick );
462                            $append = 0;
463                    }
464    
465                    $args->{fmt}->{message} ||= '%s';
466                    if (ref($args->{filter}->{message}) eq 'CODE') {
467                            $msg .= cr_sprintf($args->{fmt}->{message},
468                                    $args->{filter}->{message}->(
469                                            $row->{message}
470                                    )
471                            );
472                    } else {
473                            $msg .= cr_sprintf($args->{fmt}->{message}, $row->{message});
474                    }
475    
476                    if ($append && @msgs) {
477                            $msgs[$#msgs] .= " " . $msg;
478                    } else {
479                            push @msgs, $msg;
480                    }
481    
482                    $last_row = $row;
483            }
484    
485            return @msgs;
486    }
487    
488    # tags support
489    
490    my $cloud = HTML::TagCloud->new;
491    
492    =head2 add_tag
493    
494     add_tag( id => 42, message => 'irc message', nick => 'foobar' [, me => 1 ] );
495    
496    =cut
497    
498    my @last_tags;
499    
500    sub add_tag {
501            my $arg = {@_};
502    
503            return unless ($arg->{id} && $arg->{message});
504    
505            my $m = $arg->{message};
506    
507            my @tags;
508    
509            while ($m =~ s#$tag_regex##s) {
510                    my $tag = $1;
511                    next if (! $tag || $tag =~ m/https?:/i);
512                    push @{ $tags->{$tag} }, $arg->{id};
513                    #warn "+tag $tag: $arg->{id}\n";
514                    $cloud->add($tag, "$url?tag=$tag", scalar @{$tags->{$tag}} + 1);
515                    push @tags, $tag;
516    
517            }
518    
519            if ( @tags ) {
520                    pop @last_tags if $#last_tags == $last_x_tags;
521                    unshift @last_tags, { tags => [ @tags ], %$arg };
522            }
523    
524    }
525    
526    =head2 seed_tags
527    
528    Read all tags from database and create in-memory cache for tags
529    
530    =cut
531    
532    sub seed_tags {
533            my $sth = $dbh->prepare(qq{ select id,message,nick,me,time from log where message like '%//%' order by time asc });
534            $sth->execute;
535            while (my $row = $sth->fetchrow_hashref) {
536                    add_tag( %$row );
537            }
538    
539            foreach my $tag (keys %$tags) {
540                    $cloud->add($tag, "$url?tag=$tag", scalar @{$tags->{$tag}} + 1);
541            }
542    }
543    
544    seed_tags;
545    
546    
547    =head2 save_message
548    
549      save_message(
550            channel => '#foobar',
551            me => 0,
552            nick => 'dpavlin',
553            message => 'test message',
554            time => '2006-06-25 18:57:18',
555      );
556    
557    C<time> is optional, it will use C<< now() >> if it's not available.
558    
559    C<me> if not specified will be C<0> (not C</me> message)
560    
561    =cut
562    
563    sub save_message {
564            my $a = {@_};
565            confess "have msg" if $a->{msg};
566            $a->{me} ||= 0;
567            $a->{time} ||= strftime($TIMESTAMP,localtime());
568    
569            _log
570                    $a->{channel}, " ",
571                    $a->{me} ? "***" . $a->{nick} : "<" . $a->{nick} . ">",
572                    " " . $a->{message};
573    
574            $sth_insert_log->execute($a->{channel}, $a->{me}, $a->{nick}, $a->{message}, $a->{time});
575            add_tag( id => $dbh->last_insert_id(undef,undef,"log",undef), %$a );
576    }
577    
578    
579    if ($import_dircproxy) {
580            open(my $l, $import_dircproxy) || die "can't open $import_dircproxy: $!";
581            warn "importing $import_dircproxy...\n";
582            my $tz_offset = 1 * 60 * 60;    # TZ GMT+2
583            while(<$l>) {
584                    chomp;
585                    if (/^@(\d+)\s(\S+)\s(.+)$/) {
586                            my ($time, $nick, $msg) = ($1,$2,$3);
587    
588                            my $dt = DateTime->from_epoch( epoch => $time + $tz_offset );
589    
590                            my $me = 0;
591                            $me = 1 if ($nick =~ m/^\[\S+]/);
592                            $nick =~ s/^[\[<]([^!]+).*$/$1/;
593    
594                            $msg =~ s/^ACTION\s+// if ($me);
595    
596                            save_message(
597                                    channel => $CHANNEL,
598                                    me => $me,
599                                    nick => $nick,
600                                    message => $msg,
601                                    time => $dt->ymd . " " . $dt->hms,
602                            ) if ($nick !~ m/^-/);
603    
604                    } else {
605                            _log "can't parse: $_";
606                    }
607            }
608            close($l);
609            warn "import over\n";
610            exit;
611    }
612    
613    #
614    # RSS follow
615    #
616    
617    my $_rss;
618    
619    
620    sub rss_fetch {
621            my ($args) = @_;
622    
623            # how many messages to send out when feed is seen for the first time?
624            my $send_rss_msgs = 1;
625    
626            _log "RSS fetch", $args->{url};
627    
628            my $feed = XML::Feed->parse(URI->new( $args->{url} ));
629            if ( ! $feed ) {
630                    _log("can't fetch RSS ", $args->{url});
631                    return;
632            }
633    
634            my ( $total, $updates ) = ( 0, 0 );
635            for my $entry ($feed->entries) {
636                    $total++;
637    
638                    # seen allready?
639                    next if $_rss->{$args->{channel}}->{$feed->link}->{$entry->id}++ > 0;
640    
641                    sub prefix {
642                            my ($txt,$var) = @_;
643                            $var =~ s/\s+/ /gs;
644                            $var =~ s/^\s+//g;
645                            $var =~ s/\s+$//g;
646                            return $txt . $var if $var;
647                    }
648    
649                    # fix absolute and relative links to feed entries
650                    my $link = $entry->link;
651                    if ( $link =~ m!^/! ) {
652                            my $host = $args->{url};
653                            $host =~ s!^(http://[^/]+).*$!$1!;      #!vim
654                            $link = "$host/$link";
655                    } elsif ( $link !~ m!^http! ) {
656                            $link = $args->{url} . $link;
657                    }
658                    $link =~ s!//+!/!g;
659    
660                    my $msg;
661                    $msg .= prefix( 'From: ' , $args->{name} || $feed->title );
662                    $msg .= prefix( ' by ' , $entry->author );
663                    $msg .= prefix( ' | ' , $entry->title );
664                    $msg .= prefix( ' | ' , $link );
665    #               $msg .= prefix( ' id ' , $entry->id );
666    
667                    if ( $args->{kernel} && $send_rss_msgs ) {
668                            $send_rss_msgs--;
669                            $sth_insert_log->execute( $args->{channel}, 1, $NICK, $msg, 'now()' );
670                            my ( $type, $to ) = ( 'notice', $args->{channel} );
671                            ( $type, $to ) = ( 'privmsg', $args->{nick} ) if $args->{private};
672                            _log(">> $type $to |", $msg);
673                            $args->{kernel}->post( $IRC_ALIAS => $type => $to, $msg );
674                            $updates++;
675                    }
676            }
677    
678            my $sql = qq{ update feeds set last_update = now(), polls = polls + 1 };
679            $sql .= qq{, updates = updates + $updates } if $updates;
680            $sql .= qq{where id = } . $args->{id};
681            eval { $dbh->do( $sql ) };
682    
683            _log "RSS got $total items of which $updates new";
684    
685            return $updates;
686    }
687    
688    sub rss_fetch_all {
689            my $kernel = shift;
690            my $sql = qq{
691                    select id, url, name, channel, nick, private
692                    from feeds
693                    where active is true
694            };
695            # limit to newer feeds only if we are not sending messages out
696            $sql .= qq{     and last_update + delay < now() } if $kernel;
697            my $sth = $dbh->prepare( $sql );
698            $sth->execute();
699            warn "# ",$sth->rows," active RSS feeds\n";
700            my $count = 0;
701            while (my $row = $sth->fetchrow_hashref) {
702                    $row->{kernel} = $kernel if $kernel;
703                    $count += rss_fetch( $row );
704            }
705            return "OK, fetched $count posts from " . $sth->rows . " feeds";
706    }
707    
708    
709    sub rss_check_updates {
710            my $kernel = shift;
711            $_rss->{last_poll} ||= time();
712            my $dt = time() - $_rss->{last_poll};
713            warn "## rss_check_updates $dt > $rss_min_delay\n";
714            if ( $dt > $rss_min_delay ) {
715                    $_rss->{last_poll} = time();
716                    _log rss_fetch_all( $kernel );
717            }
718    }
719    
720    # seed rss seen cache so we won't send out all items on startup
721    _log rss_fetch_all;
722    
723    #
724    # POE handing part
725    #
726    
727    my $ping;                                               # ping stats
728    
729  POE::Component::IRC->new($IRC_ALIAS);  POE::Component::IRC->new($IRC_ALIAS);
730    
731  POE::Session->create  POE::Session->create( inline_states => {
732    (inline_states =>          _start => sub {      
733     {_start => sub {                        $_[KERNEL]->post($IRC_ALIAS => register => 'all');
734        $_[KERNEL]->post($IRC_ALIAS => register => 'all');                  $_[KERNEL]->post($IRC_ALIAS => connect => $CONNECT);
       $_[KERNEL]->post($IRC_ALIAS => connect => $CONNECT);  
735      },      },
736      irc_255 => sub {            # server is done blabbing      irc_255 => sub {    # server is done blabbing
737        $_[KERNEL]->post($IRC_ALIAS => join => $CHANNEL);                  $_[KERNEL]->post($IRC_ALIAS => join => $CHANNEL);
738        $_[KERNEL]->post($IRC_ALIAS => join => '#logger');                  $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "IDENTIFY $NICK" );
       $_[KERNEL]->yield("heartbeat"); # start heartbeat  
 #      $_[KERNEL]->yield("my_add", $_) for keys %FOLLOWS;  
739      },      },
740      irc_public => sub {      irc_public => sub {
741            my $kernel = $_[KERNEL];                  my $kernel = $_[KERNEL];
742            my $nick = (split /!/, $_[ARG0])[0];                  my $nick = (split /!/, $_[ARG0])[0];
743            my $channel = $_[ARG1]->[0];                  my $channel = $_[ARG1]->[0];
744            my $msg = $_[ARG2];                  my $msg = $_[ARG2];
745    
746                    save_message( channel => $channel, me => 0, nick => $nick, message => $msg);
747                    meta( $nick, $channel, 'last-msg', $msg );
748                    rss_check_updates( $kernel );
749        },
750        irc_ctcp_action => sub {
751                    my $kernel = $_[KERNEL];
752                    my $nick = (split /!/, $_[ARG0])[0];
753                    my $channel = $_[ARG1]->[0];
754                    my $msg = $_[ARG2];
755    
756                    save_message( channel => $channel, me => 1, nick => $nick, message => $msg);
757    
758                    if ( $use_twitter ) {
759                            if ( my $twitter = meta( $nick, $NICK, 'twitter' ) ) {
760                                    my ($login,$passwd) = split(/\s+/,$twitter,2);
761                                    _log("sending twitter for $nick/$login on $channel ");
762                                    my $bot = Net::Twitter->new( username=>$login, password=>$passwd );
763                                    $bot->update("<${channel}> $msg");
764                            }
765                    }
766    
           print "$channel: <$nick> $msg\n";  
           $sth->execute($channel, $nick, $msg);  
767      },      },
768      (map          irc_ping => sub {
769       {                  _log( "pong ", $_[ARG0] );
770         ;"irc_$_" => sub { }}                  $ping->{ $_[ARG0] }++;
771       qw(join                  rss_check_updates( $_[KERNEL] );
772          ctcp_version          },
773          connected snotice ctcp_action ping notice mode part quit          irc_invite => sub {
774          001 002 003 004 005                  my $kernel = $_[KERNEL];
775          250 251 252 253 254 265 266                  my $nick = (split /!/, $_[ARG0])[0];
776          332 333 353 366 372 375 376                  my $channel = $_[ARG1];
777                  477  
778                  )),                  _log "invited to $channel by $nick";
779    
780                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => $nick, "how nice of you to invite me to $channel, I'll be right there..." );
781                    $_[KERNEL]->post($IRC_ALIAS => join => $channel);
782    
783            },
784            irc_msg => sub {
785                    my $kernel = $_[KERNEL];
786                    my $nick = (split /!/, $_[ARG0])[0];
787                    my $msg = $_[ARG2];
788                    my $channel = $_[ARG1]->[0];
789    
790                    my $res = "unknown command '$msg', try /msg $NICK help!";
791                    my @out;
792    
793                    _log "<< $msg";
794    
795                    if ($msg =~ m/^help/i) {
796    
797                            $res = "usage: /msg $NICK comand | commands: stat - user/message stat | last - show backtrace | grep foobar - find foobar";
798    
799                    } elsif ($msg =~ m/^msg\s+(\S+)\s+(.*)$/i) {
800    
801                            _log ">> /msg $1 $2";
802                            $_[KERNEL]->post( $IRC_ALIAS => privmsg => $1, $2 );
803                            $res = '';
804    
805                    } elsif ($msg =~ m/^stat.*?\s*(\d*)/i) {
806    
807                            my $nr = $1 || 10;
808    
809                            my $sth = $dbh->prepare(qq{
810                                    select
811                                            trim(both '_' from nick) as nick,
812                                            count(*) as count,
813                                            sum(length(message)) as len
814                                    from log
815                                    group by trim(both '_' from nick)
816                                    order by len desc,count desc
817                                    limit $nr
818                            });
819                            $sth->execute();
820                            $res = "Top $nr users: ";
821                            my @users;
822                            while (my $row = $sth->fetchrow_hashref) {
823                                    push @users,$row->{nick} . ': ' . $row->{count} . '/' . $row->{len} . '=' . sprintf("%.2f", $row->{len}/$row->{count});
824                            }
825                            $res .= join(" | ", @users);
826                    } elsif ($msg =~ m/^last.*?\s*(\d*)/i) {
827    
828                            my $limit = $1 || meta( $nick, $channel, 'last-size' ) || 10;
829    
830                            foreach my $res (get_from_log( limit => $limit )) {
831                                    _log "last: $res";
832                                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => $nick, $res );
833                            }
834    
835                            $res = '';
836    
837                    } elsif ($msg =~ m/^(search|grep)\s+(.*)\s*$/i) {
838    
839                            my $what = $2;
840    
841                            foreach my $res (get_from_log(
842                                            limit => 20,
843                                            search => $what,
844                                    )) {
845                                    _log "search [$what]: $res";
846                                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => $nick, $res );
847                            }
848    
849                            $res = '';
850    
851                    } elsif ($msg =~ m/^(?:count|poll)\s+(.*)(?:\s+(\d+))?\s*$/i) {
852    
853                            my ($what,$limit) = ($1,$2);
854                            $limit ||= 100;
855    
856                            my $stat;
857    
858                            foreach my $res (get_from_log(
859                                            limit => $limit,
860                                            search => $what,
861                                            full_rows => 1,
862                                    )) {
863                                    while ($res->{message} =~ s/\Q$what\E(\+|\-)//) {
864                                            $stat->{vote}->{$1}++;
865                                            $stat->{from}->{ $res->{nick} }++;
866                                    }
867                            }
868    
869                            my @nicks;
870                            foreach my $nick (sort { $stat->{from}->{$a} <=> $stat->{from}->{$b} } keys %{ $stat->{from} }) {
871                                    push @nicks, $nick . ( $stat->{from}->{$nick} == 1 ? '' :
872                                            "(" . $stat->{from}->{$nick} . ")"
873                                    );
874                            }
875    
876                            $res =
877                                    "$what ++ " . ( $stat->{vote}->{'+'} || 0 ) .
878                                    " : " . ( $stat->{vote}->{'-'} || 0 ) . " --" .
879                                    " from " . ( join(", ", @nicks) || 'nobody' );
880    
881                            $_[KERNEL]->post( $IRC_ALIAS => notice => $nick, $res );
882    
883                    } elsif ($msg =~ m/^ping/) {
884                            $res = "ping = " . dump( $ping );
885                    } elsif ($msg =~ m/^conf(?:ig)*\s*(last-size|twitter)*\s*(.*)/) {
886                            if ( ! defined( $1 ) ) {
887                                    my $sth = $dbh->prepare(qq{ select name,value,changed from meta where nick = ? and channel = ? });
888                                    $sth->execute( $nick, $channel );
889                                    $res = "config for $nick on $channel";
890                                    while ( my ($n,$v) = $sth->fetchrow_array ) {
891                                            $res .= " | $n = $v";
892                                    }
893                            } elsif ( ! $2 ) {
894                                    my $val = meta( $nick, $channel, $1 );
895                                    $res = "current $1 = " . ( $val ? $val : 'undefined' );
896                            } else {
897                                    my $validate = {
898                                            'last-size' => qr/^\d+/,
899                                            'twitter' => qr/^\w+\s+\w+/,
900                                    };
901    
902                                    my ( $op, $val ) = ( $1, $2 );
903    
904                                    if ( my $regex = $validate->{$op} ) {
905                                            if ( $val =~ $regex ) {
906                                                    meta( $nick, $channel, $op, $val );
907                                                    $res = "saved $op = $val";
908                                            } else {
909                                                    $res = "config option $op = $val doesn't validate against $regex";
910                                            }
911                                    } else {
912                                            $res = "config option $op doesn't exist";
913                                    }
914                            }
915                    } elsif ($msg =~ m/^rss-update/) {
916                            $res = rss_fetch_all( $_[KERNEL] );
917                    } elsif ($msg =~ m/^rss-clean/) {
918                            $_rss = undef;
919                            $dbh->do( qq{ update feeds set last_update = now() - delay } );
920                            $res = "OK, cleaned RSS cache";
921                    } elsif ($msg =~ m/^rss-list/) {
922                            my $sth = $dbh->prepare(qq{ select url,name,last_update,active,channel,nick,private from feeds });
923                            $sth->execute;
924                            while (my @row = $sth->fetchrow_array) {
925                                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => $nick, join(' | ',@row) );
926                            }
927                            $res = '';
928                    } elsif ($msg =~ m!^rss-(add|remove|stop|start)(?:-(private))?\s+(http://\S+)\s*(.*)!) {
929                            my ( $command, $sub, $url, $arg ) = ( $1,$2,$3,$4 );
930    
931                            my $channel = $1 if ( $arg =~ s/\s*(#\S+)\s*// );
932                            $channel = $nick if $sub eq 'private';
933    
934                            my $sql = {
935                                    add             => qq{ insert into feeds (url,name,channel,nick,private) values (?,?,?,?,?) },
936    #                               remove  => qq{ delete from feeds                                where url = ? and name = ? },
937                                    start   => qq{ update feeds set active = true   where url = ? },
938                                    stop    => qq{ update feeds set active = false  where url = ? },
939                            };
940    
941                            if ( $command eq 'add' && ! $channel ) {
942                                    $res = "ERROR: got '$msg' which doesn't have #channel in it, ignoring!";
943                            } elsif (my $q = $sql->{$command} ) {
944                                    my $sth = $dbh->prepare( $q );
945                                    my @data = ( $url );
946                                    if ( $command eq 'add' ) {
947                                            push @data, ( $arg, $channel, $nick, $sub eq 'private' ? 1 : 0 );
948                                    }
949                                    warn "## $command SQL $q with ",dump( @data ),"\n";
950                                    eval { $sth->execute( @data ) };
951                                    if ($@) {
952                                            $res = "ERROR: $@";
953                                    } else {
954                                            $res = "OK, RSS [$command|$sub|$url|$arg]";
955                                    }
956                            } else {
957                                    $res = "ERROR: don't know what to do with: $msg";
958                            }
959                    }
960    
961                    if ($res) {
962                            _log ">> [$nick] $res";
963                            $_[KERNEL]->post( $IRC_ALIAS => privmsg => $nick, $res );
964                    }
965    
966                    rss_check_updates( $_[KERNEL] );
967            },
968            irc_477 => sub {
969                    _log "# irc_477: ",$_[ARG1];
970                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "register $NICK" );
971            },
972            irc_505 => sub {
973                    _log "# irc_505: ",$_[ARG1];
974                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "register $NICK" );
975    #               $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "set hide email on" );
976    #               $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "set email dpavlin\@rot13.org" );
977            },
978            irc_registered => sub {
979                    _log "## registrated $NICK";
980                    $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "IDENTIFY $NICK" );
981            },
982            irc_disconnected => sub {
983                    _log "## disconnected, reconnecting again";
984                    $_[KERNEL]->post($IRC_ALIAS => connect => $CONNECT);
985            },
986            irc_socketerr => sub {
987                    _log "## socket error... sleeping for $sleep_on_error seconds and retry";
988                    sleep($sleep_on_error);
989                    $_[KERNEL]->post($IRC_ALIAS => connect => $CONNECT);
990            },
991    #       irc_433 => sub {
992    #               print "# irc_433: ",$_[ARG1], "\n";
993    #               warn "## indetify $NICK\n";
994    #               $_[KERNEL]->post( $IRC_ALIAS => privmsg => 'nickserv', "IDENTIFY $NICK" );
995    #       },
996      _child => sub {},      _child => sub {},
997      _default => sub {      _default => sub {
998        printf "%s: session %s caught an unhandled %s event.\n",                  _log sprintf "sID:%s %s %s",
999          scalar localtime(), $_[SESSION]->ID, $_[ARG0];                          $_[SESSION]->ID, $_[ARG0],
1000        print "The $_[ARG0] event was given these parameters: ",                          ref($_[ARG1]) eq "ARRAY"        ?       join(",", map { ref($_) eq "ARRAY" ? join(";", @{$_}) : $_ } @{ $_[ARG1] })     :
1001          join(" ", map({"ARRAY" eq ref $_ ? "[@$_]" : "$_"} @{$_[ARG1]})), "\n";                          $_[ARG1]                                        ?       $_[ARG1]                                        :
1002                            "";
1003        0;                        # false for signals        0;                        # false for signals
1004      },      },
     my_add => sub {  
       my $trailing = $_[ARG0];  
       my $session = $_[SESSION];  
       POE::Session->create  
           (inline_states =>  
            {_start => sub {  
               $_[HEAP]->{wheel} =  
                 POE::Wheel::FollowTail->new  
                     (  
                      Filename => $FOLLOWS{$trailing},  
                      InputEvent => 'got_line',  
                     );  
             },  
             got_line => sub {  
               $_[KERNEL]->post($session => my_tailed =>  
                                time, $trailing, $_[ARG0]);  
             },  
            },  
           );  
       
     },  
     my_tailed => sub {  
       my ($time, $file, $line) = @_[ARG0..ARG2];  
       ## $time will be undef on a probe, or a time value if a real line  
   
       ## PoCo::IRC has throttling built in, but no external visibility  
       ## so this is reaching "under the hood"  
       $SEND_QUEUE ||=  
         $_[KERNEL]->alias_resolve($IRC_ALIAS)->get_heap->{send_queue};  
   
       ## handle "no need to keep skipping" transition  
       if ($SKIPPING and @$SEND_QUEUE < 1) {  
         $_[KERNEL]->post($IRC_ALIAS => privmsg => $CHANNEL =>  
                          "[discarded $SKIPPING messages]");  
         $SKIPPING = 0;  
       }  
   
       ## handle potential message display  
       if ($time) {  
         if ($SKIPPING or @$SEND_QUEUE > 3) { # 3 msgs per 10 seconds  
           $SKIPPING++;  
         } else {  
           my @time = localtime $time;  
           $_[KERNEL]->post($IRC_ALIAS => privmsg => $CHANNEL =>  
                            sprintf "%02d:%02d:%02d: %s: %s",  
                            ($time[2] + 11) % 12 + 1, $time[1], $time[0],  
                            $file, $line);  
         }  
       }  
   
       ## handle re-probe/flush if skipping  
       if ($SKIPPING) {  
         $_[KERNEL]->delay($_[STATE] => 0.5); # $time will be undef  
       }  
   
     },  
     my_heartbeat => sub {  
       $_[KERNEL]->yield(my_tailed => time, "heartbeat", "beep");  
       $_[KERNEL]->delay($_[STATE] => 10);  
     }  
1005     },     },
1006    );    );
1007    
1008    # http server
1009    
1010    my $httpd = POE::Component::Server::HTTP->new(
1011            Port => $http_port,
1012            PreHandler => {
1013                    '/' => sub {
1014                            $_[0]->header(Connection => 'close')
1015                    }
1016            },
1017            ContentHandler => { '/' => \&root_handler },
1018            Headers        => { Server => 'irc-logger' },
1019    );
1020    
1021    my $style = <<'_END_OF_STYLE_';
1022    p { margin: 0; padding: 0.1em; }
1023    .time, .channel { color: #808080; font-size: 60%; }
1024    .date { float: right; background: #e0e0e0; color: #404040; font-size: 120%; padding: 0.25em; border: 1px dashed #808080; }
1025    .nick { color: #000000; font-size: 80%; padding: 2px; font-family: courier, courier new, monospace ; }
1026    .message { color: #000000; font-size: 100%; }
1027    .search { float: right; }
1028    a:link.tag, a:visited.tag { border: 1px dashed #ccc; backgound: #ccc; text-decoration: none }
1029    a:hover.tag { border: 1px solid #eee }
1030    hr { border: 1px dashed #ccc; height: 1px; clear: both; }
1031    /*
1032    .col-0 { background: #ffff66 }
1033    .col-1 { background: #a0ffff }
1034    .col-2 { background: #99ff99 }
1035    .col-3 { background: #ff9999 }
1036    .col-4 { background: #ff66ff }
1037    */
1038    .calendar { border: 1px solid red; width: 100%; }
1039    .month { border: 0px; width: 100%; }
1040    _END_OF_STYLE_
1041    
1042    $max_color = 0;
1043    
1044    my @cols = qw(
1045            #ffcccc #ccffe6 #ccccff #e6ccff #ffccff #ffcce6 #ff9999 #ffcc99 #ffff99
1046            #ccff99 #99ff99 #99ffcc #99ccff #9999ff #cc99ff #ff6666 #ffb366 #ffff66
1047            #66ff66 #66ffb3 #66b3ff #6666ff #ff3333 #33ff33 #3399ff #3333ff #ff3399
1048            #a0a0a0 #ff0000 #ffff00 #80ff00 #0000ff #8000ff #ff00ff #ff0080 #994d00
1049            #999900 #009900 #cc0066 #c0c0c0 #ccff99 #99ff33 #808080 #660033 #ffffff
1050    );
1051    
1052    foreach my $c (@cols) {
1053            $style .= ".col-${max_color} { background: $c }\n";
1054            $max_color++;
1055    }
1056    warn "defined $max_color colors for users...\n";
1057    
1058    sub root_handler {
1059            my ($request, $response) = @_;
1060            $response->code(RC_OK);
1061    
1062            # this doesn't seem to work, so moved to PreHandler
1063            #$response->header(Connection => 'close');
1064    
1065            return RC_OK if $request->uri =~ m/favicon.ico$/;
1066    
1067            my $q;
1068    
1069            if ( $request->method eq 'POST' ) {
1070                    $q = new CGI::Simple( $request->content );
1071            } elsif ( $request->uri =~ /\?(.+)$/ ) {
1072                    $q = new CGI::Simple( $1 );
1073            } else {
1074                    $q = new CGI::Simple;
1075            }
1076    
1077            my $search = $q->param('search') || $q->param('grep') || '';
1078    
1079            if ($request->url =~ m#/rss(?:/(tags|last-tag|follow.*)\w*(?:=(\d+))?)?#i) {
1080                    my $show = lc($1);
1081                    my $nr = $2;
1082    
1083                    my $type = 'RSS';       # Atom
1084    
1085                    $response->content_type( 'application/' . lc($type) . '+xml' );
1086    
1087                    my $html = '<!-- error -->';
1088                    #warn "create $type feed from ",dump( @last_tags );
1089    
1090                    my $feed = XML::Feed->new( $type );
1091                    $feed->link( $url );
1092    
1093                    if ( $show eq 'tags' ) {
1094                            $nr ||= 50;
1095                            $feed->title( "tags from $CHANNEL" );
1096                            $feed->link( "$url/tags" );
1097                            $feed->description( "tag cloud created from messages on channel $CHANNEL which have tags// in them" );
1098                            my $feed_entry = XML::Feed::Entry->new($type);
1099                            $feed_entry->title( "$nr tags from $CHANNEL" );
1100                            $feed_entry->author( $NICK );
1101                            $feed_entry->link( '/#tags'  );
1102    
1103                            $feed_entry->content(
1104                                    qq{<![CDATA[<style type="text/css">}
1105                                    . $cloud->css
1106                                    . qq{</style>}
1107                                    . $cloud->html( $nr )
1108                                    . qq{]]>}
1109                            );
1110                            $feed->add_entry( $feed_entry );
1111    
1112                    } elsif ( $show eq 'last-tag' ) {
1113    
1114                            $nr ||= $last_x_tags;
1115                            $nr = $last_x_tags if $nr > $last_x_tags;
1116    
1117                            $feed->title( "last $nr tagged messages from $CHANNEL" );
1118                            $feed->description( "collects messages which have tags// in them" );
1119    
1120                            foreach my $m ( @last_tags ) {
1121    #                               warn dump( $m );
1122                                    #my $tags = join(' ', @{$m->{tags}} );
1123                                    my $feed_entry = XML::Feed::Entry->new($type);
1124                                    $feed_entry->title( $m->{nick} . '@' . $m->{time} );
1125                                    $feed_entry->author( $m->{nick} );
1126                                    $feed_entry->link( '/#' . $m->{id}  );
1127                                    $feed_entry->issued( DateTime::Format::Flexible->build( $m->{time} ) );
1128    
1129                                    my $message = $filter->{message}->( $m->{message} );
1130                                    $message .= "<br/>\n" unless $message =~ m!<(/p|br/?)>!;
1131    #                               warn "## message = $message\n";
1132    
1133                                    #$feed_entry->summary(
1134                                    $feed_entry->content(
1135                                            "<![CDATA[$message]]>"
1136                                    );
1137                                    $feed_entry->category( join(', ', @{$m->{tags}}) );
1138                                    $feed->add_entry( $feed_entry );
1139    
1140                                    $nr--;
1141                                    last if $nr <= 0;
1142    
1143                            }
1144    
1145                    } elsif ( $show =~ m/^follow/ ) {
1146    
1147                            $feed->title( "Feeds which this bot follows" );
1148    
1149                            my $sth = $dbh->prepare( qq{ select * from feeds order by last_update desc } );
1150                            $sth->execute;
1151                            while (my $row = $sth->fetchrow_hashref) {
1152                                    my $feed_entry = XML::Feed::Entry->new($type);
1153                                    $feed_entry->title( $row->{name} );
1154                                    $feed_entry->link( $row->{url}  );
1155                                    $feed_entry->issued( DateTime::Format::Flexible->build( $row->{last_update} ) );
1156                                    $feed_entry->content(
1157                                            '<![CDATA[<pre>' . dump( $row ) . '</pre>]]>'
1158                                    );
1159                                    $feed->add_entry( $feed_entry );
1160                            }
1161    
1162                            my $feed_entry = XML::Feed::Entry->new($type);
1163                            $feed_entry->title( "Internal stats" );
1164                            $feed_entry->content(
1165                                    '<![CDATA[<pre>' . dump( $_rss ) . '</pre>]]>'
1166                            );
1167                            $feed->add_entry( $feed_entry );
1168    
1169                    } else {
1170                            _log "unknown rss request ",$request->url;
1171                            return RC_DENY;
1172                    }
1173    
1174                    $response->content( $feed->as_xml );
1175                    return RC_OK;
1176            }
1177    
1178            if ( $@ ) {
1179                    warn "$@";
1180            }
1181    
1182            $response->content_type("text/html; charset=UTF-8");
1183    
1184            my $html =
1185                    qq{<html><head><title>$NICK</title><style type="text/css">$style}
1186                    . $cloud->css
1187                    . qq{</style></head><body>}
1188                    . qq{
1189                    <form method="post" class="search" action="/">
1190                    <input type="text" name="search" value="$search" size="10">
1191                    <input type="submit" value="search">
1192                    </form>
1193                    }
1194                    . $cloud->html(500)
1195                    . qq{<p>};
1196    
1197            if ($request->url =~ m#/tags?#) {
1198                    # nop
1199            } elsif ($request->url =~ m#/history#) {
1200                    my $sth = $dbh->prepare(qq{
1201                            select date(time) as date,count(*) as nr,sum(length(message)) as len
1202                                    from log
1203                                    group by date(time)
1204                                    order by date(time) desc
1205                    });
1206                    $sth->execute();
1207                    my ($l_yyyy,$l_mm) = (0,0);
1208                    $html .= qq{<table class="calendar"><tr>};
1209                    my $cal;
1210                    my $ord = 0;
1211                    while (my $row = $sth->fetchrow_hashref) {
1212                            # this is probably PostgreSQL specific, expects ISO date
1213                            my ($yyyy,$mm,$dd) = split(/-/, $row->{date});
1214                            if ($yyyy != $l_yyyy || $mm != $l_mm) {
1215                                    if ( $cal ) {
1216                                            $html .= qq{<td valign="top">} . $cal->as_HTML() . qq{</td>};
1217                                            $ord++;
1218                                            $html .= qq{</tr><tr>} if ( $ord % 3 == 0 );
1219                                    }
1220                                    $cal = new HTML::CalendarMonthSimple('month'=>$mm,'year'=>$yyyy);
1221                                    $cal->border(1);
1222                                    $cal->width('30%');
1223                                    $cal->cellheight('5em');
1224                                    $cal->tableclass('month');
1225                                    #$cal->cellclass('day');
1226                                    $cal->sunday('SUN');
1227                                    $cal->saturday('SAT');
1228                                    $cal->weekdays('MON','TUE','WED','THU','FRI');
1229                                    ($l_yyyy,$l_mm) = ($yyyy,$mm);
1230                            }
1231                            $cal->setcontent($dd, qq[
1232                                    <a href="$url?date=$row->{date}">$row->{nr}</a><br/>$row->{len}
1233                            ]) if $cal;
1234                            
1235                    }
1236                    $html .= qq{<td valign="top">} . $cal->as_HTML() . qq{</td></tr></table>};
1237    
1238            } else {
1239                    $html .= join("</p><p>",
1240                            get_from_log(
1241                                    limit => ( $q->param('last') || $q->param('date') ) ? undef : 100,
1242                                    search => $search || undef,
1243                                    tag => $q->param('tag') || undef,
1244                                    date => $q->param('date') || undef,
1245                                    fmt => {
1246                                            date => sub {
1247                                                    my $date = shift || return;
1248                                                    qq{<hr/><div class="date"><a href="$url?date=$date">$date</a></div>};
1249                                            },
1250                                            time => '<span class="time">%s</span> ',
1251                                            time_channel => '<span class="channel">%s %s</span> ',
1252                                            nick => '%s:&nbsp;',
1253                                            me_nick => '***%s&nbsp;',
1254                                            message => '<span class="message">%s</span>',
1255                                    },
1256                                    filter => $filter,
1257                            )
1258                    );
1259            }
1260    
1261            $html .= qq{</p>
1262            <hr/>
1263            <p>See <a href="/history">history</a> of all messages.</p>
1264            </body></html>};
1265    
1266            $response->content( $html );
1267            warn "<< ", $request->method, " ", $request->uri, " created ", length($html), " bytes\n";
1268            return RC_OK;
1269    }
1270    
1271  POE::Kernel->run;  POE::Kernel->run;

Legend:
Removed from v.5  
changed lines
  Added in v.99

  ViewVC Help
Powered by ViewVC 1.1.26