X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=src%2FIRC%2FIrcHooks.pl;h=d217fc5cfcd85549c841b17996e573e01057b9a2;hb=7c48b6390b68c68d4364f1f60685703ea7f1e639;hp=8be2723da9f2dfe39238a57a771a6027229afaa3;hpb=4e06c875ee1ada22edc8eecdaeea976f15506d76;p=infobot.git diff --git a/src/IRC/IrcHooks.pl b/src/IRC/IrcHooks.pl index 8be2723..409b818 100644 --- a/src/IRC/IrcHooks.pl +++ b/src/IRC/IrcHooks.pl @@ -4,998 +4,1389 @@ # Version: 20000126 # NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997 # - -if (&IsParam("useStrict")) { use strict; } - -my $nickserv = 0; +use vars qw(%chanconf); # GENERIC. TO COPY. sub on_generic { - my ($self, $event) = @_; - my $nick = $event->nick(); - my $chan = ($event->to)[0]; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $chan = ( $event->to )[0]; &DEBUG("on_generic: nick => '$nick'."); &DEBUG("on_generic: chan => '$chan'."); - foreach ($event->args) { - &DEBUG("on_generic: args => '$_'."); + foreach ( $event->args ) { + &DEBUG("on_generic: args => '$_'."); } } sub on_action { - my ($self, $event) = @_; - my ($nick, @args) = ($event->nick, $event->args); - my $chan = ($event->to)[0]; - - shift @args; + $conn = shift(@_); + my ($event) = @_; + my ( $nick, $args ) = ( $event->nick, $event->args ); + my $chan = ( $event->to )[0]; - if ($chan eq $ident) { - &status("* [$nick] @args"); - } else { - &status("* $nick/$chan @args"); + if ( $chan eq $ident ) { + &status("* [$nick] $args"); + } + else { + &status("* $nick/$chan $args"); } } sub on_chat { - my ($self, $event) = @_; - my $msg = ($event->args)[0]; - my $sock = ($event->to)[0]; - my $nick = $event->nick(); + $conn = shift(@_); + my ($event) = @_; + my $msg = ( $event->args )[0]; + my $sock = ( $event->to )[0]; + my $nick = lc $event->nick(); + + if ( !exists $nuh{$nick} ) { + &DEBUG("chat: nuh{$nick} doesn't exist; trying WHOIS ."); + $conn->whois($nick); + return; + } + + ### set vars that would have been set in hookMsg. + $userHandle = ''; # reset. + $who = lc $nick; + $message = $msg; + $orig{who} = $nick; + $orig{message} = $msg; + $nuh = $nuh{$who}; + $uh = ( split /\!/, $nuh )[1]; + $h = ( split /\@/, $uh )[1]; + $addressed = 1; + $msgType = 'chat'; + + if ( !exists $dcc{'CHATvrfy'}{$nick} ) { + $userHandle = &verifyUser( $who, $nuh ); + my $crypto = $users{$userHandle}{PASS}; + my $success = 0; + + if ( $userHandle eq '_default' ) { + &WARN('DCC CHAT: _default/guest not allowed.'); + return; + } + + ### TODO: prevent users without CRYPT chatting. + if ( !defined $crypto ) { + &TODO('dcc close chat'); + &msg( $who, 'nope, no guest logins allowed...' ); + return; + } + + if ( &ckpasswd( $msg, $crypto ) ) { + + # stolen from eggdrop. + $conn->privmsg( $sock, "Connected to $ident" ); + $conn->privmsg( $sock, + 'Commands start with "." (like ".quit" or ".help")' ); + $conn->privmsg( $sock, + 'Everything else goes out to the party line.' ); + + &dccStatus(2) unless ( exists $sched{'dccStatus'}{RUNNING} ); + + $success++; + + } + else { + &status('DCC CHAT: incorrect pass; closing connection.'); + &DEBUG("chat: sock => '$sock'."); +### $sock->close(); + delete $dcc{'CHAT'}{$nick}; + &FIXME('chat: after closing sock.'); + ### BUG: close seizes bot. why? + } + + if ($success) { + &status("DCC CHAT: user $nick is here!"); + &DCCBroadcast("*** $nick ($uh) joined the party line."); + + $dcc{'CHATvrfy'}{$nick} = $userHandle; + + return if ( $userHandle eq '_default' ); + + &dccsay( $nick, "Flags: $users{$userHandle}{FLAGS}" ); + } + + return; + } - if (!exists $nuh{lc $nick}) { - &DEBUG("chat: nuh{$nick} doesn't exist; hrm should retry."); - &msg($nick, "could not get whois info?"); - return; - } else { - $message = $msg; - $who = lc $nick; - $orig{who} = $nick; - $orig{message} = $msg; - $nuh = $nuh{$who}; - $uh = (split /\!/, $nuh)[1]; - $addressed = 1; - $msgType = 'chat'; - } - - if (!exists $dcc{'CHATvrfy'}{$nick}) { - $userHandle = &verifyUser($who, $nuh); - my $crypto = $userList{$userHandle}{'pass'}; - my $success = 0; - - if (!defined $crypto) { - &DEBUG("chat: no pass required."); - $success++; - } elsif (&ckpasswd($msg, $crypto)) { - $self->privmsg($sock,"Authorized."); - $self->privmsg($sock,"I'll respond as if through /msg and addressed in public. Addition to that, access to 'user' commands will be allowed, like 'die' and 'jump'."); - # hrm... it's stupid to ask for factoids _within_ dcc chat. - # perhaps it should be default to commands, especially - # commands only authorized through DCC CHAT. - &status("DCC CHAT: passwd is ok."); - $success++; - } else { - &status("DCC CHAT: incorrect pass; closing connection."); - &DEBUG("chat: sock => '$sock'."); - $sock->close(); - delete $dcc{'CHAT'}{$nick}; - &DEBUG("chat: after closing sock. FIXME"); - ### BUG: close seizes bot. why? - } - - if ($success) { - &status("DCC CHAT: user $nick is here!"); - &DCCBroadcast("$nick ($uh) has joined the chat arena."); - $dcc{'CHATvrfy'}{$nick} = 1; - if ($userHandle ne "default") { - &dccsay($nick,"Flags: $userList{$userHandle}{'flags'}"); - } - } - - return; - } - - $userHandle = &verifyUser($who, $nuh); &status("$b_red=$b_cyan$who$b_red=$ob $message"); - if ($message =~ s/^\.//) { # dcc chat commands. - ### TODO: make use of &Forker(); here? - &loadMyModule($myModules{'ircdcc'}); - return '$noreply from userD' if (&userDCC() eq $noreply); - $conn->privmsg($dcc{'CHAT'}{$who}, "Invalid command."); - } else { # dcc chat arena. - foreach (keys %{$dcc{'CHAT'}}) { - $conn->privmsg($dcc{'CHAT'}{$_}, "<$who> $orig{message}"); - } + if ( $message =~ s/^\.// ) { # dcc chat commands. + ### TODO: make use of &Forker(); here? + &loadMyModule('UserDCC'); + + &DCCBroadcast( "#$who# $message", 'm' ); + + my $retval = &userDCC(); + return unless ( defined $retval ); + return if ( $retval eq $noreply ); + + $conn->privmsg( $dcc{'CHAT'}{$who}, 'Invalid command.' ); + + } + else { # dcc chat arena. + + foreach ( keys %{ $dcc{'CHAT'} } ) { + $conn->privmsg( $dcc{'CHAT'}{$_}, "<$who> $orig{message}" ); + } } return 'DCC CHAT MESSAGE'; } -sub on_endofmotd { - my ($self) = @_; +# is there isoff? how do we know if someone signs off? +sub on_ison { + $conn = shift(@_); + my ($event) = @_; + my $x1 = ( $event->args )[0]; + my $x2 = ( $event->args )[1]; + $x2 =~ s/\s$//; + + &DEBUG("on_ison: x1 = '$x1', x2 => '$x2'"); +} + +sub on_connected { + $conn = shift(@_); - # what's the following for? - $ident = $param{'ircNick'}; # update IRCStats. - $ircstats{'ConnectTime'} = time(); + $ident = $conn->nick(); + $ircstats{'ConnectTime'} = time(); $ircstats{'ConnectCount'}++; + if ( defined $ircstats{'DisconnectTime'} ) { + $ircstats{'OffTime'} += time() - $ircstats{'DisconnectTime'}; + } + + # first time run. + if ( !exists $users{_default} ) { + &status('!!! First time run... adding _default user.'); + $users{_default}{FLAGS} = 'amrt'; + $users{_default}{HOSTS}{'*!*@*'} = 1; + } - if (&IsParam("wingate")) { - my $file = "$bot_base_dir/$param{'ircUser'}.wingate"; - open(IN, $file); - while () { - chop; - next unless (/^(\S+)\*$/); - push(@wingateBad, $_); - } - close IN; + if ( scalar keys %users < 2 ) { + &status( '!' x 40 ); + &status( +"!!! Ok. Now type '/msg $ident PASS ' to get master access through DCC CHAT." + ); + &status( '!' x 40 ); + } + + # end of first time run. + + if ( &IsChanConf('Wingate') > 0 ) { + my $file = "$bot_base_dir/$param{'ircUser'}.wingate"; + open( IN, $file ); + while () { + chop; + next unless (/^(\S+)\*$/); + push( @wingateBad, $_ ); + } + close IN; } if ($firsttime) { - $conn->schedule(60, \&setupSchedulers, ""); - $firsttime = 0; + &ScheduleThis( 1, 'setupSchedulers' ); + $firsttime = 0; } - if (&IsParam("ircUMode")) { - &status("Attempting change of user modes to $param{'ircUMode'}."); - &rawout("MODE $ident $param{'ircUMode'}"); + if ( &IsParam('ircUMode') ) { + &VERB( "Attempting change of user modes to $param{'ircUMode'}.", 2 ); + if ( $param{'ircUMode'} !~ /^[-+]/ ) { + &WARN('ircUMode had no +- prefix; adding +'); + $param{'ircUMode'} = '+' . $param{'ircUMode'}; + } + + &rawout("MODE $ident $param{'ircUMode'}"); } - &status("End of motd. Now lets join some channels..."); - if (!scalar @joinchan) { - &WARN("joinchan array is empty!!!"); - @joinchan = split /[\t\s]+/, $param{'join_channels'}; + # ok, we're free to do whatever we want now. go for it! + $running = 1; + + # add ourself to notify. + $conn->ison( $conn->nick() ); + + # Q, as on quakenet.org. + if ( &IsParam('Q_pass') ) { + &status('Authing to Q...'); + &rawout( +"PRIVMSG Q\@CServe.quakenet.org :AUTH $param{'Q_user'} $param{'Q_pass'}" + ); } - &joinNextChan(); + &status('End of motd. Now lets join some channels...'); + + #&joinNextChan(); +} + +sub on_endofwho { + $conn = shift(@_); + my ($event) = @_; + + # &DEBUG("endofwho: chan => $chan"); + $chan ||= ( $event->args )[1]; + + # &DEBUG("endofwho: chan => $chan"); + + if ( exists $cache{countryStats} ) { + &do_countrystats(); + } } sub on_dcc { - my ($self, $event) = @_; - my $type = uc( ($event->args)[1] ); - my $nick = $event->nick(); + $conn = shift(@_); + my ($event) = @_; + my $type = uc( ( $event->args )[1] ); + my $nick = lc $event->nick(); + + &status("on_dcc type=$type nick=$nick sock=$sock"); # pity Net::IRC doesn't store nuh. Here's a hack :) - $self->whois($nick); - $type ||= "???"; - - if ($type eq 'SEND') { # GET for us. - # incoming DCC SEND. we're receiving a file. - my $get = ($event->args)[2]; - open(DCCGET,">$get"); - - $self->new_get($nick, - ($event->args)[2], - ($event->args)[3], - ($event->args)[4], - ($event->args)[5], - \*DCCGET - ); - } elsif ($type eq 'GET') { # SEND for us? - &DEBUG("starting get."); - $self->new_send($event->args); - } elsif ($type eq 'CHAT') { - &DEBUG("starting chat."); - $self->new_chat($event); - } else { - &WARN("${b_green}DCC $type$ob (1)"); + if ( !exists $nuh{ lc $nick } ) { + $conn->whois($nick); + $nuh{$nick} = 'GETTING-NOW'; # trying. + } + $type ||= '???'; + + if ( $type eq 'SEND' ) { # GET for us. + # incoming DCC SEND. we're receiving a file. + my $get = ( $event->args )[2]; + &status( + "DCC: not Initializing GET from $nick to '$param{tempDir}/$get'"); + + # FIXME: do we want to get anything? + return; + + #open(DCCGET,">$param{tempDir}/$get"); + #$conn->new_get($event, \*DCCGET); + + } + elsif ( $type eq 'GET' ) { # SEND for us? + &status("DCC: not Initializing SEND for $nick."); + + # FIXME: do we want to do anything? + return; + $conn->new_send( $event->args ); + + } + elsif ( $type eq 'CHAT' ) { + &status("DCC: Initializing CHAT for $nick."); + $conn->new_chat($event); + + # $conn->new_chat(1, $nick, $event->host); + + } + else { + &WARN("${b_green}DCC $type$ob (1)"); } } sub on_dcc_close { - my ($self, $event) = @_; - my $nick = $event->nick(); - my $sock = ($event->to)[0]; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $sock = ( $event->to )[0]; # DCC CHAT close on fork exit workaround. - return if ($bot_pid != $$); - - &DEBUG("dcc_close: nick => '$nick'."); + if ( $bot_pid != $$ ) { + &WARN('run-away fork; exiting.'); + &delForked($forker); + } - if (exists $dcc{'SEND'}{$nick} and -f "$param{tempDir}/$nick.txt") { - &status("${b_green}DCC SEND$ob close from $b_cyan$nick$ob"); + if ( exists $dcc{'SEND'}{$nick} and -f "$param{tempDir}/$nick.txt" ) { + &status("${b_green}DCC SEND$ob close from $b_cyan$nick$ob"); - &status("dcc_close: purging $nick.txt from Debian.pl"); - unlink "$param{tempDir}/$nick.txt"; + &status("dcc_close: purging DCC send $nick.txt"); + unlink "$param{tempDir}/$nick.txt"; - delete $dcc{'SEND'}{$nick}; - } elsif (exists $dcc{'CHAT'}{$nick} and $dcc{'CHAT'}{$nick} eq $sock) { - &status("${b_green}DCC CHAT$ob close from $b_cyan$nick$ob"); - delete $dcc{'CHAT'}{$nick}; - } else { - &status("${b_green}DCC$ob UNKNOWN close from $b_cyan$nick$ob (2)"); + delete $dcc{'SEND'}{$nick}; + } + elsif ( exists $dcc{'CHAT'}{$nick} and $dcc{'CHAT'}{$nick} eq $sock ) { + &status("${b_green}DCC CHAT$ob close from $b_cyan$nick$ob"); + delete $dcc{'CHAT'}{$nick}; + delete $dcc{'CHATvrfy'}{$nick}; + } + else { + &status("${b_green}DCC$ob UNKNOWN close from $b_cyan$nick$ob (2)"); } } sub on_dcc_open { - my ($self, $event) = @_; - my $type = uc( ($event->args)[0] ); - my $nick = $event->nick(); - my $sock = ($event->to)[0]; + $conn = shift(@_); + my ($event) = @_; + my $type = uc( ( $event->args )[0] ); + my $nick = lc $event->nick(); + my $sock = ( $event->to )[0]; + + &status("on_dcc_open type=$type nick=$nick sock=$sock"); + $msgType = 'chat'; + $type ||= '???'; + ### BUG: who is set to bot's nick? + + # lets do it. + if ( $type eq 'SEND' ) { + &status("${b_green}DCC lGET$ob established with $b_cyan$nick$ob"); + + } + elsif ( $type eq 'CHAT' ) { + + # very cheap hack. + ### TODO: run ScheduleThis inside on_dcc_open_chat recursively + ### 1,3,5,10 seconds then fail. + if ( $nuh{$nick} eq 'GETTING-NOW' ) { + &ScheduleThis( 3 / 60, 'on_dcc_open_chat', $nick, $sock ); + } + else { + on_dcc_open_chat( undef, $nick, $sock ); + } + + } + elsif ( $type eq 'SEND' ) { + &status('Starting DCC receive.'); + foreach ( $event->args ) { + &status(" => '$_'."); + } + + } + else { + &WARN("${b_green}DCC $type$ob (3)"); + } +} + +# really custom sub to get NUH since Net::IRC doesn't appear to support +# it. +sub on_dcc_open_chat { + my ( undef, $nick, $sock ) = @_; + + if ( $nuh{$nick} eq 'GETTING-NOW' ) { + &FIXME("getting nuh for $nick failed."); + return; + } + + &status( +"${b_green}DCC CHAT$ob established with $b_cyan$nick$ob $b_yellow($ob$nuh{$nick}$b_yellow)$ob" + ); + + &verifyUser( $nick, $nuh{ lc $nick } ); + + if ( !exists $users{$userHandle}{HOSTS} ) { + &performStrictReply( + 'you have no hosts defined in my user file; rejecting.'); + $sock->close(); + return; + } + + my $crypto = $users{$userHandle}{PASS}; + $dcc{'CHAT'}{$nick} = $sock; - $type ||= "???"; + # TODO: don't make DCC CHAT established in the first place. + if ( $userHandle eq '_default' ) { + &dccsay( $nick, '_default/guest not allowed' ); + $sock->close(); + return; + } - if ($type eq 'SEND') { - &status("${b_green}DCC lGET$ob established with $b_cyan$nick$ob"); - } elsif ($type eq 'CHAT') { - &status("${b_green}DCC CHAT$ob established with $b_cyan$nick$ob $b_yellow($ob$nuh{$nick}$b_yellow)$ob"); - $userHandle = &verifyUser($nick, $nuh{lc $nick}); - my $crypto = $userList{$userHandle}{'pass'}; - $dcc{'CHAT'}{$nick} = $sock; + if ( defined $crypto ) { + &status( "DCC CHAT: going to use $nick\'s crypt." ); + &dccsay( $nick, 'Enter your password.' ); + } + else { - if (defined $crypto) { - &dccsay($nick,"Enter Password, $userHandle."); - } else { - &dccsay($nick,"Welcome to blootbot DCC CHAT interface, $userHandle."); - } - } elsif ($type eq 'SEND') { - &DEBUG("Starting DCC receive."); - foreach ($event->args) { - &DEBUG(" => '$_'."); - } - } else { - &WARN("${b_green}DCC $type$ob (3)"); + # &dccsay($nick,"Welcome to infobot DCC CHAT interface, $userHandle."); } } sub on_disconnect { - my ($self, $event) = @_; - my $from = $event->from(); - my $what = ($event->args)[0]; - - &status("disconnect from $from ($what)."); - $ircstats{'DisconnectTime'} = time(); - $ircstats{'DisconnectReason'} = $what; + $conn = shift(@_); + my ($event) = @_; + my $from = $event->from(); + my $what = ( $event->args )[0]; + my $mynick = $conn->nick(); + + &status("$mynick disconnect from $from ($what)."); + $ircstats{'DisconnectTime'} = time(); + $ircstats{'DisconnectReason'} = $what; $ircstats{'DisconnectCount'}++; + $ircstats{'TotalTime'} += time() - $ircstats{'ConnectTime'} + if ( $ircstats{'ConnectTime'} ); # clear any variables on reconnection. $nickserv = 0; &clearIRCVars(); - if (!$self->connect()) { - &WARN("not connected? help me. gonna call ircCheck() in 1800s"); - $conn->schedule(1800, \&ircCheck(), ""); + + if ( !defined $conn ) { + &WARN('on_disconnect: self is undefined! WTF'); + &DEBUG('running function irc... lets hope this works.'); + &irc(); + return; } + + &WARN('scheduling call ircCheck() in 60s'); + &clearIRCVars(); + &ScheduleThis( 1, 'ircCheck' ); } sub on_endofnames { - my ($self, $event) = @_; - my $chan = ($event->args)[1]; - - if (exists $jointime{$chan}) { - my $delta_time = sprintf("%.03f", &gettimeofday() - $jointime{$chan}); - $delta_time = 0 if ($delta_time < 0); - - &status("$b_blue$chan$ob: sync in ${delta_time}s."); + $conn = shift(@_); + my ($event) = @_; + my $chan = ( $event->args )[1]; + + # sync time should be done in on_endofwho like in BitchX + if ( exists $cache{jointime}{$chan} ) { + my $delta_time = + sprintf( '%.03f', &timedelta( $cache{jointime}{$chan} ) ); + $delta_time = 0 if ( $delta_time <= 0 ); + if ( $delta_time > 100 ) { + &WARN("endofnames: delta_time > 100 ($delta_time)"); + } + + &status("$b_blue$chan$ob: sync in ${delta_time}s."); } - rawout("MODE $chan"); + $conn->mode($chan); my $txt; my @array; - foreach ("o","v","") { - my $count = scalar(keys %{$channels{$chan}{$_}}); - next unless ($count); + foreach ( 'o', 'v', '' ) { + my $count = scalar( keys %{ $channels{$chan}{$_} } ); + next unless ($count); - $txt = "total" if ($_ eq ""); - $txt = "voice" if ($_ eq "v"); - $txt = "ops" if ($_ eq "o"); + $txt = 'total' if ( $_ eq '' ); + $txt = 'voice' if ( $_ eq 'v' ); + $txt = 'ops' if ( $_ eq 'o' ); - push(@array, "$count $txt"); + push( @array, "$count $txt" ); } - my $chanstats = join(' || ', @array); + my $chanstats = join( ' || ', @array ); &status("$b_blue$chan$ob: [$chanstats]"); - if (scalar @joinchan) { # remaining channels to join. - &joinNextChan(); - } else { - ### chanserv support. - ### TODO: what if we rejoin a channel.. need to set a var that - ### we've done the request-for-ops-on-join. - return unless (&IsParam("chanServ_ops")); - return unless ($nickserv); - - my @chans = split(/[\s\t]+/, $param{'chanServ_ops'}); - - ### TODO: since this function has a chan arg, why don't we use - ### that instead of doing the following? - foreach $chan (keys %channels) { - next unless (grep /^$chan$/i, @chans); - - if (!exists $channels{$chan}{'o'}{$ident}) { - &status("ChanServ ==> Requesting ops for $chan."); - &rawout("PRIVMSG ChanServ :OP $chan $ident"); - } - } - } + &chanServCheck($chan); + # schedule used to solve ircu (OPN) 'target too fast' problems. + $conn->schedule( 5, sub { &joinNextChan(); } ); } sub on_init { - my ($self, $event) = @_; - my (@args) = ($event->args); + $conn = shift(@_); + my ($event) = @_; + my (@args) = ( $event->args ); shift @args; &status("@args"); } sub on_invite { - my ($self, $event) = @_; - my $chan = ($event->args)[0]; + $conn = shift(@_); + my ($event) = @_; + my $chan = lc( ( $event->args )[0] ); my $nick = $event->nick; - &DEBUG("on_invite: chan => '$chan', nick => '$nick'."); + if ( $nick =~ /^\Q$ident\E$/ ) { + &DEBUG('on_invite: self invite.'); + return; + } + + ### TODO: join key. + if ( exists $chanconf{$chan} ) { - # chan + possible_key. - ### do we need to know the key if we're invited??? - ### grep the channel list? - foreach (split /[\s\t]+/, $param{'join_channels'}) { - next unless /^\Q$chan\E(,\S+)?$/i; - s/,/ /; + # it's still buggy :/ + if ( &validChan($chan) ) { + &msg( $who, "i'm already in \002$chan\002." ); - next if ($nick =~ /^\Q$ident\E$/); - if (&validChan($chan)) { - &msg($who, "i'm already in \002$chan\002."); - next; - } + # return; + } - &status("invited to $b_blue$_$ob by $b_cyan$who$ob"); - &joinchan($self, $_); + &status("invited to $b_blue$chan$ob by $b_cyan$nick$ob"); + &joinchan($chan); } } sub on_join { - my ($self, $event) = @_; - my ($user,$host) = split(/\@/, $event->userhost); - $chan = lc( ($event->to)[0] ); # CASING!!!! - $who = $event->nick(); + $conn = shift(@_); + my ($event) = @_; + my ( $user, $host ) = split( /\@/, $event->userhost ); + $chan = lc( ( $event->to )[0] ); # CASING!!!! + $who = $event->nick(); + $msgType = 'public'; + my $i = scalar( keys %{ $channels{$chan} } ); + my $j = $cache{maxpeeps}{$chan} || 0; + + if ( !&IsParam('noSHM') + && time() > ( $sched{shmFlush}{TIME} || time() ) + 3600 ) + { + &DEBUG('looks like schedulers died somewhere... restarting...'); + &setupSchedulers(); + } $chanstats{$chan}{'Join'}++; - $userstats{lc $who}{'Join'} = time() if (&IsParam("seenStats")); + $userstats{ lc $who }{'Join'} = time() if ( &IsChanConf('seenStats') > 0 ); + $cache{maxpeeps}{$chan} = $i if ( $i > $j ); - &joinfloodCheck($who, $chan, $event->userhost); + &joinfloodCheck( $who, $chan, $event->userhost ); # netjoin detection. my $netsplit = 0; - if (exists $netsplit{lc $who}) { - delete $netsplit{lc $who}; - $netsplit = 1; + if ( exists $netsplit{ lc $who } ) { + delete $netsplit{ lc $who }; + $netsplit = 1; + + if ( !scalar keys %netsplit ) { + &DEBUG('on_join: netsplit hash is now empty!'); + undef %netsplitservers; + &netsplitCheck(); # any point in running this? + &chanlimitCheck(); + } } - if ($netsplit and !$netsplittime) { - &status("ok.... re-running chanlimitCheck in 60."); - $conn->schedule(60, sub { - &chanlimitCheck(); - $netsplittime = undef; - } ); - - $netsplittime = time(); + if ( $netsplit and !exists $cache{netsplit} ) { + &VERB('on_join: ok.... re-running chanlimitCheck in 60.', 2); + $conn->schedule( + 60, + sub { + &chanlimitCheck(); + delete $cache{netsplit}; + } + ); + + $cache{netsplit} = time(); } # how to tell if there's a netjoin??? - my $netsplitstr = ""; - $netsplitstr = " $b_yellow\[${ob}NETSPLIT VICTIM$b_yellow]$ob" if ($netsplit); - &status(">>> join/$b_blue$chan$ob $b_cyan$who$ob $b_yellow($ob$user\@$host$b_yellow)$ob$netsplitstr"); + my $netsplitstr = ''; + $netsplitstr = " $b_yellow\[${ob}NETSPLIT VICTIM$b_yellow]$ob" + if ($netsplit); + &status( +">>> join/$b_blue$chan$ob $b_cyan$who$ob $b_yellow($ob$user\@$host$b_yellow)$ob$netsplitstr" + ); $channels{$chan}{''}{$who}++; - $nuh{lc $who} = $who."!".$user."\@".$host unless (exists $nuh{lc $who}); + $nuh = $who . '!' . $user . '@' . $host; + $nuh{ lc $who } = $nuh unless ( exists $nuh{ lc $who } ); - ### ROOTWARN: - &rootWarn($who,$user,$host,$chan) - if (&IsParam("rootWarn") && - $user =~ /^r(oo|ew|00)t$/i && - $channels{$chan}{'o'}{$ident}); + ### on-join bans. + my @bans; + push( @bans, keys %{ $bans{$chan} } ) if ( exists $bans{$chan} ); + push( @bans, keys %{ $bans{'*'} } ) if ( exists $bans{'*'} ); + + foreach (@bans) { + my $ban = $_; + s/\?/./g; + s/\*/\\S*/g; + my $mask = $_; + next unless ( $nuh =~ /^$mask$/i ); + + ### TODO: check $channels{$chan}{'b'} if ban already exists. + foreach ( keys %{ $channels{$chan}{'b'} } ) { + &DEBUG(" bans_on_chan($chan) => $_"); + } - # used to determine sync time. - if ($who =~ /^$ident$/i) { - if (defined( my $whojoin = $joinverb{$chan} )) { - &msg($chan, "Okay, I'm here. (courtesy of $whojoin)"); - delete $joinverb{$chan}; - } + my $reason = 'no reason'; + foreach ( $chan, '*' ) { + next unless ( exists $bans{$_} ); + next unless ( exists $bans{$_}{$ban} ); - ### TODO: move this to &joinchan()? - $jointime{$chan} = &gettimeofday(); - rawout("WHO $chan"); - } else { - ### TODO: this may go wild on a netjoin :) - ### WINGATE: - &wingateCheck(); + my @array = @{ $bans{$_}{$ban} }; + + $reason = $array[4] if ( $array[4] ); + last; + } + + &ban( $ban, $chan ); + &kick( $who, $chan, $reason ); + + last; } -} -sub on_kick { - my ($self, $event) = @_; - my ($chan,$reason) = $event->args; - my $kicker = $event->nick; - my $kickee = ($event->to)[0]; - my $uh = $event->userhost(); + # no need to go further. + return if ($netsplit); + + # who == bot. + if ( $who =~ /^\Q$ident\E$/i ) { + if ( defined( my $whojoin = $cache{join}{$chan} ) ) { + &msg( $chan, "Okay, I'm here. (courtesy of $whojoin)" ); + delete $cache{join}{$chan}; + &joinNextChan(); # hack. + } - &status(">>> kick/$b_blue$chan$ob [$b$kickee!$uh$ob] by $b_cyan$kicker$ob $b_yellow($ob$reason$b_yellow)$ob"); + ### TODO: move this to &joinchan()? + $cache{jointime}{$chan} = &timeget(); + $conn->who($chan); + + return; + } + + ### ROOTWARN: + &rootWarn( $who, $user, $host, $chan ) + if ( &IsChanConf('RootWarn') > 0 + && $user =~ /^~?r(oo|ew|00)t$/i ); + + ### emit a message based on who just joined + &onjoin( $who, $user, $host, $chan ) if ( &IsChanConf('OnJoin') > 0 ); + + ### NEWS: + if ( &IsChanConf('News') > 0 && &IsChanConf('newsKeepRead') > 0 ) { + if ( !&loadMyModule('News') ) { # just in case. + &DEBUG('could not load news.'); + } + else { + &News::latest($chan); + } + } - $chan = lc $chan; # forgot about this, found by xsdg, 20001229. + ### botmail: + if ( &IsChanConf('botmail') > 0 ) { + &botmail::check( lc $who ); + } + + ### wingate: + &wingateCheck(); +} + +sub on_kick { + $conn = shift(@_); + my ($event) = @_; + my ( $chan, $reason ) = $event->args; + my $kicker = $event->nick; + my $kickee = ( $event->to )[0]; + my $uh = $event->userhost(); + + &status( +">>> kick/$b_blue$chan$ob [$b$kickee!$uh$ob] by $b_cyan$kicker$ob $b_yellow($ob$reason$b_yellow)$ob" + ); + + $chan = lc $chan; # forgot about this, found by xsdg, 20001229. $chanstats{$chan}{'Kick'}++; - if ($kickee eq $ident) { - &clearChanVars($chan); + if ( $kickee eq $ident ) { + &clearChanVars($chan); - &status("SELF attempting to rejoin lost channel $chan"); - &joinchan($chan); - } else { - &DeleteUserInfo($kickee,$chan); + &status("SELF attempting to rejoin lost channel $chan"); + &joinchan($chan); + } + else { + &delUserInfo( $kickee, $chan ); } } sub on_mode { - my ($self, $event) = @_; - my ($user, $host) = split(/\@/, $event->userhost); + $conn = shift(@_); + my ($event) = @_; + my ( $user, $host ) = split( /\@/, $event->userhost ); my @args = $event->args(); my $nick = $event->nick(); - my $chan = ($event->to)[0]; + $chan = ( $event->to )[0]; - $args[0] =~ s/\s$//; + # last element is empty... so nuke it. + pop @args while ( $args[$#args] eq '' ); - if ($nick eq $chan) { # UMODE - &status(">>> mode $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob"); - } else { # MODE - &status(">>> mode/$b_blue$chan$ob $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob"); - &hookMode($chan, @args); + if ( $nick eq $chan ) { # UMODE + &status( + ">>> mode $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob"); + } + else { # MODE + &status( +">>> mode/$b_blue$chan$ob $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob" + ); + &hookMode( $nick, @args ); } } sub on_modeis { - my ($self, $event) = @_; + $conn = shift(@_); + my ($event) = @_; + my ( $myself, undef, @args ) = $event->args(); my $nick = $event->nick(); - my ($myself,$chan,@args) = $event->args(); + $chan = ( $event->args() )[1]; - &hookMode(lc $chan, @args); # CASING. + &hookMode( $nick, @args ); } sub on_msg { - my ($self, $event) = @_; - my $nick = $event->nick; - my $chan = lc ( ($event->to)[0] ); # CASING. - my $msg = ($event->args)[0]; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick; + my $msg = ( $event->args )[0]; - ($user,$host) = split(/\@/, $event->userhost); - $uh = $event->userhost(); - $nuh = $nick."!".$uh; + ( $user, $host ) = split( /\@/, $event->userhost ); + $uh = $event->userhost(); + $nuh = $nick . '!' . $uh; + $msgtime = time(); + $h = $host; + + if ( $nick eq $ident ) { # hopefully ourselves. + if ( $msg eq 'TEST' ) { + &status("IRCTEST: Yes, we're alive."); + delete $cache{connect}; + return; + } + } - &hookMsg('private', $chan, $nick, $msg); + &hookMsg( 'private', undef, $nick, $msg ); + $who = ''; + $chan = ''; + $msgType = ''; } sub on_names { - my ($self, $event) = @_; - my @args = $event->args; - my $chan = lc $args[2]; # CASING, the last of them! - - foreach (split / /, @args[3..$#args]) { - $channels{$chan}{'o'}{$_}++ if s/\@//; - $channels{$chan}{'v'}{$_}++ if s/\+//; - $channels{$chan}{''}{$_}++; + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + my $chan = lc $args[2]; # CASING, the last of them! + + foreach ( split / /, @args[ 3 .. $#args ] ) { + $channels{$chan}{'o'}{$_}++ if s/\@//; + $channels{$chan}{'v'}{$_}++ if s/\+//; + $channels{$chan}{''}{$_}++; } } sub on_nick { - my ($self, $event) = @_; - my $nick = $event->nick(); - my $newnick = ($event->args)[0]; - - if (exists $netsplit{lc $newnick}) { - &status("Netsplit: $newnick/$nick came back from netsplit and changed to original nick! removing from hash."); - delete $netsplit{lc $newnick}; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $newnick = ( $event->args )[0]; + + if ( exists $netsplit{ lc $newnick } ) { + &status( +"Netsplit: $newnick/$nick came back from netsplit and changed to original nick! removing from hash." + ); + delete $netsplit{ lc $newnick }; + &netsplitCheck() if ( time() != $sched{netsplitCheck}{TIME} ); } - my ($chan,$mode); - foreach $chan (keys %channels) { - foreach $mode (keys %{$channels{$chan}}) { - next unless (exists $channels{$chan}{$mode}{$nick}); + my ( $chan, $mode ); + foreach $chan ( keys %channels ) { + foreach $mode ( keys %{ $channels{$chan} } ) { + next unless ( exists $channels{$chan}{$mode}{$nick} ); - $channels{$chan}{$mode}{$newnick} = $channels{$chan}{$mode}{$nick}; - } + $channels{$chan}{$mode}{$newnick} = $channels{$chan}{$mode}{$nick}; + } } - &DeleteUserInfo($nick,keys %channels); - $nuh{lc $newnick} = $nuh{lc $nick}; - delete $nuh{lc $nick}; - # successful self-nick change. - if ($nick eq $ident) { - &status(">>> I materialized into $b_green$newnick$ob from $nick"); - $ident = $newnick; - } else { - &status(">>> $b_cyan$nick$ob materializes into $b_green$newnick$ob"); + # TODO: do %flood* aswell. + + &delUserInfo( $nick, keys %channels ); + $nuh{ lc $newnick } = $nuh{ lc $nick }; + delete $nuh{ lc $nick }; + + if ( $nick eq $conn->nick() ) { + &status(">>> I materialized into $b_green$newnick$ob from $nick"); + $ident = $newnick; + $conn->nick($newnick); + } + else { + &status(">>> $b_cyan$nick$ob materializes into $b_green$newnick$ob"); + my $mynick = $conn->nick(); + if ( $nick =~ /^\Q$mynick\E$/i ) { + &getNickInUse(); + } } } sub on_nick_taken { - my ($self) = @_; - my $nick = $self->nick; - my $newnick = substr($nick,0,7)."-"; + $conn = shift(@_); + my $nick = $conn->nick(); + + #my $newnick = $nick . int(rand 10); + my $newnick = $nick . '_'; + + &DEBUG("on_nick_taken: nick => $nick"); + + &status("nick taken ($nick); preparing nick change."); + + $conn->whois($nick); - &status("nick taken; changing to temporary nick."); + #$conn->schedule(5, sub { + &status("nick taken; changing to temporary nick ($nick -> $newnick)."); &nick($newnick); - &getNickInUse(1); + + #} ); } sub on_notice { - my ($self, $event) = @_; - my $nick = $event->nick(); - my $chan = ($event->to)[0]; - my $args = ($event->args)[0]; - - if ($nick =~ /^NickServ$/i) { # nickserv. - &status("NickServ: <== '$args'"); - - if ($args =~ /^This nickname is registered/i) { - &status("nickserv told us to register; doing it."); - if (&IsParam("nickServ_pass")) { - &status("NickServ: ==> Identifying."); - &rawout("PRIVMSG NickServ :IDENTIFY $param{'nickServ_pass'}"); - return; - } else { - &status("We can't tell nickserv a passwd ;("); - } - } - - # password accepted. - if ($args =~ /^Password a/i) { - $nickserv++; - } - } elsif ($nick =~ /^ChanServ$/i) { # chanserv. - &status("ChanServ: <== '$args'."); - } else { - if ($chan =~ /^$mask{chan}$/) { # channel notice. - &status("-$nick/$chan- $args"); - } else { - $server = $nick unless (defined $server); - &status("-$nick- $args"); # private or server notice. - } + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $chan = ( $event->to )[0]; + my $args = ( $event->args )[0]; + + if ( $nick =~ /^NickServ$/i ) { # nickserv. + &status("NickServ: <== '$args'"); + + my $check = 0; + $check++ if ( $args =~ /^This nickname is registered/i ); + $check++ if ( $args =~ /nickname.*owned/i ); + + if ($check) { + &status('nickserv told us to register; doing it.'); + + if ( &IsParam('nickServ_pass') ) { + &status('NickServ: ==> Identifying.'); + &rawout("PRIVMSG NickServ :IDENTIFY $param{'nickServ_pass'}"); + return; + } + else { + &status("We can't tell nickserv a passwd ;("); + } + } + + # password accepted. + if ( $args =~ /^Password a/i ) { + my $done = 0; + + foreach ( &ChanConfList('chanServ_ops') ) { + next unless &chanServCheck($_); + next if ($done); + &DEBUG( + 'nickserv activated or restarted; doing chanserv check.'); + $done++; + } + + $nickserv++; + } + + } + elsif ( $nick =~ /^ChanServ$/i ) { # chanserv. + &status("ChanServ: <== '$args'."); + + } + else { + if ( $chan =~ /^$mask{chan}$/ ) { # channel notice. + &status("-$nick/$chan- $args"); + } + else { + $server = $nick unless ( defined $server ); + &status("-$nick- $args"); # private or server notice. + } } } sub on_other { - my ($self, $event) = @_; - my $chan = ($event->to)[0]; - my $nick = $event->nick; + $conn = shift(@_); + my ($event) = @_; + my $chan = ( $event->to )[0]; + my $nick = $event->nick; - &status("!!! other called."); + &status('!!! other called.'); &status("!!! $event->args"); } sub on_part { - my ($self, $event) = @_; - my $chan = lc( ($event->to)[0] ); # CASING!!! - my $nick = $event->nick; + $conn = shift(@_); + my ($event) = @_; + $chan = lc( ( $event->to )[0] ); # CASING!!! + my $mynick = $conn->nick(); + my $nick = $event->nick; my $userhost = $event->userhost; + $who = $nick; + $msgType = 'public'; + + if ( !exists $channels{$chan} ) { + &DEBUG("on_part: found out $mynick is on $chan!"); + $channels{$chan} = 1; + } - if (!exists $floodjoin{$chan}{$nick}{Time}) { - &WARN("on_part: $nick/$chan not in floodjoin hash?"); - } else { - delete $floodjoin{$chan}{$nick}; + if ( exists $floodjoin{$chan}{$nick}{Time} ) { + delete $floodjoin{$chan}{$nick}; } $chanstats{$chan}{'Part'}++; - &DeleteUserInfo($nick,$chan); - &clearChanVars($chan) if ($nick eq $ident); - if (!&IsNickInAnyChan($nick) and &IsParam("seenStats")) { - delete $userstats{lc $nick}; + &delUserInfo( $nick, $chan ); + if ( $nick eq $ident ) { + &clearChanVars($chan); + } + + if ( !&IsNickInAnyChan($nick) and &IsChanConf('seenStats') > 0 ) { + delete $userstats{ lc $nick }; } - &status(">>> part/$b_blue$chan$ob $b_cyan$nick$ob $b_yellow($ob$userhost$b_yellow)$ob"); + &status( +">>> part/$b_blue$chan$ob $b_cyan$nick$ob $b_yellow($ob$userhost$b_yellow)$ob" + ); } sub on_ping { - my ($self, $event) = @_; + $conn = shift(@_); + my ($event) = @_; my $nick = $event->nick; - $self->ctcp_reply($nick, join(' ', ($event->args))); - &status(">>> ${b_green}CTCP PING$ob request from $b_cyan$nick$ob received."); + $conn->ctcp_reply( $nick, join( ' ', ( $event->args ) ) ); + &status( + ">>> ${b_green}CTCP PING$ob request from $b_cyan$nick$ob received."); } sub on_ping_reply { - my ($self, $event) = @_; - my $nick = $event->nick; - my $lag = time() - ($event->args)[1]; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick; + my $t = ( $event->args )[1]; + if ( !defined $t ) { + &WARN('on_ping_reply: t == undefined.'); + return; + } + + my $lag = time() - $t; &status(">>> ${b_green}CTCP PING$ob reply from $b_cyan$nick$ob: $lag sec."); } sub on_public { - my ($self, $event) = @_; - my $msg = ($event->args)[0]; - my $chan = lc( ($event->to)[0] ); # CASING. + $conn = shift(@_); + my ($event) = @_; + my $msg = ( $event->args )[0]; + $chan = lc( ( $event->to )[0] ); # CASING. my $nick = $event->nick; + $who = $nick; $uh = $event->userhost(); - $nuh = $nick."!".$uh; - ($user,$host) = split(/\@/, $uh); + $nuh = $nick . '!' . $uh; + $msgType = 'public'; - if ($$ != $bot_pid) { - &ERROR("SHOULD NEVER HAPPEN."); - exit(0); - } + # TODO: move this out of hookMsg to here? + ( $user, $host ) = split( /\@/, $uh ); + $h = $host; - ### DEBUGGING. - if ($statcount < 200) { - foreach $chan (grep /[A-Z]/, keys %channels) { - &DEBUG("leak: chan => '$chan'."); - my ($i,$j); - foreach $i (keys %{$channels{$chan}}) { - foreach (keys %{$channels{$chan}{$i}}) { - &DEBUG("leak: \$channels{$chan}{$i}{$_} ..."); - } - } - } + # rare case should this happen - catch it just in case. + if ( $bot_pid != $$ ) { + &ERROR('run-away fork; exiting.'); + &delForked($forker); } - $msgtime = time(); $lastWho{$chan} = $nick; ### TODO: use $nick or lc $nick? - if (&IsParam("seenStats")) { - $userstats{lc $nick}{'Count'}++; - $userstats{lc $nick}{'Time'} = time(); + if ( &IsChanConf('seenStats') > 0 ) { + $userstats{ lc $nick }{'Count'}++; + $userstats{ lc $nick }{'Time'} = time(); } -# if (&IsParam("hehCounter")) { -# #... -# } + # cache it. + my $time = time(); + if ( !$cache{ircTextCounters} ) { + &DEBUG('caching ircTextCounters for first time.'); + my @str = split( /\s+/, &getChanConf('ircTextCounters') ); + for (@str) { $_ = quotemeta($_); } + $cache{ircTextCounters} = join( '|', @str ); + } - &hookMsg('public', $chan, $nick, $msg); + my $str = $cache{ircTextCounters}; + if ( $str && $msg =~ /^($str)[\s!\.]?$/i ) { + my $x = $1; + + &VERB( "textcounters: $x matched for $who", 2 ); + my $c = $chan || 'PRIVATE'; + + # better to do 'counter=counter+1'. + # but that will avoid time check. + my ( $v, $t ) = &sqlSelect( + 'stats', + 'counter,time', + { + nick => $who, + type => $x, + channel => $c, + } + ); + $v++; + + # don't allow ppl to cheat the stats :-) + if ( ( defined $t && $time - $t > 60 ) or ( !defined $t ) ) { + &sqlSet( + 'stats', + { + 'nick' => $who, + 'type' => $x, + 'channel' => $c, + }, + { + time => $time, + counter => $v, + } + ); + } + } + + &hookMsg( 'public', $chan, $nick, $msg ); $chanstats{$chan}{'PublicMsg'}++; + $who = ''; + $chan = ''; + $msgType = ''; } sub on_quit { - my ($self, $event) = @_; - my $nick = $event->nick(); - my $reason = ($event->args)[0]; - - foreach (keys %channels) { - # fixes inconsistent chanstats bug #1. - next unless (&IsNickInChan($nick,$_)); - $chanstats{$_}{'SignOff'}++; + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $reason = ( $event->args )[0]; + + # hack for ICC. + $msgType = 'public'; + $who = $nick; +### $chan = $reason; # no. + + my $count = 0; + foreach ( grep !/^_default$/, keys %channels ) { + + # fixes inconsistent chanstats bug #1. + if ( !&IsNickInChan( $nick, $_ ) ) { + $count++; + next; + } + $chanstats{$_}{'SignOff'}++; } - &DeleteUserInfo($nick, keys %channels); - if (exists $nuh{lc $nick}) { - delete $nuh{lc $nick}; - } else { - &DEBUG("on_quit: nuh{lc $nick} does not exist! FIXME"); + + if ( $count == scalar keys %channels ) { + &DEBUG("on_quit: nick $nick was not found in any chan."); } - delete $userstats{lc $nick} if (&IsParam("seenStats")); # should fix chanstats inconsistencies bug #2. - if ($reason=~/^($mask{host})\s($mask{host})$/) { # netsplit. - $reason = "NETSPLIT: $1 <=> $2"; + if ( $reason =~ /^($mask{host})\s($mask{host})$/ ) { # netsplit. + $reason = "NETSPLIT: $1 <=> $2"; + + # chanlimit code. + foreach $chan ( &getNickInChans($nick) ) { + next unless ( &IsChanConf('chanlimitcheck') > 0 ); + next unless ( exists $channels{$_}{'l'} ); + + &DEBUG("on_quit: netsplit detected on $_; disabling chan limit."); + $conn->mode( $_, '-l' ); + } + + $netsplit{ lc $nick } = time(); + if ( !exists $netsplitservers{$1}{$2} ) { + &status("netsplit detected between $1 and $2 at [" + . scalar(gmtime) + . ']' ); + $netsplitservers{$1}{$2} = time(); + } + } - $netsplit{lc $nick} = time(); - if (!exists $netsplitservers{$1}{$2}) { - &status("netsplit detected between $1 and $2."); - $netsplitservers{$1}{$2} = time(); - } + my $chans = join( ' ', &getNickInChans($nick) ); + &status( +">>> $b_cyan$nick$ob has signed off IRC $b_red($ob$reason$b_red)$ob [$chans]" + ); + + ### + ### ok... lets clear out the cache + ### + &delUserInfo( $nick, keys %channels ); + if ( exists $nuh{ lc $nick } ) { + delete $nuh{ lc $nick }; } + else { - &status(">>> $b_cyan$nick$ob has signed off IRC $b_red($ob$reason$b_red)$ob"); - if ($nick =~ /^\Q$ident\E$/) { - &DEBUG("!!! THIS SHOULD NEVER HAPPEN. FIXME HOPEFULLY"); + # well.. it's good but weird that this has happened - lets just + # be quiet about it. } - if ($nick !~ /^\Q$ident\E$/ and $nick =~ /^\Q$param{'ircNick'}\E$/i) { - &status("own nickname became free; changing."); - &nick($param{'ircNick'}); + delete $userstats{ lc $nick } if ( &IsChanConf('seenStats') > 0 ); + delete $chanstats{ lc $nick }; + ### + + # if we have a temp nick, and whoever is camping on our main nick leaves + # revert to main nick. Note that Net::IRC only knows our main nick + if ( $nick eq $conn->nick() ) { + &status("nickchange: own nick \"$nick\" became free; changing."); + &nick($mynick); } } sub on_targettoofast { - my ($self, $event) = @_; + $conn = shift(@_); + my ($event) = @_; my $nick = $event->nick(); - my $chan = ($event->to)[0]; + my ( $me, $chan, $why ) = $event->args(); + + ### TODO: incomplete. + if ( $why =~ /.* wait (\d+) second/ ) { + my $sleep = $1; + my $max = 10; + + if ( $sleep > $max ) { + &status("targettoofast: going to sleep for $max ($sleep)..."); + $sleep = $max; + } + else { + &status("targettoofast: going to sleep for $sleep"); + } + + my $delta = time() - ( $cache{sleepTime} || 0 ); + if ( $delta > $max + 2 ) { + sleep $sleep; + $cache{sleepTime} = time(); + } + + return; + } - &DEBUG("on_targettoofast: nick => '$nick'."); - &DEBUG("on_targettoofast: chan => '$chan'."); + if ( !exists $cache{TargetTooFast} ) { + &DEBUG("on_ttf: failed: $why"); + $cache{TargetTooFast}++; + } +} - foreach ($event->args) { - &DEBUG("on_targettoofast: args => '$_'."); +sub on_topic { + $conn = shift(@_); + my ($event) = @_; + + if ( scalar( $event->args ) == 1 ) { # change. + my $topic = ( $event->args )[0]; + my $chan = ( $event->to )[0]; + my $nick = $event->nick(); + + ### + # WARNING: + # race condition here. To fix, change '1' to '0'. + # This will keep track of topics set by bot only. + ### + # UPDATE: + # this may be fixed at a later date with topic queueing. + ### + + $topic{$chan}{'Current'} = $topic if (1); + $chanstats{$chan}{'Topic'}++; + + &status(">>> topic/$b_blue$chan$ob by $b_cyan$nick$ob -> $topic"); } + else { # join. + my ( $nick, $chan, $topic ) = $event->args; + if ( &IsChanConf('Topic') > 0 ) { + $topic{$chan}{'Current'} = $topic; + &topicAddHistory( $chan, $topic ); + } + + $topic = &fixString( $topic, 1 ); + &status(">>> topic/$b_blue$chan$ob is $topic"); + } +} -### .* wait (\d+) second/) { - &status("X1 $msg"); - my $sleep = $3 + 10; +sub on_topicinfo { + $conn = shift(@_); + my ($event) = @_; + my ( $myself, $chan, $setby, $time ) = $event->args(); - &status("going to sleep for $sleep..."); - sleep $sleep; - &joinNextChan(); -### } + my $timestr; + if ( time() - $time > 60 * 60 * 24 ) { + $timestr = 'at ' . gmtime $time; + } + else { + $timestr = &Time2String( time() - $time ) . ' ago'; + } + + &status(">>> set by $b_cyan$setby$ob $timestr"); } -sub on_topic { - my ($self, $event) = @_; +sub on_crversion { + $conn = shift(@_); + my ($event) = @_; + my $nick = $event->nick(); + my $ver; - if (scalar($event->args) == 1) { # change. - my $topic = ($event->args)[0]; - my $chan = ($event->to)[0]; - my $nick = $event->nick(); + if ( scalar $event->args() != 1 ) { # old. + $ver = join ' ', $event->args(); + $ver =~ s/^VERSION //; + } + else { # new. + $ver = ( $event->args() )[0]; + } - ### - # WARNING: - # race condition here. To fix, change '1' to '0'. - # This will keep track of topics set by bot only. - ### - # UPDATE: - # this may be fixed at a later date with topic queueing. - ### + if ( grep /^\Q$nick\E$/i, @vernick ) { + &WARN("nick $nick found in vernick ($ver); skipping."); + return; + } + push( @vernick, $nick ); - $topic{$chan}{'Current'} = $topic if (1 and &IsParam("topic") == 1); - $chanstats{$chan}{'Topic'}++; + &DEBUG("on_crversion: Got '$ver' from $nick"); - &status(">>> topic/$b_blue$chan$ob by $b_cyan$nick$ob -> $topic"); - } else { # join. - my ($nick, $chan, $topic) = $event->args; - if (&IsParam("topic")) { - $topic{$chan}{'Current'} = $topic; - &topicAddHistory($chan,$topic); - } + if ( $ver =~ /bitchx/i ) { + $ver{bitchx}{$nick} = $ver; - $topic = &fixString($topic, 1); - &status(">>> topic/$b_blue$chan$ob is $topic"); } -} + elsif ( $ver =~ /infobot/i ) { + $ver{infobot}{$nick} = $ver; -sub on_topicinfo { - my ($self, $event) = @_; - my ($myself,$chan,$setby,$time) = $event->args(); + } + elsif ( $ver =~ /(xc\!|xchat)/i ) { + $ver{xchat}{$nick} = $ver; - my $timestr; - if (time() - $time > 60*60*24) { - $timestr = "at ". localtime $time; - } else { - $timestr = &Time2String(time() - $time) ." ago"; } + elsif ( $ver =~ /irssi/i ) { + $ver{irssi}{$nick} = $ver; - &status(">>> set by $b_cyan$setby$ob $timestr"); + } + elsif ( $ver =~ /(epic|Third Eye)/i ) { + $ver{epic}{$nick} = $ver; + + } + elsif ( $ver =~ /(ircII|PhoEniX)/i ) { + $ver{ircII}{$nick} = $ver; + + } + elsif ( $ver =~ /mirc/i ) { + # Apparently, mIRC gets the reply as "VERSION " and doesnt like the + # space, so mirc matching is considered bugged. + $ver{mirc}{$nick} = $ver; + + } + elsif ( $ver =~ /ircle/i ) { + $ver{ircle}{$nick} = $ver; + + } + elsif ( $ver =~ /chatzilla/i ) { + $ver{chatzilla}{$nick} = $ver; + + } + elsif ( $ver =~ /pirch/i ) { + $ver{pirch}{$nick} = $ver; + + } + elsif ( $ver =~ /sirc /i ) { + $ver{sirc}{$nick} = $ver; + + } + elsif ( $ver =~ /kvirc/i ) { + $ver{kvirc}{$nick} = $ver; + + } + elsif ( $ver =~ /eggdrop/i ) { + $ver{eggdrop}{$nick} = $ver; + + } + elsif ( $ver =~ /xircon/i ) { + $ver{xircon}{$nick} = $ver; + + } + else { + &DEBUG("verstats: other: $nick => '$ver'."); + $ver{other}{$nick} = $ver; + } } sub on_version { - my ($self, $event) = @_; + $conn = shift(@_); + my ($event) = @_; my $nick = $event->nick; &status(">>> ${b_green}CTCP VERSION$ob request from $b_cyan$nick$ob"); - $self->ctcp_reply($nick, "VERSION $bot_version"); + $conn->ctcp_reply( $nick, "VERSION $bot_version" ); } sub on_who { - my ($self, $event) = @_; - my @args = $event->args; - - $nuh{lc $args[5]} = $args[5]."!".$args[2]."\@".$args[3]; -} - -sub on_whoisuser { - my ($self, $event) = @_; - my @args = $event->args; - - $nuh{lc $args[1]} = $args[1]."!".$args[2]."\@".$args[3]; -} - -####################################################################### -####### IRC HOOK HELPERS IRC HOOK HELPERS IRC HOOK HELPERS ######## -####################################################################### - -##### -# Usage: &hookMode($chan, $modes, @targets); -sub hookMode { - my ($chan, $modes, @targets) = @_; - my $parity = 0; - - $chan = lc $chan; # !!!. - - my $mode; - foreach $mode (split(//, $modes)) { - # sign. - if ($mode =~ /[-+]/) { - $parity = 1 if ($mode eq "+"); - $parity = 0 if ($mode eq "-"); - next; - } - - # mode with target. - if ($mode =~ /[bklov]/) { - my $target = shift @targets; - - if ($parity) { - $chanstats{$chan}{'Op'}++ if ($mode eq "o"); - $chanstats{$chan}{'Ban'}++ if ($mode eq "b"); - } else { - $chanstats{$chan}{'Deop'}++ if ($mode eq "o"); - $chanstats{$chan}{'Unban'}++ if ($mode eq "b"); - } - - # modes w/ target affecting nick => cache it. - if ($mode =~ /[ov]/) { - $channels{$chan}{$mode}{$target}++ if $parity; - delete $channels{$chan}{$mode}{$target} if !$parity; - } - - if ($mode =~ /[l]/) { - $channels{$chan}{$mode} = $target if $parity; - delete $channels{$chan}{$mode} if !$parity; - } - } - - # important channel modes, targetless. - if ($mode =~ /[mt]/) { - $channels{$chan}{$mode}++ if $parity; - delete $channels{$chan}{$mode} if !$parity; - } - } -} - -sub hookMsg { - ($msgType, $chan, $who, $message) = @_; - my $skipmessage = 0; - $addressed = 0; - $addressedother = 0; - $orig{message} = $message; - $orig{who} = $who; - $addrchar = 0; - - $message =~ s/[\cA-\c_]//ig; # strip control characters - $message =~ s/^\s+//; # initial whitespaces. - $who =~ tr/A-Z/a-z/; # lowercase. - - &showProc(); - - # addressing. - if ($msgType =~ /private/) { - # private messages. - $addressed = 1; - } else { - # public messages. - # addressing revamped by the xk. - ### below needs to be fixed... - if (&IsParam("addressCharacter")) { - if ($message =~ s/^$param{'addressCharacter'}//) { - $addrchar = 1; - $addressed = 1; - } - } - - if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) { - my $newmessage = $'; - if ($1 =~ /^\Q$ident\E$/i) { - $message = $newmessage; - $addressed = 1; - } else { - # ignore messages addressed to other people or unaddressed. - $skipmessage++ if ($2 ne "" and $2 !~ /^ /); - } - } - } - - # Determine floodwho. - if ($msgType =~ /public/i) { # public. - $floodwho = lc $chan; - } elsif ($msgType =~ /private/i) { # private. - $floodwho = lc $who; - } else { # dcc? - &DEBUG("FIXME: floodwho = ???"); - } - - my ($count, $interval) = split(/:/, $param{'floodRepeat'} || "2:10"); - - # flood repeat protection. - if ($addressed) { - my $time = $flood{$floodwho}{$message}; - - if (defined $time and (time - $time < $interval)) { - ### public != personal who so the below is kind of pointless. - my @who; - foreach (keys %flood) { - next if (/^\Q$floodwho\E$/ or /^\Q$chan\E$/); - push(@who, grep /^\Q$message\E$/i, keys %{$flood{$_}}); - } - if (scalar @who) { - &msg($who, "you already said what ". - join(' ', @who)." have said."); - } else { - &msg($who,"Someone already said that ". (time - $time) ." seconds ago" ); - } - - ### TODO: delete old floodwarn{} keys. - my $floodwarn = 0; - if (!exists $floodwarn{$floodwho}) { - $floodwarn++; - } else { - $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval); - } - - if ($floodwarn) { - &status("FLOOD repetition detected from $floodwho."); - $floodwarn{$floodwho} = time(); - } - - return; - } - - if ($addrchar) { - &status("$b_cyan$who$ob is short-addressing me"); - } else { - &status("$b_cyan$who$ob is addressing me"); - } - - $flood{$floodwho}{$message} = time(); - } - - ($count, $interval) = split(/:/, $param{'floodMessages'} || "5:30"); - # flood overflow protection. - if ($addressed) { - foreach (keys %{$flood{$floodwho}}) { - next unless (time() - $flood{$floodwho}{$_} > $interval); - delete $flood{$floodwho}{$_}; - } - - my $i = scalar keys %{$flood{$floodwho}}; - if ($i > $count) { - &msg($who,"overflow of messages ($i > $count)"); - &status("FLOOD overflow detected from $floodwho; ignoring"); - - my $expire = $param{'ignoreAutoExpire'} || 5; - $ignoreList{"*!$uh"} = time() + ($expire * 60); - return; - } - - $flood{$floodwho}{$message} = time(); - } - - # public. - if ($msgType =~ /public/i) { - $talkchannel = $chan; - &status("<$orig{who}/$chan> $orig{message}"); - } - - # private. - if ($msgType =~ /private/i) { - &status("[$orig{who}] $orig{message}"); - } - - if ((!$skipmessage or &IsParam("seenStoreAll")) and - &IsParam("seen") and - $msgType =~ /public/ - ) { - $seencache{$who}{'time'} = time(); - $seencache{$who}{'nick'} = $orig{who}; - $seencache{$who}{'host'} = $uh; - $seencache{$who}{'chan'} = $talkchannel; - $seencache{$who}{'msg'} = $orig{message}; - $seencache{$who}{'msgcount'}++; + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + my $str = $args[5] . '!' . $args[2] . '@' . $args[3]; + + if ( $cache{on_who_Hack} ) { + $cache{nuhInfo}{ lc $args[5] }{Nick} = $args[5]; + $cache{nuhInfo}{ lc $args[5] }{User} = $args[2]; + $cache{nuhInfo}{ lc $args[5] }{Host} = $args[3]; + $cache{nuhInfo}{ lc $args[5] }{NUH} = "$args[5]!$args[2]\@$args[3]"; + return; } - return if ($skipmessage); - return unless (&IsParam("minVolunteerLength") or $addressed); + if ( $args[5] =~ /^nickserv$/i and !$nickserv ) { + &DEBUG('ok... we did a who for nickserv.'); + &rawout("PRIVMSG NickServ :IDENTIFY $param{'nickServ_pass'}"); + } - local $ignore = 0; - foreach (keys %ignoreList) { - my $ignoreRE = $_; - my @parts = split /\*/, "a${ignoreRE}a"; - my $recast = join '\S*', map quotemeta($_), @parts; - $recast =~ s/^a(.*)a$/$1/; - if ($nuh =~ /^$recast$/) { - $ignore++; - last; - } - } + $nuh{ lc $args[5] } = $args[5] . '!' . $args[2] . '@' . $args[3]; +} - if (defined $nuh) { - $userHandle = &verifyUser($who, $nuh); - } else { - &DEBUG("hookMsg: 'nuh' not defined?"); - } +sub on_whois { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; -### For extra debugging purposes... - if ($_ = &process()) { -# &DEBUG("IrcHooks: process returned '$_'."); - } + $nuh{ lc $args[1] } = $args[1] . '!' . $args[2] . '@' . $args[3]; +} - return; +sub on_whoischannels { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + + &DEBUG("on_whoischannels: @args"); +} + +sub on_useronchannel { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + + &DEBUG("on_useronchannel: @args"); + &joinNextChan(); +} + +### +### since joinnextchan is hooked onto on_endofnames, these are needed. +### + +sub on_chanfull { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + + &status(">>> chanfull/$b_blue$args[1]$ob"); + + &joinNextChan(); +} + +sub on_inviteonly { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + + &status(">>> inviteonly/$b_cyan$args[1]$ob"); + + &joinNextChan(); +} + +sub on_banned { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + my $chan = $args[1]; + + &status( +">>> banned/$b_blue$chan$ob $b_cyan$args[0]$ob, removing autojoin for $chan" + ); + delete $chanconf{$chan}{autojoin}; + &joinNextChan(); +} + +sub on_badchankey { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + my $chan = $args[1]; + + &DEBUG("on_badchankey: args => @args, removing autojoin for $chan"); + delete $chanconf{$chan}{autojoin}; + &joinNextChan(); +} + +sub on_useronchan { + $conn = shift(@_); + my ($event) = @_; + my @args = $event->args; + + &DEBUG("on_useronchan: args => @args"); + &joinNextChan(); +} + +# TODO not used yet +sub on_stdin { + my $line = ; + chomp($line); + &FIXME("on_stdin: line => \"$line\""); } 1; + +# vim:ts=4:sw=4:expandtab:tw=80