# DreamBot - An Experimental IRC Bot written in Perl. # Copyright (C) 1999-2002 Osman "spectre" Keskin # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA sub konnekt { ($server, $port) = @_; local *SOCK; $iaddr = inet_aton($server); $paddr = sockaddr_in($port, $iaddr); $proto = getprotobyname('tcp'); socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "socket failed: $!"; $sockaddr = 'S n a4 x8'; if (!connect(SOCK, $paddr)) { console("*** connect failed: $!\n"); return 0; } return *SOCK; } sub sockwrite { my $sck = $_[0]; $stuff = $_[1]; select($sck); $| = 1; print $sck "$stuff\n"; select(STDOUT); } sub fhbits { my (@fhlist) = split (' ', $_[0]); my ($bits); for (@fhlist) { vec($bits, fileno($_), 1) = 1; } $bits; } sub console { printf("$_[0]"); if ($log eq true) { ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime(time()); $text = sprintf("%02d/%02d/%2d %02d:%02d:%02d %s", $day, $mon, $year, $hour, $min, $sec, $_[0]); select(LOGFILE); $| = 1; print LOGFILE $text; select(STDOUT); } } sub getline { my $line = ""; my $sck = $_[0]; while (1) { ($nfound, $timeleft) = select($rout = $rin, undef, undef, 0); if ($rout & $sck) { if (sysread($sck, $buf, 1) <= 0) { console("*** died, reconnecting\n"); serverconnect(); return ""; } if ($buf eq "\n" || $buf eq "\r") { return $line; } elsif ($buf ne "\r") { $line .= $buf; } } } } sub gettag { my $line = ""; my $sck = $_[0]; $opentag = false; while (1) { ($nfound, $timeleft) = select($rout = $rin, undef, undef, 0); if ($rout & $sck) { if (sysread($sck, $buf, 1) <= 0) { return ""; } if ($buf eq ">" || ($opentag eq false && $buf eq "<" && $line ne "")) { return $line; } elsif ($buf ne "<") { $line .= $buf; } elsif ($buf eq "<") { $opentag = true; } } } } sub chanlog { ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime(time()); $text = sprintf("%02d/%02d/%2d %02d:%02d:%02d %s", $day, $mon, $year, $hour, $min, $sec, $_[1]); select($_[0]); $| = 1; print {$_[0]} $text; select(STDOUT); } sub openlogfile { local *LFILE; open(LFILE, ">>$logdir/" . $_[0] . ".log"); return *LFILE; } sub processchanflags { $chan = $_[0]; $channels{$chan} = ""; $bitchmode{$chan} = false; $opprot{$chan} = false; $modelock{$chan} = false; $topiclock{$chan} = false; $logging{$chan} = false; $needinvite{$chan} = ""; $public_fm{$chan} = true; $public_sd{$chan} = true; $public_jb{$chan} = false; $learning{$chan} = true; if ($_[1] ne "") { @keys = split (' ', $_[1]); for ($i = 0; $i <= $#keys; $i++) { $key = $keys[$i]; $key =~ /([\+\-]?)(.*)/; $flag = $1; $key = $2; if ($flag eq '+') { if ($key =~ /bitch(?:mode)?/i) { $bitchmode{$chan} = true; } elsif ($key =~ /opprot/i) { $opprot{$chan} = true; } elsif ($key =~ /modelock/i) { $modelock{$chan} = true; } elsif ($key =~ /topiclock/i) { $topiclock{$chan} = true; } elsif ($key =~ /log/i) { $logging{$chan} = true; } elsif ($key =~ /key/i) { $i++; $channels{$chan} =~ /([^,]*)[,]?(.*)/; $channels{$chan} = $1 . ',' . $keys[$i]; } elsif ($key =~ /modes/i) { $i++; $channels{$chan} =~ /([^,]*)[,]?(.*)/; $modes = $keys[$i]; $data = $2; if ($modes =~ /k/) { $i++; $modes .= ' ' . $keys[$i]; } if ($modes =~ /l/) { $i++; $modes .= ' ' . $keys[$i]; } $channels{$chan} = $modes . ',' . $data; } elsif ($key =~ /needinvite/i) { $data = ''; $i++; while ($keys[$i] !~ /^[\+\-]{1,1}/ && $i <= $#keys) { $data .= $keys[$i] . ' '; $i++; } $i--; chop($data); $needinvite{$chan} = $data; } } else # if flag == '-' { if ($key =~ /freshmeat/i) { $public_fm{$chan} = false; } elsif ($key =~ /slashdot/i) { $public_sd{$chan} = false; } elsif ($key =~ /learning/i) { $learning{$chan} = false; } } } } if ($logging{$chan} eq true) { $logfiles{$chan} = openlogfile($chan); } elsif (defined($logfiles{$chan})) { close($logfiles{$chan}); delete($logfiles{$chan}); } } sub loadchannels { undef(%channels); open(FILE, "$datadir/channels"); while (!eof(FILE)) { $uline = ; if ($uline =~ /^([^ ]*)[ ]?(.*)$/) { $chan = lc($1); if ($chan ne "") { $chanflags{$chan} = $2; processchanflags($chan, $2); } } } close(FILE); console "*** loaded channels\n"; } sub savechannels { open(FILE, ">$datadir/channels"); foreach $key (keys %channels) { $key = lc($key); if ($key ne "") { printf FILE "$key $chanflags{$key}\n"; } } close(FILE); console "*** saved channels\n"; } sub loadusers { open(FILE, "$datadir/users"); while (!eof(FILE)) { $uline = ; if ($uline =~ /(.*):(.*):(.*):(.*)/) { $uhosts{lc($1)} = $2; $uflags{lc($1)} = $3; $uchans{lc($1)} = $4; } } close(FILE); console "*** loaded users\n"; } sub saveusers { open(FILE, ">$datadir/users"); foreach $key (keys %uhosts) { if ($key ne $nick && $uhosts{$key} ne "" && defined($uflags{$key}) && defined($uchans{$key})) { printf FILE "$key:$uhosts{$key}:$uflags{$key}:$uchans{$key}\n"; } } close(FILE); console "*** saved users\n"; } sub loadfacts { open(FILE, "$datadir/facts"); while (!eof(FILE)) { $line = ; if ($line =~ /^([^:]*): (.*) -> (.*)$/) { $facts{$2} = "$1: $3"; } } close(FILE); console "*** loaded facts\n"; } sub savefacts { open(FILE, ">$datadir/facts"); foreach $key (keys %facts) { $facts{$key} =~ /^([^:]*): (.*)$/; printf FILE "%s: %s -> %s\n", $1, $key, $2; } close(FILE); console "*** saved facts\n"; } sub loadnotes { open(FILE, "$datadir/notes"); while (!eof(FILE)) { $line = ; if ($line =~ /^([^ ]*) (.*)$/) { $notes{lc($1)} = $2; } } close(FILE); console "*** loaded notes\n"; } sub savenotes { open(FILE, ">$datadir/notes"); foreach $key (keys %notes) { printf FILE "%s %s\n", $key, $notes{$key}; } close(FILE); console "*** saved notes\n"; } sub loadseen { open(FILE, "$datadir/seen"); while (!eof(FILE)) { $line = ; if ($line =~ /^(.*)\x01(.*)\x01(.*)\x01(.*)\x01(.*)$/) { $seen{lc($1)} = "$2\x01$3\x01$4\x01$5"; } } close(FILE); console "*** loaded seen\n"; } sub saveseen { open(FILE, ">$datadir/seen"); foreach $key (keys %seen) { $seen{$key} =~ /^(.*)\x01(.*)\x01(.*)\x01(.*)$/; printf FILE "%s\x01%s\x01%s\x01%s\x01%s\n", $key, $1, $2, $3, $4; } close(FILE); console "*** saved seen\n"; } sub loadtalkers { open(FILE, "$datadir/talkers"); while (!eof(FILE)) { $line = ; if ($line =~ /^(.*) (.*)$/) { $talkers{lc($1)} = int($2); } } close(FILE); console "*** loaded talkers\n"; } sub savetalkers { open(FILE, ">$datadir/talkers"); foreach $key (keys %talkers) { printf FILE "%s %d\n", $key, $talkers{$key}; } close(FILE); console "*** saved talkers\n"; } sub loadpasswords { open(FILE, "$datadir/passwd"); while (!eof(FILE)) { $line = ; if ($line =~ /^(.*):(.*)$/) { $password{lc($1)} = $2; } } close(FILE); console "*** loaded passwords\n"; } sub savepasswords { open(FILE, ">$datadir/passwd"); foreach $key (keys %password) { printf FILE "%s:%s\n", $key, $password{$key}; } close(FILE); console "*** saved passwords\n"; } sub loadtopiclocks { open(FILE, "$datadir/topiclocks"); while (!eof(FILE)) { $line = ; if ($line =~ /^([^ ]*) ([^ ]*) (.*)$/) { $topiclock{lc($1)} = $2; $oldtopic{lc($1)} = $3; } } close(FILE); console "*** loaded topiclocks\n"; } sub savetopiclocks { open(FILE, ">$datadir/topiclocks"); foreach $key (keys %topiclock) { printf FILE "%s %s %s\n", $key, $topiclock{$key}, $oldtopic{$key}; } close(FILE); console "*** saved topiclocks\n"; } sub checkonehost { @hosts = split (' ', $_[0]); undef $chost; foreach $chost (@hosts) { if ($_[1] =~ $chost) { return true; } } return false; } sub checkhost { if ($_[0] eq $nick) { return $nick; } if ($uhosts{$_[0]} ne "" && checkonehost($uhosts{$_[0]}, $_[1]) eq true) { return $_[0]; } foreach $key (%uhosts) { if ($uhosts{$key} ne "" && checkonehost($uhosts{$key}, $_[1]) eq true) { return $key; } } return false; } sub jumptoserver { console "*** connecting to $server:$port\n"; $sock = konnekt($server, $port); if ($sock ne 0) { sockwrite($sock, "USER $ident dummy dummy :$username"); sockwrite($sock, "NICK $nick"); console "*** done\n"; return true; } return false; } sub serverconnect { $konnekted = false; while ($konnekted eq false) { $servers[$curserver] =~ /([^:]*)[:]?(.*)/; $server = $1; if ($2 eq "") { $port = "6667"; } else { $port = $2; } $curserver++; if ($curserver > $#servers) { $curserver = 0; } $nick = $nicks[$curnick]; $konnekted = jumptoserver(); $rin = fhbits('SOCK'); } } sub httpget { $sockette = konnekt($_[0], $_[1]); sockwrite($sockette, "GET $_[2] HTTP/1.0\n"); undef($data); while (!eof($sockette)) { $line = <$sockette>; $data .= $line; } return $data; } sub httppost { my $len = length ($_[3]); $sockette = konnekt($_[0], $_[1]); sockwrite($sockette, "POST $_[2] HTTP/1.1"); sockwrite($sockette, "Host: $_[0]"); sockwrite($sockette, "Content-type: application/x-www-form-urlencoded"); sockwrite($sockette, "Content-Length: $len\n"); sockwrite($sockette, $_[3]); undef($data); while (!eof($sockette)) { $line = <$sockette>; $data .= $line; } return $data; } sub backend { @file = split (/\n/, $_[0]); undef($data); for ($i = 0; $i <= $#file; $i++) { $line = $file[$i]; if ($line =~ //) { $line = $file[++$i]; if ($line =~ /(.*)<\/title>$/) { $data .= "$1, "; } } } chop($data); chop($data); $data =~ s/\&/\&/g; $data =~ s/\</\</g; $data =~ s/\>/\>/g; $data =~ s/\'/\'/g; $data =~ s/\"/\"/g; return $data; } sub urlencode { $string = $_[0]; $string =~ s/ /\+/g; @chars = split (//, $string); $output = ""; foreach $char (@chars) { if ($char !~ /[a-zA-Z0-9\+ ]/) { $output .= sprintf("%%%02x", ord($char)); } else { $output .= $char; } } return $output; } sub add_handler { $$_[0][0] = $_[1]; } sub privmsg { sockwrite($sock, "PRIVMSG $_[0] :$_[1]"); console("[$_[0]] <$nick> $_[1]\n"); if ($logging{lc($_[0])} eq true) { chanlog($logfiles{lc($_[0])}, "<$nick> $_[1]\n"); } } sub notice { sockwrite($sock, "NOTICE $_[0] :$_[1]"); console("[$_[0]] $nick NOTICE $_[1]\n"); foreach $logfile (values %logfiles) { chanlog($logfile, "[$_[0]] $nick NOTICE $_[1]\n"); } } sub action { sockwrite($sock, "PRIVMSG $_[0] :\x01ACTION $_[1]\x01"); console("[$_[0]] $nick $_[1]\n"); if ($logging{lc($_[0])} eq true) { chanlog($logfiles{lc($_[0])}, "*** $nick $_[1]\n"); } } sub pickrandom { $num = rand($#_ + 1); return $_[$num]; } sub terminate { sockwrite($sock, "QUIT :Terminated"); close($sock); close($jsock); unlink("$ENV{HOME}/.dreambot.pid"); console("*** terminated\n"); if ($log eq true) { printf LOGFILE "[end]\n"; close(LOGFILE); } exit(0); } sub saveall { savechannels(); saveusers(); savefacts(); savenotes(); saveseen(); savetopiclocks(); savetalkers(); savepasswords(); console("*** saved all\n"); alarm 3600; }