Hello!
Installed bitchbot for my clan IRC channel for a bit of fun, noticed it's writing a nice log, so added two more lines in bitch.pl so it also logs JOIN's and NICK's, and wrote a wee script to display this so's you can filter/read/search the log.
Interested to hear if you try it, like it, can improve it! :)
(yes - I did nab some of the disclaimers from r1ch!)
#!/usr/bin/perl -w
#########################################################################
#
# bitchlog: a script to display bitchbott log files online with filtering
# capabilities.
#
# Author: Robin Clarke
#
# Date: 09.07.2003
#
# Purpose: so regulars of an IRC channel can read'n'filter stuff that
# happened when they weren't online (god-forbid! ;) )
#
# It's not very pretty, but it's simple and practical.
# Also, please note THIS SOURCE IS PROBABLY NOT 100% SECURE AND/OR WORKING.
#
# If you know ANYTHING about Perl I'm sure (and hopeful) you'll find 101
# ways to optomise/improve the code, and will tell me!
# email any comments to bitchlog@robinclarke.net
#
# see http://www.r1ch.net/projects/bitchbot/ for more details on bitchbot
#
#
#########################################################################
#
# Copyright (C) 2003 Robin Clarke
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# View the license online at http://www.gnu.org/copyleft/gpl.html
#
#########################################################################
### Vars to set up program
my $version = "0.34";
my $defaultShow = 50; # number of lines to show by default
my $timeCol = "#339933"; # colour of time stamp
my $dateCol = "#FF6666"; # colour of date stamp
my $nickCol = "#333399"; # colour of nicks
my $wrap = 110; # at what lenght to wrap lines
# just so we're working on same data as the bitch is
do "/home/clarke/progs/bitchbot/bitch.conf" or die "$!\n";
### Start program
use Time::Local;
use POSIX qw(strftime);
my $debug = undef;
my $errors = undef;
my %input = &getQuery();
my $totalLines = "0";
my $title = "chatlog of $channel on $server heard by $botname : bitchlog $version";
my $textTime = "" . nicetime( time() );
my $from = undef;
my $to = undef;
if( $input->{from} ){
$errors .= "From time/date was not in a valid format... it has been ignored.\n"
unless ( $from = parseDate( $input->{from} ) );
}
if( $input->{to} ){
$errors .= "To time/date was not in a valid format... it has been ignored.\n"
unless ( $to = parseDate( $input->{to} ) );
}
my $show = ( $input->{show} ? $input->{show} : $defaultShow ); # how many lines to display
my $nick = ( $input->{nick} ? $input->{nick} : "" ); # only display messages from this nick
my $filter = ( $input->{filter} ? $input->{filter} : "" ); # filter text to display only lines containing this
my $hlnick = ( $input->{hlnick} ? $input->{hlnick} : "" ); # text from this nick should be highlighted
# unbuffer output and start sending page...
$| = 1;
print <<EOM;
Content-type: text/html
<HTML>
<HEAD>
<TITLE>$title</TITLE>
</HEAD>
<BODY>
<H2>$title</H2>
<FORM METHOD='GET'>
<TABLE BORDER='1'>
<TR>
<TH>From</TH><TH>Untill</TH><TH>Show</TH><TH>Nick Filter</TH><TH>Text Filter</TH><TH>HL Nick</TH><TH></TH>
</TR>
<TR>
<TD><INPUT TYPE="text" SIZE="19" NAME="from" VALUE="$input->{from}"></TD>
<TD><INPUT TYPE="text" SIZE="19" NAME="to" VALUE="$input->{to}"></TD>
<TD><INPUT TYPE="text" SIZE="5" NAME="show" VALUE="$show"></TD>
<TD><INPUT TYPE="text" SIZE="15" NAME="nick" VALUE="$nick"></TD>
<TD><INPUT TYPE="text" SIZE="15" NAME="filter" VALUE="$filter"></TD>
<TD><INPUT TYPE="text" SIZE="15" NAME="hlnick" VALUE="$hlnick"></TD>
<TD><INPUT TYPE="submit" VALUE="Filter"></TD>
</TR>
</TABLE>
</FORM>
<PRE>
EOM
print "$debug\n\n" if( $debug );
print "$errors\n\n" if( $errors );
if( $log = &gimmeLog() ){
print $log;
}else{
print "No log to display meeting your criteria";
}
# finish off the page
print "\n\nLocal time here/now is $textTime\nThere are $totalLines lines in the log\n";
print "I applied following filters:\n" if( $nick || $filter || $from || $to );
print "Nickname had to contain <B>" . lc( $nick ) . "</B>\n" if ( $nick );
print "Text had to contain <B>" . lc( $filter ) . "</B>\n" if ( $filter );
print "Message had to be <I>after</I> <B>" . nicetime( $from ) . "</B>\n" if( $from );
print "Message had to be <I>before</I> <B>" . nicetime( $to ) . "</B>\n" if( $to );
print "</PRE>\n</BODY>\n</HTML>";
exit;
### Subroutines
# gets all appropriate log lines
sub gimmeLog{
my $log;
return $! unless ( open( LOG, "<$logfile" ) );
my( $lastD, $lastM, $lastY );
my @lines = <LOG>;
close LOG;
$totalLines = scalar( @lines );
my $lineCount = 0; # counts the number of lines parsed
my $displayCount = 0; # counts the number of lines displayed
LINE:
while( $displayCount < $show && $lineCount < $totalLines ) {
$lineCount++;
($lTime, $lAction, $lNick, $lText) = split ("\001", $lines[ $totalLines - $lineCount ] );
( $sec, $min, $hour, $day, $month, $year, undef ) = localtime( $lTime );
$year += 1900; $month++;
# skip this line if any of the filters entered by the user say-so
next LINE unless( !$nick || $lNick =~ m/$nick/i );
next LINE unless( !$filter || $lText =~ m/$filter/i );
next LINE unless( !$from || $lTime > $from );
next LINE unless( !$to || $lTime < $to );
$displayCount++;
# add in the date to the log if we've traversed to a new day
unless( $lastD && $lastM && $lastY && ( $lastD == $day && $lastM == $month && $lastY == $year ) ){
$log .= "<FONT SIZE='+1' COLOR='$dateCol'><B>" . sprintf( "%02d", $day ) . "." . sprintf( "%02d", $month ) .
"." . sprintf( "%02d", $year ) . "</B></FONT>\n";
$lastD = $day;
$lastM = $month;
$lastY = $year;
}
# I like some of the text to appear in different colours...
$tCol = undef;
if( $lAction == 1 ){ $tCol = "#ff33ff"; }
elsif( $lAction == 2 || $lAction == 6 || $lAction == 7 || $lNick eq $botname ){ $tCol = "#999999"; }
# don't display secret lines
$lText = "-- something secret was said! --\n" if $lText =~ m/^:s/i;
# wrap text
if( ( length( $lText ) + length( $lNick ) ) > $wrap ){
$lText = wrap( $lText, length( $lNick ) );
}
# HighLight a line if is lines Nick matches hlnick, and then print the line to the log
$hl = ( $hlnick && $lNick =~ m/$hlnick/i ? 1 : undef );
$log .= "<B>" if( $hl );
$log .= "<FONT COLOR='$timeCol'>[" . sprintf( "%02d", $hour ) . ":" . sprintf( "%02d", $min ) . ":" .
sprintf( "%02d", $sec ) . "]</FONT><FONT COLOR='$nickCol'>" . "$lNick</FONT>: " .
( $tCol ? "<FONT COLOR='$tCol'>$lText</FONT>" : $lText ) ;
$log .= "</B>" if( $hl );
}
return ( $log ? $log : "Nothing to display meeting your criteria through $totalLines lines...\n" );
}
# parses the entries from the query
sub getQuery{
my $query = undef;
unless( $ENV{ "REQUEST_METHOD" } ){
return undef;
}
if( $ENV{ "REQUEST_METHOD" } eq "GET" ){
$query = $ENV{ "QUERY_STRING" };
} elsif( $ENV{ "REQUEST_METHOD" } eq "POST" ){
read( STDIN, $query, $ENV{ "CONTENT_LENGTH" } );
}
# $debug .= "$ENV{ REMOTE_ADDR }:$query\n";
my (%input, @elements, $element, $key, $value );
@elements = split /&/, $query;
for $element( @elements ){
$element =~ tr/+/ /; # decode '+' to spaces
($key, $value) = split( /=/, $element );
$key =~ s/%([\dA-Fa-f]{2})/pack("C",hex($1))/ge; # decode key
$value =~ s/%([\dA-Fa-f]{2})/pack("C",hex($1))/ge; # decode value
if( defined $input->{ $key } ){
$input->{ $key } .= "\0$value";
} else {
$input->{ $key } = $value;
}
}
%input;
}
# wraps a text line
sub wrap{
my $in = shift;
my $lbuff = shift;
$lbuff += 12; # 12 chars for time
my $out = undef;
my $rWrap = ( $wrap - $lbuff > 0 ? $wrap - $lbuff : 10 );
return $in unless( index( $in, " " ) > 0 );
while( length( $in ) > $rWrap ){
$index = 0; $try = 0;
while( ( $try = index( $in, " ", $index + 1 ) ) > 0 && $try < $rWrap ){ $index = $try; } # find last space
$index = ( $try > 0 ? $try : length( $in ) - 1 ) if( $index == 0 );
$out .= "" . ( $out ? ( " " x $lbuff ) : "" ) . substr( $in, 0, $index ) . "\n";
$in = substr( $in, $index + 1 ); # add one to remove the space you've just stopped at.
}
$out .= ( " " x $lbuff ) . $in if( length( $in ) > 0 ); # don't forget to add erst of line
return $out;
}
# various formats of text entry into from/until fields are parsed here to a fully qualified time
# [number]d = [number] days ago (to the second)
# [number]h = [number] hours ago (to the second)
# [number]m = [number] minutes ago (to the second)
# hh:mm:ss-DD.MM.YYYY is the way to define a static date.
sub parseDate{
my $in = shift;
if( $in =~ m/^(\d*)D$/i ){
return time() - ( $1 * 86400 );
}elsif( $in =~ m/^(\d*)H$/i ){
return time() - ( $1 * 3600 );
}elsif( $in =~ m/^(\d*)M$/i ){
return time() - ( $1 * 60 );
}elsif( $in =~ /^(\d{2}):(\d{2}):(\d{2})-(\d{2})\.(\d{2})\.(\d{4})$/ ){
$temp = $5 - 1;
return timelocal( $3, $2, $1, $4, $temp, $6 );
}
return undef;
}
# this is the time format /I/ likes
sub nicetime{
$in = shift;
return strftime( "%a %d.%b.%Y %H:%M:%S", localtime( $in ) );
}
[/code]