rendered paste body#!/usr/bin/perl -w
use Net::IRC;
use LWP::Simple;
use URI::Escape;
use IO::Handle;
use Data::Dumper;
### Init
my $irc = new Net::IRC;
my $conn = $irc->newconn(Server => 'irc.freenode.net',
Port => 6667,
Nick => ($ARGV[0] || 'hamlin'),
Ircname => 'hamlin, second incarnation',
Username => 'hamlin')
or die "Can't connect to IRC server.\n";
open LOG, ">>hamlin.log";
LOG->autoflush(1);
### The body of the bot.
my %Factoids;
my @Todos; # [idx] = { id => ID, name => name of person doing it, priority => priority, desc => description }
my($TodoLastID) = 0;
my %Karma;
my %Countdowns;
my %CountdownHelpers;
my %NicksHere;
my(@GoodNicks) = (); # canuckid canuckid_ chacha2006 Stern ichaelmay8
my(%IdNicks);
my($ReverseMode) = 0;
sub handle_line($$$$;$);
sub excellent($) {
my($nick) = shift or die;
$Karma{$nick} = 0 unless exists $Karma{$nick};
$Karma{$nick} += 3;
}
sub good($) {
my($nick) = shift or die;
$Karma{$nick} = 0 unless exists $Karma{$nick};
$Karma{$nick}++;
}
sub bad($) {
my($nick) = shift or die;
$Karma{$nick} = 0 unless exists $Karma{$nick};
$Karma{$nick}--;
}
sub terrible($) {
my($nick) = shift or die;
$Karma{$nick} = 0 unless exists $Karma{$nick};
$Karma{$nick} -= 3;
}
sub trivial_get($$$)
{
my($host, $port, $path) = @_;
#print "HOST=$host, PORT=$port, PATH=$path\n";
require IO::Socket;
local($^W) = 0;
my $sock = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
Timeout => 60) || return undef;
$sock->autoflush;
my $netloc = $host;
$netloc .= ":$port" if $port != 80;
print $sock join("\015\012" =>
"GET $path HTTP/1.0",
"Host: $netloc",
"User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.6) Gecko/20050411 Firefox/1.0.2",
"", "");
my $buf = "";
my $n;
1 while $n = sysread($sock, $buf, 8*1024, length($buf));
return undef unless defined($n);
if ($buf =~ m,^HTTP/\d+\.\d+\s+(\d+)[^\012]*\012,) {
my $code = $1;
if ($code =~ /^30[1237]/ && $buf =~ /\012Location:\s*(\S+)/i) {
# redirect
# my $url = $1;
return undef;# if $loop_check{$url}++;
# return trivial_get($url, $host, $port, $path);
}
if ($code !~ /^2/) {
print STDERR "Error $code\n";
}
return undef unless $code =~ /^2/;
$buf =~ s/.+?\015?\012\015?\012//s; # zap header
}
return $buf;
}
our($depth) = 0;
our($np) = qr{
\[
(?:
(?> [^][]+ )
|
(??{ $np })
)*
\]
}x;
sub send_line($$$$;$) {
my($self,$line,$nick,$dest) = @_;
unless ($IdNicks{$nick} > 0) { return; }
$self->privmsg($dest, &handle_line||"");
}
## ##
# Arg 1: $self (object) #
# Arg 2: the incoming line (string) #
# Arg 3: nick who sent it (string) #
# Arg 4: where to send response #
## ##
sub handle_line($$$$;$) {
my($self,$line,$nick,$dest,$prefix) = @_;
my($wordy) = ($nick eq $dest);
$prefix = "" unless defined($prefix);
if ($line =~ /^\[.*\]$/) {
$line =~ s/^\[//; $line =~ s/\]$//;
}
if ($depth >= 20) {
return "... no, I'm not going to crash.";
}
if ($nick ne "hamlin") {
$depth++;
$line =~ s/[~`]($np)/handle_line($self,$1,$nick,$dest)||""/egx; #`]//;# to appease xemacs
$depth--;
}
my(@words) = split/\s+/, $line;
my($rest) = join " ", @words[1..$#words];
my($nickok) = 0;
if (grep { /^\Q$nick\E$/i } (@GoodNicks)) {
$nickok = 1;
unless (exists $IdNicks{$nick} and $IdNicks{$nick}>0) {
$IdNicks{$nick} = 0;
$self->privmsg("NickServ", "list $nick");
$self->privmsg($nick, "You must be identified to use the privileged parts of the bot.");
$self->schedule(2, \&send_line, $line, $nick, $dest);
$self->privmsg($nick, "If you are, wait a few seconds.");
return;
}
}
if ($nick eq "hamlin") { $nickok = 1; }
$words[0] = lc $words[0];
if ($words[0] eq "literal") {
if (exists $Factoids{$rest}) {
my($reply) = $Factoids{$rest};
$reply =~ s/^is\s+//;
return "$reply";
}
return "I literally don't know what you're talking about.";
}
if ($words[0] eq "setkarma") {
if ($nick ne "hamlin") { bad $nick; return "Loophole closed. :-)"; }
$Karma{$words[1]} = int($words[2]);
return;
}
if ($words[0] eq "braindump") {
$self->privmsg($nick, "I know about ".join(", ", keys(%Factoids)).".");
return;
}
if ($words[0] eq "say") {
return "$rest";
}
if ($words[0] eq "latest") {
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time());
$year += 1900;
$month = sprintf "%02d", $mon+1;
$day = sprintf "%02d", $mday;
return "Kernel: http://izilla.elwiki.com/builds/$year-$month-$day-kernel.bin.gz || Podzilla: http://izilla.elwiki.com/builds/$year-$month-$day-podzilla.gz || Remember to gunzip the files. If podzilla looks corrupted, check if the first four letters are 'bFLT'; if so, it's not compressed.";
}
if ($words[0] eq "wiki" or $words[0] eq "wikisearch") { SEARCH: {
if (!$rest) {
return "Use the WIKI. http://izilla.elwiki.com/";
} else {
if ($words[0] ne "wikisearch") {
$content = get("http://izilla.elwiki.com/".uri_escape($rest));
if (defined($content) && $content !~ /currently no text in this page/) {
$retstr = "$rest (http://izilla.elwiki.com/".uri_escape($rest).") [".length($content)." bytes]";
$self->privmsg($dest, $retstr);
return "";
}
}
$content = get("http://izilla.elwiki.com/Special:Search?search=".
uri_escape($rest)."&fulltext=Search");
if (!defined($content)) {
return "Error fetching <http://izilla.elwiki.com/Special:Search?search=".
uri_escape($rest)."&fulltext=Search>";
}
if ($content =~ /unsuccessful search/) {
return "No matches. (By the way, words <= 3 letters aren't indexed.)";
}
my($firstmatch) = 1;
my($infosent) = 0;
my($result) = 1;
my($results) = 5;
while ($content =~ m[<li><a href="(.*?)" title=".*?">(.*?)</a> \((\d+) bytes\)]g) {
$retstr = "$2 (http://izilla.elwiki.com$1) [$3 bytes]";
if ($dest eq $nick and $words[0] eq "wikisearch") {
$retstr = "$result. ".$retstr;
}
if ($firstmatch) {
$self->privmsg($dest, $retstr);
$firstmatch = 0;
} else {
if (!$infosent and $dest ne $nick) {
$self->privmsg($dest, "I'll /msg you the next @{[$results-1]} results.");
$infosent = 1;
}
$self->privmsg($nick, $retstr);
}
last if $result >= $results or $words[0] ne "wikisearch";
$result++;
}
if ($firstmatch) { return "No match, apparently (bug in hamlin)." }
return "";
}
}}
if ($words[0] eq "google") {
$dst = "";
$longlist = 0;
$summfirst = 0;
if ($nick eq $dest) { $dst = $nick; $longlist = 1; $summfirst = 0; }
elsif ($rest =~ /for me/) { $rest =~ s/\s*for me//; $dst = $nick; $longlist = 1; $summfirst = 1; }
elsif ($rest =~ /for (\S+)/) { $dst = $1; $rest =~ s/\s*for (\S+)//; $longlist = 1; $summfirst = 1; }
else { $dst = $dest; $longlist = 0; $summfirst = 1; }
$content = trivial_get("www.google.com", 80, "/search?hl=en&q=".uri_escape($rest)."&btnG=Google+Search");
if (!defined($content)) {
return "Error fetching <http://www.google.com/search?hl=en&q=".
uri_escape($rest)."&btnG=Google+Search>";
}
if ($content =~ /did not match any documents/) {
return "No match.";
}
$content =~ s/&#(\d+);/chr($1)/eg;
my($firstmatch) = 1;
my($infosent) = 0;
my($result) = 1;
my($results) = 5;
while ($content =~ m[<p class=g>.*?<a href="(.*?)".*?>(.*?)</a>.*?<font size=-1>(.*?)<br>]sg) {
$retstr = "$2 ($1): $3";
$retstr =~ s|</?b>||g;
if (length $retstr > 200) {
$retstr = substr($retstr,0,197)."...";
}
if ($firstmatch and $summfirst) {
$self->privmsg($dest, $retstr);
$firstmatch = 0;
}
if ($longlist) {
$retstr = "$result. ".$retstr;
}
if (!$infosent and $longlist and $summfirst) {
$self->privmsg($dest, "I'll /msg you the next @{[$results-1]} results.");
$infosent = 1;
}
if ($longlist) {
$self->privmsg($dst, $retstr);
}
last if $result >= $results or !$longlist;
$result++;
}
if ($firstmatch and $summfirst) { return "No match, apparently (bug)." }
return "";
}
# if ($words[0] eq "countdown") {
# my($thing) = $words[1];
# if ($line =~ /.*in\s+(.+)/) {
# my($offset) = $1;
# $offset =~ s/(\d+)/+$1/g;
# $offset =~ s/days?/*86400/g;
# $offset =~ s/weeks?/*86400*7/g;
# $offset =~ s/months?/*86400*30/g;
# $offset =~ s/years?/*86400*365/g;
# $offset =~ s/(\s+|[a-zA-Z`$()]+)//g;
# $Countdowns{$words[1]} = time() + eval($offset);
# $CountdownHelpers{$words[1]} = [];
# } elsif ($line =~ /.*at\s+(\d+)/) {
# $Countdowns{$words[1]} = $1;
# $CountdownHelpers{$words[1]} = [];
# } elsif (!exists $Countdowns{$words[1]}) {
# return "I don't know anything about a countdown to $words[1].";
# }
# return "Tentative release date for $thing: ".localtime($Countdowns{$words[1]});
# }
# if ($words[0] eq "when") {
# $Countdowns{$words[1]} += 86400*30;
# my($asker) = $dest;
# if ($asker =~ /^#/) {
# $asker = $words[2];
# }
# push @{$CountdownHelpers{$words[1]}}, $asker;
# if (defined($prefix) and $prefix =~ /^#/) {
# $dest = $prefix;
# }
# $self->privmsg($dest, "Tentative release date for $words[1]: ".localtime($Countdowns{$words[1]}));
# $self->privmsg($dest, "Some of those responsible for the delay: ".join(', ', @{$CountdownHelpers{$words[1]}}));
# return "";
# }
# if ($words[0] eq "blame") {
# push @{$CountdownHelpers{$words[3]}}, $words[1];
# $self->privmsg($dest, "Tentative release date for $words[3]: ".localtime($Countdowns{$words[3]}));
# $self->privmsg($dest, "Some of those responsible for the delay: ".join(', ', @{$CountdownHelpers{$words[3]}}));
# return "";
# }
#
# if ($words[0] eq "nocountdown") {
# delete $Countdowns{$words[1]};
# delete $CountdownHelpers{$words[1]};
# return "Countdown $words[1] deleted.";
# }
if ($words[0] eq "karma") {
if (exists $Karma{$words[1]}) {
if ($Karma{$words[1]} >= 1000000) {
return "$words[1]'s karma is infinitely good.";
} elsif ($Karma{$words[1]} <= -1000000) {
return "$words[1]'s karma is infinitely bad.";
}
return "$words[1]'s karma is $Karma{$words[1]}.";
}
return "$words[1] has no karma.";
}
if ($words[0] eq "extol") {
if (!$nickok) { return; }
$Karma{$words[1]} = 10000000;
return;
}
if ($words[0] eq "abhor") {
if (!$nickok) { return; }
$Karma{$words[1]} = -10000000;
return;
}
if ($words[0] eq "excellent") {
if (!$nickok) { return; }
if ($words[1] eq $nick) { $self->privmsg($nick, "You can't change your own karma."); return; }
$Karma{$words[1]} = 0 unless exists $Karma{$words[1]};
$Karma{$words[1]} += 3;
return ($wordy?"$words[1]'s karma is now $Karma{$words[1]}.":"");
}
if ($words[0] =~ /\+\+$/) {
$words[0] =~ s/\+\+$//;
($words[1],$words[0]) = ($words[0],"good");
}
if ($words[0] =~ /--$/) {
$words[0] =~ s/--$//;
($words[1],$words[0]) = ($words[0],"bad");
}
if ($words[0] eq "good") {
if ($words[1] eq $nick) { $self->privmsg($nick, "You can't change your own karma."); return; }
$Karma{$words[1]} = 0 unless exists $Karma{$words[1]};
$Karma{$words[1]}++;
return ($wordy?"$words[1]'s karma is now $Karma{$words[1]}.":"");
}
if ($words[0] eq "bad") {
if ($words[1] eq $nick) { $self->privmsg($nick, "You can't change your own karma."); return; }
$Karma{$words[1]} = 0 unless exists $Karma{$words[1]};
$Karma{$words[1]}--;
return ($wordy?"$words[1]'s karma is now $Karma{$words[1]}.":"");
}
if ($words[0] eq "terrible") {
if (!$nickok) { return; }
if ($words[1] eq $nick) { $self->privmsg($nick, "You can't change your own karma."); return; }
$Karma{$words[1]} = 0 unless exists $Karma{$words[1]};
$Karma{$words[1]} -= 3;
return ($wordy?"$words[1]'s karma is now $Karma{$words[1]}.":"");
}
if ($words[0] eq "voices") {
return "Listening to: < @GoodNicks >";
}
if ($words[0] eq "listen") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
push @GoodNicks, $words[1]||"";
return ($wordy?"Now listening to $words[1]. Listening to: < @GoodNicks >":"");
}
if ($words[0] eq "ignore") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
my($badnick) = $words[1]||"";
@GoodNicks = grep { !m/^\Q$badnick\E$/ } (@GoodNicks);
if (!scalar @GoodNicks) {
@GoodNicks = ( $nick );
}
return ($wordy?"Now ignoring $badnick. Listening to: < @GoodNicks >":"");
}
if ($words[0] eq "forget") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
if (exists $Factoids{$rest}) {
delete $Factoids{$rest};
return ($wordy?"I forgot about $rest, $nick.":"");
}
return ($wordy?"I didn't know anything about $rest, $nick.":"");
}
if ($words[0] eq "todo") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
push @Todos, { id => ++$TodoLastID, name => $words[1], priority => $words[2], desc => join(" ", @words[3..$#words]) };
my($ret) = $TodoLastID.". @".$words[2]." <".$words[1]."> ".$Todos[$#Todos]{desc};
@Todos = sort { $b->{priority} <=> $a->{priority} } @Todos;
return $ret;
}
if ($words[0] eq "todump") {
my($dumpy) = Data::Dumper::Dumper(\@Todos);
$dumpy =~ s/\n/ \\ /g;
return $dumpy;
}
if ($words[0] eq "todone") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
my($oldnum) = scalar @Todos;
@Todos = grep { $_->{id} != $words[1] } @Todos;
return ($oldnum - scalar @Todos
)." todos killed off.";
}
if ($words[0] eq "todos") {
if ($rest =~ /^\s*$/ || $rest =~ /for (me|\w+)/) {
my($target) = $1;
$target = "" unless defined($target);
$target = $nick if $target eq "me";
if (scalar(@Todos) > 0) {
my($ret);
if ($target eq $nick) {
$ret = "Your issues";
} elsif ($target) {
$ret = "${target}'s issues";
} else {
$ret = "Most pressing issues";
}
my($maxnr) = 5;
my($comma) = 0;
foreach (@Todos) {
last if !$maxnr;
next if $target && $target !~ /$_->{name}_*/;
$ret .= ($comma++? ", " : ": ");
if (!$target) {
$ret .= "<03".$_->{name}."> ";
}
$ret .= "(#".$_->{id}.") ".$_->{desc};
$maxnr--;
}
$ret .= ", ..." unless $maxnr;
if ($maxnr == 5) { return "Nothing todo for $target."; }
return $ret;
}
return "Nothing todo.";
} elsif ($words[1] =~ /#(\d+)/) {
my($id) = $1;
foreach (@Todos) {
if ($_->{id} == $id) {
return "<03".$_->{name}."> ".$_->{desc};
}
}
return "Couldn't find it.";
}
return "Huh?";
}
if ($words[0] eq "part") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->part ($words[1], $nick);
return;
}
if ($words[0] eq "join") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->join ($words[1], $words[2]);
return ($wordy?"Joined $words[1].":"");
}
if ($words[0] eq "write") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
open FACTOIDS, ">factoids.txt";
my($nfac,$nkar,$nblame) = (0,0,0);
foreach (keys %Factoids) {
print FACTOIDS "$_ $Factoids{$_}\n";
$nfac++;
}
foreach (keys %Karma) {
print FACTOIDS "setkarma $_ $Karma{$_}\n";
$nkar++;
}
foreach (@GoodNicks) {
print FACTOIDS "listen $_\n";
}
foreach (@Todos) {
print FACTOIDS "todo ".$_->{name}." ".$_->{priority}." ".$_->{desc}."\n";
}
# foreach (keys %Countdowns) {
# print FACTOIDS "countdown $_ at $Countdowns{$_}\n";
# }
# foreach (keys %CountdownHelpers) {
# my($ctdn) = $_;
# for (@{$CountdownHelpers{$_}}) {
# print FACTOIDS "blame $_ for $ctdn\n";
# $nblame++;
# }
# }
close FACTOIDS;
return "Wrote $nfac factoids".($nkar?" and $nkar karma data":"").".";
}
if ($words[0] eq "mode") {
if (!$nickok) { terrible $nick; return; }
if (substr($words[1],0,1) eq "#") {
$dest = $words[1];
$rest = join " ", @words[2..$#words];
}
$self->mode ($dest, $rest);
return;
}
if ($words[0] eq "kick") {
my($whom);
if (!$nickok) { terrible $nick; return; }
if (substr($words[1],0,1) eq "#") {
$dest = $words[1];
$whom = $words[2];
$rest = join " ", @words[3..$#words];
} else {
$whom = $words[1];
$rest = join " ", @words[2..$#words];
}
$self->kick ($dest, $whom, $rest);
return;
}
if ($words[0] eq "leave") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->quit ($nick);
sleep(2);
open FACTOIDS, ">factoids.txt";
foreach (keys %Factoids) {
print FACTOIDS "$_ $Factoids{$_}\n";
}
foreach (keys %Karma) {
print FACTOIDS "setkarma $_ $Karma{$_}\n";
}
foreach (@GoodNicks) {
print FACTOIDS "listen $_\n";
}
foreach (@Todos) {
print FACTOIDS "todo ".$_->{name}." ".$_->{priority}." ".$_->{desc}."\n";
}
# foreach (keys %Countdowns) {
# print FACTOIDS "countdown $_ at $Countdowns{$_}\n";
# }
# foreach (keys %CountdownHelpers) {
# my($ctdn) = $_;
# for (@{$CountdownHelpers{$_}}) {
# print FACTOIDS "blame $_ for $ctdn\n";
# }
# }
close FACTOIDS;
close LOG;
exit(0);
}
if ($words[0] eq "nick") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->nick ($words[1]);
return;
}
if ($words[0] eq "qquit") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->quit ("$nick");
close LOG;
exit 0;
}
if ($words[0] eq "qupdate") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$self->quit ("Restarted by $nick");
close LOG;
sleep 2;
exec "./$0";
}
if ($words[0] eq "update") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
open FACTOIDS, ">factoids.txt";
foreach (keys %Factoids) {
print FACTOIDS "$_ $Factoids{$_}\n";
}
foreach (keys %Karma) {
print FACTOIDS "setkarma $_ $Karma{$_}\n";
}
foreach (@GoodNicks) {
print FACTOIDS "listen $_\n";
}
foreach (@Todos) {
print FACTOIDS "todo ".$_->{name}." ".$_->{priority}." ".$_->{desc}."\n";
}
# foreach (keys %Countdowns) {
# print FACTOIDS "countdown $_ at $Countdowns{$_}\n";
# }
# foreach (keys %CountdownHelpers) {
# my($ctdn) = $_;
# for (@{$CountdownHelpers{$_}}) {
# print FACTOIDS "blame $_ for $ctdn\n";
# }
# }
close FACTOIDS;
$self->quit ("Restarted by $nick");
close LOG;
exec "./$0";
}
if ($words[0] eq "tell") {
if (!$nickok) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
if ($line =~ /tell\s+(\S+)\s+(?:about\s+)?(.*)/) {
$self->privmsg ($1, handle_line($self,$2,$nick,$1)||"");
return;
}
}
if ($words[0] eq "inform") {
if (!$nickok && $words[1] =~ /^#/) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
if ($line =~ /inform\s+(\S+)\s+(?:about\s+)?(.*)/) {
my($res) = (handle_line($self,$2,$nick,$1,$dest)||"");
if ($res ne "") {
return "$1, ".$res;
}
return "";
}
}
if ($line =~ /(.+?)\s+(is.+|means.+|are.+)/) {
my($word,$factoid) = ($1,$2);
$word =~ s/^fact\s+//;
if (!$nickok and $nick ne "hamlin" and $word ne $nick) { $self->privmsg($nick, "You aren't allowed to do that."); bad $nick; return; }
$Factoids{$word} = $factoid;
return ($wordy?"OK, $nick.":"");
}
keys %Factoids; # resets `each' iterator
# Iterate through the whole factoid list.
while (($k, $v) = each %Factoids) {
my($key) = $k;
$key =~ s/([][.^\$+*?])/\\$1/g if $key =~ /[][.^\$+*?]/;
$key =~ s/\\\$\d+/(.*?)/g; # fix arg specifier
my($input) = $line;
$input=~s/(^\s+|\s+$)//; # strip whitespace
if ($input =~ /^$key$/i) {
my($fact) = $v;
my($one,$two,$three,$four,$five) = ($1,$2,$3,$4,$5);
$fact =~ s/\$1/$one/g if $one;
$fact =~ s/\$2/$two/g if $two;
$fact =~ s/\$3/$three/g if $three;
$fact =~ s/\$4/$four/g if $four;
$fact =~ s/\$5/$five/g if $five;
$fact =~ s/^\s+//;
$fact =~ s/\$who/$nick/gi;
my(@factoids) = split /\s \\ \s/x, $fact;
if (scalar @factoids > 1) {
my($prefix) = "$key ";
if ($fact =~ /<reply>/) {
$prefix = "";
$fact =~ s/<reply>\s*//; $fact =~ s/(is|means|are)\s+//;
}
@factoids = split /\s \\ \s/x, $fact;
foreach (@factoids) {
my($factoid) = $_;
my($suffix) = (($factoid eq $factoids[$#factoids])? " [from $nick]" : "");
sleep(1);
$self->privmsg($dest, $prefix.$factoid.($wordy? "" : $suffix));
$prefix = "";
}
return "";
} else {
if ($fact =~ /<reply>/) {
$fact =~ s/<reply>\s*//; $fact =~ s/(is|means|are)\s+//;
return "$fact ".($wordy?"":"[from $nick]");
} elsif ($fact =~ /<action>/) {
$fact =~ s/<action>\s*//; $fact =~ s/(is|means|are)\s+//;
$self->ctcp('action',$dest,$fact);
return;
} else {
return "$key $fact ".($wordy?"":"[from $nick]");
}
}
}
}
my(@goodwords) = split/\s+/, $line;
my($bestfact,$bestnum) = ("",0);
my($num);
keys %Factoids;
while (($k, $v) = each %Factoids) {
$num = 0;
for (@goodwords) {
my($word) = $_;
my($kk) = $k;
$num += ($kk =~ s/\Q$word\E//g);
}
if ($num > $bestnum) {
$bestnum = $num;
$bestfact = $k;
}
}
if ($bestnum) {
return "I assume you meant $bestfact. ".handle_line($self,$bestfact,$nick,$dest)||"";
}
if ($wordy) {
return "Sorry, $nick, I don't know anything about $line.";
}
return;
}
### The handlers
sub on_connect {
my $self = shift;
$self->join("#izilla.bot");
$self->join("#izilla");
# Ha! You thought you'd actually get the password! :-)
$self->privmsg("nickserv", "identify izilla");
print "Joined #izilla and #izilla.bot.\n";
print "Loading factoids... ";
if (-e "factoids.txt") {
open FACTOIDS, "factoids.txt";
while (<FACTOIDS>) {
chomp;
handle_line($self, $_, "hamlin", "nonexistent");
}
close FACTOIDS;
}
print "Ready.\n";
}
sub on_init {
my ($self, $event) = @_;
my (@args) = ($event->args);
shift(@args);
print "*** @args\n";
}
sub on_join {
my ($self, $event) = @_;
my (@args) = ($event->args);
my (@to) = ($event->to);
print LOG "-!- join ".$event->nick."\n";
$NicksHere{$to[0]} = {} unless exists $NicksHere{$to[0]};
$NicksHere{$to[0]}{$event->nick} = 1;
my($nick) = $event->nick;
if (grep { /\Q$nick\E/ } (@GoodNicks)) {
$self->privmsg("NickServ", "list ".$event->nick);
$IdNicks{$event->nick} = 0;
}
}
sub on_nick {
my ($self, $event) = @_;
my (@args) = ($event->args);
my (@to) = ($event->to);
$NicksHere{$to[0]}{$event->nick} = 0;
$NicksHere{$to[0]}{$args[0]} = 1;
print LOG "-!- ".$event->nick." is now known as ".$args[0]."\n";
$IdNicks{$event->nick} = 0;
}
sub on_part {
my ($self, $event) = @_;
my (@args) = ($event->args);
my (@to) = ($event->to);
$NicksHere{$to[0]}{$event->nick} = 0;
print LOG "-!- part ".$event->nick." (@args)\n";
$IdNicks{$event->nick} = 0;
}
sub on_notice {
my($self,$event) = @_;
my($nick) = $event->nick;
my($arg) = $event->args;
if (lc $nick eq "nickserv" and $arg =~ /^(\S+).*ONLINE/) {
$IdNicks{$1} = 1;
print LOG "-!- $1 is identified.\n";
}
}
sub on_msg {
my($self,$event) = @_;
my ($nick) = ($event->nick);
my ($arg) = ($event->args);
return if $nick =~ /hamlin/;
print LOG "[$nick] $arg\n";
$arg =~ s/^[~`]+//;
$self->privmsg($nick, handle_line($self, $arg, $nick, $nick)||"");
}
my(%Warned);
my($thisnick,$lastnick) = ('','');
sub on_public {
my($self,$event) = @_;
my @to = $event->to;
my ($nick) = ($event->nick);
my ($arg) = ($event->args);
if ($nick ne $thisnick) {
$lastnick = $thisnick;
$thisnick = $nick;
}
return if $nick =~ /hamlin/;
print LOG "<$nick> $arg\n";
if ($arg =~ /(^|\W)(thx|thks|tks|tnx|thanks|thank\s*you)(\W|$)/) {
my($found) = 0;
for (keys %Karma) {
if ($arg =~ /(^|\W)($_)(\W|$)/) {
good $2;
$found = 1;
last;
}
}
good $lastnick unless $found;
}
if ($arg =~ /(\S+)[:,] /) {
my($x) = $1;
if (exists $NicksHere{$to[0]} and exists $NicksHere{$to[0]}{$x} and ($NicksHere{$to[0]}{$x} == 0)) {
$self->privmsg($to[0], "I think he left.");
$NicksHere{$to[0]}{$x} = 1; # don't say it again
}
}
if ($arg =~ /[Ii].*(working\s+on|writing)/) {
excellent $nick;
}
if ($arg =~ /[Ii].*releas(e|ing)/) {
if ($arg =~ /4g/) {
bad $nick;
} else {
excellent $nick;
}
}
if ($arg =~ /(when|how\s+close|how\+long).*(4[Gg]|fourth[- ]gen(eration)?|photo|ipod.*color|color.*ipod).*support(ed)?/i) {
bad $nick;
}
$ReverseMode = 1 if $arg =~ /esrever\s*([~`]|[:,]toblpi)$/ or $arg =~ /^([~`]|hamlin[,:])\s*reverse/;
$ReverseMode = 0 if $arg =~ /drawrof\s*([~`]|[:,]toblpi)$/ or $arg =~ /^([~`]|hamlin[,:])\s*forward/;
$arg = reverse $arg if $ReverseMode;
if ($arg =~ /^([~`]|hamlin[,:])/) {
$arg =~ s/^([~`]+|hamlin[,:]\s*)//;
my($dest) = $to[0];
if (!grep { /^\Q$nick\E$/ } (@GoodNicks)) {
$dest = $nick;
}
if ($ReverseMode) {
$self->privmsg($dest, scalar reverse(handle_line($self, $arg, $nick, $dest)||""));
} else {
$self->privmsg($dest, handle_line($self, $arg, $nick, $dest)||"");
}
} elsif ($arg =~ /~\[.*\]/) {
if (!grep { /^\Q$nick\E$/ } (@GoodNicks)) {
return;
}
$max = 3;
while ($arg =~ /~($np)/g) {
if (!$max) {
$self->privmsg($nick, "Only up to 3 offhand substitutions per line, please.");
last;
}
if ($ReverseMode) {
$self->privmsg($to[0], scalar reverse(handle_line($self, $1, $nick, $to[0])||""));
} else {
$self->privmsg($to[0], handle_line($self, $1, $nick, $to[0])||"");
}
$max--;
}
}
}
sub on_ping {
my($self,$event) = @_;
$self->ctcp_reply($event->nick, join(' ', $event->args));
}
### Set up handlers
$conn->add_handler('cping', \&on_ping);
$conn->add_handler('msg', \&on_msg);
$conn->add_handler('public',\&on_public);
$conn->add_handler('umode', \&on_umode);
$conn->add_handler('notice',\&on_notice);
$conn->add_handler('names', \&on_names);
$conn->add_handler('nick', \&on_nick);
$conn->add_handler('join', \&on_join);
$conn->add_handler('part', \&on_part);
$conn->add_global_handler([ 251,252,253,254,302,255 ], \&on_init);
$conn->add_global_handler(376, \&on_connect);
$conn->add_global_handler(433, \&on_nick_taken);
print "Starting...\n";
$irc->start;