/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26