2 # IrcHooks.pl: IRC Hooks stuff.
5 # NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997
7 use vars qw(%chanconf);
13 my $nick = $event->nick();
14 my $chan = ( $event->to )[0];
16 &DEBUG("on_generic: nick => '$nick'.");
17 &DEBUG("on_generic: chan => '$chan'.");
19 foreach ( $event->args ) {
20 &DEBUG("on_generic: args => '$_'.");
27 my ( $nick, $args ) = ( $event->nick, $event->args );
28 my $chan = ( $event->to )[0];
30 if ( $chan eq $ident ) {
31 &status("* [$nick] $args");
34 &status("* $nick/$chan $args");
41 my $msg = ( $event->args )[0];
42 my $sock = ( $event->to )[0];
43 my $nick = lc $event->nick();
45 if ( !exists $nuh{$nick} ) {
46 &DEBUG("chat: nuh{$nick} doesn't exist; trying WHOIS .");
51 ### set vars that would have been set in hookMsg.
52 $userHandle = ''; # reset.
56 $orig{message} = $msg;
58 $uh = ( split /\!/, $nuh )[1];
59 $h = ( split /\@/, $uh )[1];
63 if ( !exists $dcc{'CHATvrfy'}{$nick} ) {
64 $userHandle = &verifyUser( $who, $nuh );
65 my $crypto = $users{$userHandle}{PASS};
68 if ( $userHandle eq '_default' ) {
69 &WARN('DCC CHAT: _default/guest not allowed.');
73 ### TODO: prevent users without CRYPT chatting.
74 if ( !defined $crypto ) {
75 &TODO('dcc close chat');
76 &msg( $who, 'nope, no guest logins allowed...' );
80 if ( &ckpasswd( $msg, $crypto ) ) {
82 # stolen from eggdrop.
83 $conn->privmsg( $sock, "Connected to $ident" );
84 $conn->privmsg( $sock,
85 'Commands start with "." (like ".quit" or ".help")' );
86 $conn->privmsg( $sock,
87 'Everything else goes out to the party line.' );
89 &dccStatus(2) unless ( exists $sched{'dccStatus'}{RUNNING} );
95 &status('DCC CHAT: incorrect pass; closing connection.');
96 &DEBUG("chat: sock => '$sock'.");
98 delete $dcc{'CHAT'}{$nick};
99 &FIXME('chat: after closing sock.');
100 ### BUG: close seizes bot. why?
104 &status("DCC CHAT: user $nick is here!");
105 &DCCBroadcast("*** $nick ($uh) joined the party line.");
107 $dcc{'CHATvrfy'}{$nick} = $userHandle;
109 return if ( $userHandle eq '_default' );
111 &dccsay( $nick, "Flags: $users{$userHandle}{FLAGS}" );
117 &status("$b_red=$b_cyan$who$b_red=$ob $message");
119 if ( $message =~ s/^\.// ) { # dcc chat commands.
120 ### TODO: make use of &Forker(); here?
121 &loadMyModule('UserDCC');
123 &DCCBroadcast( "#$who# $message", 'm' );
125 my $retval = &userDCC();
126 return unless ( defined $retval );
127 return if ( $retval eq $noreply );
129 $conn->privmsg( $dcc{'CHAT'}{$who}, 'Invalid command.' );
132 else { # dcc chat arena.
134 foreach ( keys %{ $dcc{'CHAT'} } ) {
135 $conn->privmsg( $dcc{'CHAT'}{$_}, "<$who> $orig{message}" );
139 return 'DCC CHAT MESSAGE';
142 # is there isoff? how do we know if someone signs off?
146 my $x1 = ( $event->args )[0];
147 my $x2 = ( $event->args )[1];
150 &DEBUG("on_ison: x1 = '$x1', x2 => '$x2'");
157 $ident = $conn->nick();
158 $ircstats{'ConnectTime'} = time();
159 $ircstats{'ConnectCount'}++;
160 if ( defined $ircstats{'DisconnectTime'} ) {
161 $ircstats{'OffTime'} += time() - $ircstats{'DisconnectTime'};
165 if ( !exists $users{_default} ) {
166 &status('!!! First time run... adding _default user.');
167 $users{_default}{FLAGS} = 'amrt';
168 $users{_default}{HOSTS}{'*!*@*'} = 1;
171 if ( scalar keys %users < 2 ) {
173 &status("!!! Ok. Now type '/msg $ident PASS <pass>' to get master access through DCC CHAT.");
176 # end of first time run.
178 if ( &IsChanConf('Wingate') > 0 ) {
179 my $file = "$bot_base_dir/$param{'ircUser'}.wingate";
183 next unless (/^(\S+)\*$/);
184 push( @wingateBad, $_ );
190 &ScheduleThis( 1, 'setupSchedulers' );
194 if ( &IsParam('ircUMode') ) {
195 &VERB( "Attempting change of user modes to $param{'ircUMode'}.", 2 );
196 if ( $param{'ircUMode'} !~ /^[-+]/ ) {
197 &WARN('ircUMode had no +- prefix; adding +');
198 $param{'ircUMode'} = '+' . $param{'ircUMode'};
201 &rawout("MODE $ident $param{'ircUMode'}");
204 # ok, we're free to do whatever we want now. go for it!
207 # add ourself to notify.
208 $conn->ison( $conn->nick() );
210 # Q, as on quakenet.org.
211 if ( &IsParam('Q_pass') ) {
212 &status('Authing to Q...');
213 &rawout("PRIVMSG Q\@CServe.quakenet.org :AUTH $param{'Q_user'} $param{'Q_pass'}");
216 &status("$ident End of motd. Now lets join some channels...");
225 # &DEBUG("endofwho: chan => $chan");
226 $chan ||= ( $event->args )[1];
228 # &DEBUG("endofwho: chan => $chan");
230 if ( exists $cache{countryStats} ) {
238 my $type = uc( ( $event->args )[1] );
239 my $nick = lc $event->nick();
241 &status("on_dcc type=$type nick=$nick sock=$sock");
243 # pity Net::IRC doesn't store nuh. Here's a hack :)
244 if ( !exists $nuh{ lc $nick } ) {
246 $nuh{$nick} = 'GETTING-NOW'; # trying.
250 if ( $type eq 'SEND' ) { # GET for us.
251 # incoming DCC SEND. we're receiving a file.
252 my $get = ( $event->args )[2];
254 "DCC: not Initializing GET from $nick to '$param{tempDir}/$get'");
256 # FIXME: do we want to get anything?
259 #open(DCCGET,">$param{tempDir}/$get");
260 #$conn->new_get($event, \*DCCGET);
263 elsif ( $type eq 'GET' ) { # SEND for us?
264 &status("DCC: not Initializing SEND for $nick.");
266 # FIXME: do we want to do anything?
268 $conn->new_send( $event->args );
271 elsif ( $type eq 'CHAT' ) {
272 &status("DCC: Initializing CHAT for $nick.");
273 $conn->new_chat($event);
275 # $conn->new_chat(1, $nick, $event->host);
279 &WARN("${b_green}DCC $type$ob (1)");
286 my $nick = $event->nick();
287 my $sock = ( $event->to )[0];
289 # DCC CHAT close on fork exit workaround.
290 if ( $bot_pid != $$ ) {
291 &WARN('run-away fork; exiting.');
295 if ( exists $dcc{'SEND'}{$nick} and -f "$param{tempDir}/$nick.txt" ) {
296 &status("${b_green}DCC SEND$ob close from $b_cyan$nick$ob");
298 &status("dcc_close: purging DCC send $nick.txt");
299 unlink "$param{tempDir}/$nick.txt";
301 delete $dcc{'SEND'}{$nick};
303 elsif ( exists $dcc{'CHAT'}{$nick} and $dcc{'CHAT'}{$nick} eq $sock ) {
304 &status("${b_green}DCC CHAT$ob close from $b_cyan$nick$ob");
305 delete $dcc{'CHAT'}{$nick};
306 delete $dcc{'CHATvrfy'}{$nick};
309 &status("${b_green}DCC$ob UNKNOWN close from $b_cyan$nick$ob (2)");
316 my $type = uc( ( $event->args )[0] );
317 my $nick = lc $event->nick();
318 my $sock = ( $event->to )[0];
320 &status("on_dcc_open type=$type nick=$nick sock=$sock");
324 ### BUG: who is set to bot's nick?
327 if ( $type eq 'SEND' ) {
328 &status("${b_green}DCC lGET$ob established with $b_cyan$nick$ob");
331 elsif ( $type eq 'CHAT' ) {
334 ### TODO: run ScheduleThis inside on_dcc_open_chat recursively
335 ### 1,3,5,10 seconds then fail.
336 if ( $nuh{$nick} eq 'GETTING-NOW' ) {
337 &ScheduleThis( 3 / 60, 'on_dcc_open_chat', $nick, $sock );
340 on_dcc_open_chat( undef, $nick, $sock );
344 elsif ( $type eq 'SEND' ) {
345 &status('Starting DCC receive.');
346 foreach ( $event->args ) {
347 &status(" => '$_'.");
352 &WARN("${b_green}DCC $type$ob (3)");
356 # really custom sub to get NUH since Net::IRC doesn't appear to support
358 sub on_dcc_open_chat {
359 my ( undef, $nick, $sock ) = @_;
361 if ( $nuh{$nick} eq 'GETTING-NOW' ) {
362 &FIXME("getting nuh for $nick failed.");
367 "${b_green}DCC CHAT$ob established with $b_cyan$nick$ob $b_yellow($ob$nuh{$nick}$b_yellow)$ob"
370 &verifyUser( $nick, $nuh{ lc $nick } );
372 if ( !exists $users{$userHandle}{HOSTS} ) {
374 'you have no hosts defined in my user file; rejecting.');
379 my $crypto = $users{$userHandle}{PASS};
380 $dcc{'CHAT'}{$nick} = $sock;
382 # TODO: don't make DCC CHAT established in the first place.
383 if ( $userHandle eq '_default' ) {
384 &dccsay( $nick, '_default/guest not allowed' );
389 if ( defined $crypto ) {
390 &status( "DCC CHAT: going to use $nick\'s crypt." );
391 &dccsay( $nick, 'Enter your password.' );
395 # &dccsay($nick,"Welcome to infobot DCC CHAT interface, $userHandle.");
402 my $from = $event->from();
403 my $what = ( $event->args )[0];
404 my $mynick = $conn->nick();
406 &status("$mynick disconnect from $from ($what).");
407 $ircstats{'DisconnectTime'} = time();
408 $ircstats{'DisconnectReason'} = $what;
409 $ircstats{'DisconnectCount'}++;
410 $ircstats{'TotalTime'} += time() - $ircstats{'ConnectTime'}
411 if ( $ircstats{'ConnectTime'} );
413 # clear any variables on reconnection.
418 if ( !defined $conn ) {
419 &WARN('on_disconnect: self is undefined! WTF');
420 &DEBUG('running function irc... lets hope this works.');
425 &WARN('scheduling call ircCheck() in 60s');
427 &ScheduleThis( 1, 'ircCheck' );
433 my $chan = ( $event->args )[1];
435 # sync time should be done in on_endofwho like in BitchX
436 if ( exists $cache{jointime}{$chan} ) {
438 sprintf( '%.03f', &timedelta( $cache{jointime}{$chan} ) );
439 $delta_time = 0 if ( $delta_time <= 0 );
440 if ( $delta_time > 100 ) {
441 &WARN("endofnames: delta_time > 100 ($delta_time)");
444 &status("$b_blue$chan$ob: sync in ${delta_time}s.");
451 foreach ( 'o', 'v', '' ) {
452 my $count = scalar( keys %{ $channels{$chan}{$_} } );
453 next unless ($count);
455 $txt = 'total' if ( $_ eq '' );
456 $txt = 'voice' if ( $_ eq 'v' );
457 $txt = 'ops' if ( $_ eq 'o' );
459 push( @array, "$count $txt" );
461 my $chanstats = join( ' || ', @array );
462 &status("$b_blue$chan$ob: [$chanstats]");
464 &chanServCheck($chan);
466 # FIXME: scheduler is b0rken! flood join for now
467 # schedule used to solve ircu (OPN) 'target too fast' problems.
468 #$conn->schedule( 5, sub { &joinNextChan(); } );
475 my (@args) = ( $event->args );
484 my $chan = lc( ( $event->args )[0] );
485 my $nick = $event->nick;
487 if ( $nick =~ /^\Q$ident\E$/ ) {
488 &DEBUG('on_invite: self invite.');
493 if ( exists $chanconf{$chan} ) {
495 # it's still buggy :/
496 if ( &validChan($chan) ) {
497 &msg( $who, "i'm already in \002$chan\002." );
502 &status("invited to $b_blue$chan$ob by $b_cyan$nick$ob");
510 my ( $user, $host ) = split( /\@/, $event->userhost );
511 $chan = lc( ( $event->to )[0] ); # CASING!!!!
512 $who = $event->nick();
514 my $i = scalar( keys %{ $channels{$chan} } );
515 my $j = $cache{maxpeeps}{$chan} || 0;
517 if ( !&IsParam('noSHM')
518 && time() > ( $sched{shmFlush}{TIME} || time() ) + 3600 )
520 &DEBUG('looks like schedulers died somewhere... restarting...');
524 $chanstats{$chan}{'Join'}++;
525 $userstats{ lc $who }{'Join'} = time() if ( &IsChanConf('seenStats') > 0 );
526 $cache{maxpeeps}{$chan} = $i if ( $i > $j );
528 &joinfloodCheck( $who, $chan, $event->userhost );
532 if ( exists $netsplit{ lc $who } ) {
533 delete $netsplit{ lc $who };
536 if ( !scalar keys %netsplit ) {
537 &DEBUG('on_join: netsplit hash is now empty!');
538 undef %netsplitservers;
539 &netsplitCheck(); # any point in running this?
544 if ( $netsplit and !exists $cache{netsplit} ) {
545 &VERB('on_join: ok.... re-running chanlimitCheck in 60.', 2);
550 delete $cache{netsplit};
554 $cache{netsplit} = time();
557 # how to tell if there's a netjoin???
559 my $netsplitstr = '';
560 $netsplitstr = " $b_yellow\[${ob}NETSPLIT VICTIM$b_yellow]$ob"
563 ">>> join/$b_blue$chan$ob $b_cyan$who$ob $b_yellow($ob$user\@$host$b_yellow)$ob$netsplitstr"
566 $channels{$chan}{''}{$who}++;
567 $nuh = $who . '!' . $user . '@' . $host;
568 $nuh{ lc $who } = $nuh unless ( exists $nuh{ lc $who } );
572 push( @bans, keys %{ $bans{$chan} } ) if ( exists $bans{$chan} );
573 push( @bans, keys %{ $bans{'*'} } ) if ( exists $bans{'*'} );
580 next unless ( $nuh =~ /^$mask$/i );
582 ### TODO: check $channels{$chan}{'b'} if ban already exists.
583 foreach ( keys %{ $channels{$chan}{'b'} } ) {
584 &DEBUG(" bans_on_chan($chan) => $_");
587 my $reason = 'no reason';
588 foreach ( $chan, '*' ) {
589 next unless ( exists $bans{$_} );
590 next unless ( exists $bans{$_}{$ban} );
592 my @array = @{ $bans{$_}{$ban} };
594 $reason = $array[4] if ( $array[4] );
599 &kick( $who, $chan, $reason );
604 # no need to go further.
605 return if ($netsplit);
608 if ( $who =~ /^\Q$ident\E$/i ) {
609 if ( defined( my $whojoin = $cache{join}{$chan} ) ) {
610 &msg( $chan, "Okay, I'm here. (courtesy of $whojoin)" );
611 delete $cache{join}{$chan};
612 &joinNextChan(); # hack.
615 ### TODO: move this to &joinchan()?
616 $cache{jointime}{$chan} = &timeget();
623 &rootWarn( $who, $user, $host, $chan )
624 if ( &IsChanConf('RootWarn') > 0
625 && $user eq 'root' );
626 #&& $user =~ /^~?r(oo|ew|00)t$/i );
628 ### emit a message based on who just joined
629 &onjoin( $who, $user, $host, $chan ) if ( &IsChanConf('OnJoin') > 0 );
632 if ( &IsChanConf('News') > 0 && &IsChanConf('newsKeepRead') > 0 ) {
633 if ( !&loadMyModule('News') ) { # just in case.
634 &DEBUG('could not load news.');
637 &News::latest($chan);
642 if ( &IsChanConf('botmail') > 0 ) {
643 &botmail::check( lc $who );
653 my ( $chan, $reason ) = $event->args;
654 my $kicker = $event->nick;
655 my $kickee = ( $event->to )[0];
656 my $uh = $event->userhost();
659 ">>> kick/$b_blue$chan$ob [$b$kickee!$uh$ob] by $b_cyan$kicker$ob $b_yellow($ob$reason$b_yellow)$ob"
662 $chan = lc $chan; # forgot about this, found by xsdg, 20001229.
663 $chanstats{$chan}{'Kick'}++;
665 if ( $kickee eq $ident ) {
666 &clearChanVars($chan);
668 &status("SELF attempting to rejoin lost channel $chan");
672 &delUserInfo( $kickee, $chan );
679 my ( $user, $host ) = split( /\@/, $event->userhost );
680 my @args = $event->args();
681 my $nick = $event->nick();
682 $chan = ( $event->to )[0];
684 # last element is empty... so nuke it.
685 pop @args while ( $args[$#args] eq '' );
687 if ( $nick eq $chan ) { # UMODE
689 ">>> mode $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob");
693 ">>> mode/$b_blue$chan$ob $b_yellow\[$ob$b@args$b_yellow\]$ob by $b_cyan$nick$ob"
695 &hookMode( $nick, @args );
702 my ( $myself, undef, @args ) = $event->args();
703 my $nick = $event->nick();
704 $chan = ( $event->args() )[1];
706 &hookMode( $nick, @args );
712 my $nick = $event->nick;
713 my $msg = ( $event->args )[0];
715 ( $user, $host ) = split( /\@/, $event->userhost );
716 $uh = $event->userhost();
717 $nuh = $nick . '!' . $uh;
721 if ( $nick eq $ident ) { # hopefully ourselves.
722 if ( $msg eq 'TEST' ) {
723 &status("IRCTEST: Yes, we're alive.");
724 delete $cache{connect};
729 &hookMsg( 'private', undef, $nick, $msg );
738 my @args = $event->args;
739 my $chan = lc $args[2]; # CASING, the last of them!
741 foreach ( split / /, @args[ 3 .. $#args ] ) {
742 $channels{$chan}{'o'}{$_}++ if s/\@//;
743 $channels{$chan}{'v'}{$_}++ if s/\+//;
744 $channels{$chan}{''}{$_}++;
751 my $nick = $event->nick();
752 my $newnick = ( $event->args )[0];
754 if ( exists $netsplit{ lc $newnick } ) {
756 "Netsplit: $newnick/$nick came back from netsplit and changed to original nick! removing from hash."
758 delete $netsplit{ lc $newnick };
759 &netsplitCheck() if ( time() != $sched{netsplitCheck}{TIME} );
763 foreach $chan ( keys %channels ) {
764 foreach $mode ( keys %{ $channels{$chan} } ) {
765 next unless ( exists $channels{$chan}{$mode}{$nick} );
767 $channels{$chan}{$mode}{$newnick} = $channels{$chan}{$mode}{$nick};
771 # TODO: do %flood* aswell.
773 &delUserInfo( $nick, keys %channels );
774 $nuh{ lc $newnick } = $nuh{ lc $nick };
775 delete $nuh{ lc $nick };
777 if ( $nick eq $conn->nick() ) {
778 &status(">>> I materialized into $b_green$newnick$ob from $nick");
780 $conn->nick($newnick);
783 &status(">>> $b_cyan$nick$ob materializes into $b_green$newnick$ob");
784 my $mynick = $conn->nick();
785 if ( $nick =~ /^\Q$mynick\E$/i ) {
793 my $nick = $conn->nick();
795 #my $newnick = $nick . int(rand 10);
796 my $newnick = $nick . '_';
798 &DEBUG("on_nick_taken: nick => $nick");
800 &status("nick taken ($nick); preparing nick change.");
804 #$conn->schedule(5, sub {
805 &status("nick taken; changing to temporary nick ($nick -> $newnick).");
814 my $nick = $event->nick();
815 my $chan = ( $event->to )[0];
816 my $args = ( $event->args )[0];
817 my $mynick = $conn->nick();
819 if ( $nick =~ /^NickServ$/i ) { # nickserv.
820 &status("NickServ: $mynick <== '$args'");
823 $check++ if ( $args =~ /^This nickname is registered/i );
824 $check++ if ( $args =~ /nickname.*owned/i );
827 &status("nickserv told $mynick to register; doing it.");
829 if ( &IsParam('nickServ_pass') ) {
830 &status("NickServ: ==> Identifying as $mynick.");
831 &rawout("PRIVMSG NickServ :IDENTIFY $param{'nickServ_pass'}");
835 &status("$mynick can't tell nickserv a passwd ;(");
840 if ( $args =~ /^Password a/i ) {
843 foreach ( &ChanConfList('chanServ_ops') ) {
844 next unless &chanServCheck($_);
846 &DEBUG('nickserv activated or restarted; doing chanserv check.');
854 elsif ( $nick =~ /^ChanServ$/i ) { # chanserv.
855 &status("ChanServ: <== '$args'.");
859 if ( $chan =~ /^$mask{chan}$/ ) { # channel notice.
860 &status("-$nick/$chan- $args");
863 $server = $nick unless ( defined $server );
864 &status("-$nick- $args"); # private or server notice.
872 my $chan = ( $event->to )[0];
873 my $nick = $event->nick;
875 &status('!!! other called.');
876 &status("!!! $event->args");
882 $chan = lc( ( $event->to )[0] ); # CASING!!!
883 my $mynick = $conn->nick();
884 my $nick = $event->nick;
885 my $userhost = $event->userhost;
889 if ( !exists $channels{$chan} ) {
890 &DEBUG("on_part: found out $mynick is on $chan!");
891 $channels{$chan} = 1;
894 if ( exists $floodjoin{$chan}{$nick}{Time} ) {
895 delete $floodjoin{$chan}{$nick};
898 $chanstats{$chan}{'Part'}++;
899 &delUserInfo( $nick, $chan );
900 if ( $nick eq $ident ) {
901 &clearChanVars($chan);
904 if ( !&IsNickInAnyChan($nick) and &IsChanConf('seenStats') > 0 ) {
905 delete $userstats{ lc $nick };
909 ">>> part/$b_blue$chan$ob $b_cyan$nick$ob $b_yellow($ob$userhost$b_yellow)$ob"
916 my $nick = $event->nick;
918 $conn->ctcp_reply( $nick, join( ' ', ( $event->args ) ) );
920 ">>> ${b_green}CTCP PING$ob request from $b_cyan$nick$ob received.");
926 my $nick = $event->nick;
927 my $t = ( $event->args )[1];
929 &WARN('on_ping_reply: t == undefined.');
933 my $lag = time() - $t;
935 &status(">>> ${b_green}CTCP PING$ob reply from $b_cyan$nick$ob: $lag sec.");
941 my $msg = ( $event->args )[0];
942 $chan = lc( ( $event->to )[0] ); # CASING.
943 my $nick = $event->nick;
945 $uh = $event->userhost();
946 $nuh = $nick . '!' . $uh;
949 # TODO: move this out of hookMsg to here?
950 ( $user, $host ) = split( /\@/, $uh );
953 # rare case should this happen - catch it just in case.
954 if ( $bot_pid != $$ ) {
955 &ERROR('run-away fork; exiting.');
960 $lastWho{$chan} = $nick;
961 ### TODO: use $nick or lc $nick?
962 if ( &IsChanConf('seenStats') > 0 ) {
963 $userstats{ lc $nick }{'Count'}++;
964 $userstats{ lc $nick }{'Time'} = time();
969 if ( !$cache{ircTextCounters} ) {
970 &DEBUG('caching ircTextCounters for first time.');
971 my @str = split( /\s+/, &getChanConf('ircTextCounters') );
972 for (@str) { $_ = quotemeta($_); }
973 $cache{ircTextCounters} = join( '|', @str );
976 my $str = $cache{ircTextCounters};
977 if ( $str && $msg =~ /^($str)[\s!\.]?$/i ) {
980 &VERB( "textcounters: $x matched for $who", 2 );
981 my $c = $chan || 'PRIVATE';
983 # better to do 'counter=counter+1'.
984 # but that will avoid time check.
985 my ( $v, $t ) = &sqlSelect(
996 # don't allow ppl to cheat the stats :-)
997 if ( ( defined $t && $time - $t > 60 ) or ( !defined $t ) ) {
1013 &hookMsg( 'public', $chan, $nick, $msg );
1014 $chanstats{$chan}{'PublicMsg'}++;
1023 my $nick = $event->nick();
1024 my $reason = ( $event->args )[0];
1027 $msgType = 'public';
1029 ### $chan = $reason; # no.
1032 foreach ( grep !/^_default$/, keys %channels ) {
1034 # fixes inconsistent chanstats bug #1.
1035 if ( !&IsNickInChan( $nick, $_ ) ) {
1039 $chanstats{$_}{'SignOff'}++;
1042 if ( $count == scalar keys %channels ) {
1043 &DEBUG("on_quit: nick $nick was not found in any chan.");
1046 # should fix chanstats inconsistencies bug #2.
1047 if ( $reason =~ /^($mask{host})\s($mask{host})$/ ) { # netsplit.
1048 $reason = "NETSPLIT: $1 <=> $2";
1051 foreach $chan ( &getNickInChans($nick) ) {
1052 next unless ( &IsChanConf('chanlimitcheck') > 0 );
1053 next unless ( exists $channels{$_}{'l'} );
1055 &DEBUG("on_quit: netsplit detected on $_; disabling chan limit.");
1056 $conn->mode( $_, '-l' );
1059 $netsplit{ lc $nick } = time();
1060 if ( !exists $netsplitservers{$1}{$2} ) {
1061 &status("netsplit detected between $1 and $2 at ["
1064 $netsplitservers{$1}{$2} = time();
1068 my $chans = join( ' ', &getNickInChans($nick) );
1070 ">>> $b_cyan$nick$ob has signed off IRC $b_red($ob$reason$b_red)$ob [$chans]"
1074 ### ok... lets clear out the cache
1076 &delUserInfo( $nick, keys %channels );
1077 if ( exists $nuh{ lc $nick } ) {
1078 delete $nuh{ lc $nick };
1082 # well.. it's good but weird that this has happened - lets just
1083 # be quiet about it.
1085 delete $userstats{ lc $nick } if ( &IsChanConf('seenStats') > 0 );
1086 delete $chanstats{ lc $nick };
1089 # if we have a temp nick, and whoever is camping on our main nick leaves
1090 # revert to main nick. Note that Net::IRC only knows our main nick
1091 if ( $nick eq $conn->nick() ) {
1092 &status("nickchange: own nick \"$nick\" became free; changing.");
1097 sub on_targettoofast {
1100 my $nick = $event->nick();
1101 my ( $me, $chan, $why ) = $event->args();
1103 ### TODO: incomplete.
1104 if ( $why =~ /.* wait (\d+) second/ ) {
1108 if ( $sleep > $max ) {
1109 &status("targettoofast: going to sleep for $max ($sleep)...");
1113 &status("targettoofast: going to sleep for $sleep");
1116 my $delta = time() - ( $cache{sleepTime} || 0 );
1117 if ( $delta > $max + 2 ) {
1119 $cache{sleepTime} = time();
1125 if ( !exists $cache{TargetTooFast} ) {
1126 &DEBUG("on_ttf: failed: $why");
1127 $cache{TargetTooFast}++;
1135 if ( scalar( $event->args ) == 1 ) { # change.
1136 my $topic = ( $event->args )[0];
1137 my $chan = ( $event->to )[0];
1138 my $nick = $event->nick();
1142 # race condition here. To fix, change '1' to '0'.
1143 # This will keep track of topics set by bot only.
1146 # this may be fixed at a later date with topic queueing.
1149 $topic{$chan}{'Current'} = $topic if (1);
1150 $chanstats{$chan}{'Topic'}++;
1152 &status(">>> topic/$b_blue$chan$ob by $b_cyan$nick$ob -> $topic");
1155 my ( $nick, $chan, $topic ) = $event->args;
1156 if ( &IsChanConf('Topic') > 0 ) {
1157 $topic{$chan}{'Current'} = $topic;
1158 &topicAddHistory( $chan, $topic );
1161 $topic = &fixString( $topic, 1 );
1162 &status(">>> topic/$b_blue$chan$ob is $topic");
1169 my ( $myself, $chan, $setby, $time ) = $event->args();
1172 if ( time() - $time > 60 * 60 * 24 ) {
1173 $timestr = 'at ' . gmtime $time;
1176 $timestr = &Time2String( time() - $time ) . ' ago';
1179 &status(">>> set by $b_cyan$setby$ob $timestr");
1185 my $nick = $event->nick();
1188 if ( scalar $event->args() != 1 ) { # old.
1189 $ver = join ' ', $event->args();
1190 $ver =~ s/^VERSION //;
1193 $ver = ( $event->args() )[0];
1196 if ( grep /^\Q$nick\E$/i, @vernick ) {
1197 &WARN("nick $nick found in vernick ($ver); skipping.");
1200 push( @vernick, $nick );
1202 &DEBUG("on_crversion: Got '$ver' from $nick");
1204 if ( $ver =~ /bitchx/i ) {
1205 $ver{bitchx}{$nick} = $ver;
1208 elsif ( $ver =~ /infobot/i ) {
1209 $ver{infobot}{$nick} = $ver;
1212 elsif ( $ver =~ /(xc\!|xchat)/i ) {
1213 $ver{xchat}{$nick} = $ver;
1216 elsif ( $ver =~ /irssi/i ) {
1217 $ver{irssi}{$nick} = $ver;
1220 elsif ( $ver =~ /(epic|Third Eye)/i ) {
1221 $ver{epic}{$nick} = $ver;
1224 elsif ( $ver =~ /(ircII|PhoEniX)/i ) {
1225 $ver{ircII}{$nick} = $ver;
1228 elsif ( $ver =~ /mirc/i ) {
1229 # Apparently, mIRC gets the reply as "VERSION " and doesnt like the
1230 # space, so mirc matching is considered bugged.
1231 $ver{mirc}{$nick} = $ver;
1234 elsif ( $ver =~ /ircle/i ) {
1235 $ver{ircle}{$nick} = $ver;
1238 elsif ( $ver =~ /chatzilla/i ) {
1239 $ver{chatzilla}{$nick} = $ver;
1242 elsif ( $ver =~ /pirch/i ) {
1243 $ver{pirch}{$nick} = $ver;
1246 elsif ( $ver =~ /sirc /i ) {
1247 $ver{sirc}{$nick} = $ver;
1250 elsif ( $ver =~ /kvirc/i ) {
1251 $ver{kvirc}{$nick} = $ver;
1254 elsif ( $ver =~ /eggdrop/i ) {
1255 $ver{eggdrop}{$nick} = $ver;
1258 elsif ( $ver =~ /xircon/i ) {
1259 $ver{xircon}{$nick} = $ver;
1263 &DEBUG("verstats: other: $nick => '$ver'.");
1264 $ver{other}{$nick} = $ver;
1271 my $nick = $event->nick;
1273 &status(">>> ${b_green}CTCP VERSION$ob request from $b_cyan$nick$ob");
1274 $conn->ctcp_reply( $nick, "VERSION $bot_version" );
1280 my @args = $event->args;
1281 my $str = $args[5] . '!' . $args[2] . '@' . $args[3];
1283 if ( $cache{on_who_Hack} ) {
1284 $cache{nuhInfo}{ lc $args[5] }{Nick} = $args[5];
1285 $cache{nuhInfo}{ lc $args[5] }{User} = $args[2];
1286 $cache{nuhInfo}{ lc $args[5] }{Host} = $args[3];
1287 $cache{nuhInfo}{ lc $args[5] }{NUH} = "$args[5]!$args[2]\@$args[3]";
1291 if ( $args[5] =~ /^nickserv$/i and !$nickserv ) {
1292 &DEBUG('ok... we did a who for nickserv.');
1293 &rawout("PRIVMSG NickServ :IDENTIFY $param{'nickServ_pass'}");
1296 $nuh{ lc $args[5] } = $args[5] . '!' . $args[2] . '@' . $args[3];
1302 my @args = $event->args;
1304 $nuh{ lc $args[1] } = $args[1] . '!' . $args[2] . '@' . $args[3];
1307 sub on_whoischannels {
1310 my @args = $event->args;
1312 &DEBUG("on_whoischannels: @args");
1315 sub on_useronchannel {
1318 my @args = $event->args;
1320 &DEBUG("on_useronchannel: @args");
1325 ### since joinnextchan is hooked onto on_endofnames, these are needed.
1331 my @args = $event->args;
1333 &status(">>> chanfull/$b_blue$args[1]$ob, removing autojoin");
1334 delete $chanconf{$chan}{autojoin};
1341 my @args = $event->args;
1343 &status(">>> inviteonly/$b_cyan$args[1]$ob, removing autojoin");
1344 delete $chanconf{$chan}{autojoin};
1351 my @args = $event->args;
1352 my $chan = $args[1];
1355 ">>> banned/$b_blue$chan$ob $b_cyan$args[0]$ob, removing autojoin for $chan"
1357 delete $chanconf{$chan}{autojoin};
1364 my @args = $event->args;
1365 my $chan = $args[1];
1367 &DEBUG("on_badchankey: args => @args, removing autojoin for $chan");
1368 delete $chanconf{$chan}{autojoin};
1375 my @args = $event->args;
1377 &DEBUG("on_useronchan: args => @args");
1385 &FIXME("on_stdin: line => \"$line\"");
1390 # vim:ts=4:sw=4:expandtab:tw=80