]> git.donarmstrong.com Git - infobot.git/blob - src/UserExtra.pl
allow dumping schedule list to log (for now)
[infobot.git] / src / UserExtra.pl
1 #
2 # UserExtra.pl: User Commands, Public.
3 #       Author: dms
4 #      Version: v0.2b (20000707)
5 #      Created: 20000107
6 #
7
8 use strict;
9 use vars qw($message $arg $qWord $verb $lobotomized $who $result $chan
10         $conn $msgType $query $talkchannel $ident $memusage);
11 use vars qw(%channels %chanstats %cmdstats %count %ircstats %param
12         %cache %mask %userstats %hooks_main);
13
14 ###
15 ### Start of command hooks for UserExtra.
16 ###
17
18 &addCmdHook("main", 'chan(stats|info)', ('CODEREF' => 'chaninfo', ) );
19 &addCmdHook("main", 'cmd(stats|info)', ('CODEREF' => 'cmdstats', ) );
20 &addCmdHook("main", 'sched(stats|info)', ('CODEREF' => 'scheduleList', ) );
21 &addCmdHook("main", 'factinfo', ('CODEREF' => 'factinfo',
22         'Cmdstats' => 'Factoid Info', Module => 'factoids', ) );
23 &addCmdHook("main", 'factstats?', ('CODEREF' => 'factstats',
24         'Cmdstats' => 'Factoid Stats', Help => "factstats",
25         Forker => 1, 'Identifier' => 'factoids', ) );
26 &addCmdHook("main", 'help', ('CODEREF' => 'help',
27         'Cmdstats' => 'Help', ) );
28 &addCmdHook("main", 'karma', ('CODEREF' => 'karma', ) );
29 &addCmdHook("main", 'i?spell', ('CODEREF' => 'ispell',
30         Help => 'spell', Identifier => 'spell', ) );
31 &addCmdHook("main", 'd?nslookup', ('CODEREF' => 'DNS',
32         Help => 'nslookup', Identifier => 'allowDNS',
33         Forker => "NULL", ) );
34 &addCmdHook("main", 'tell|explain', ('CODEREF' => 'tell',
35         Help => 'tell', Identifier => 'allowTelling',
36         Cmdstats => 'Tell') );
37 &addCmdHook("main", 'news', ('CODEREF' => 'News::Parse',
38         Module => 'news', 'Cmdstats' => 'News' ) );
39 &addCmdHook("main", 'countrystats', ('CODEREF' => 'countryStats',
40 #       Forker => "NULL",
41  ) );
42
43 &status("CMD: loaded ".scalar(keys %hooks_main)." MAIN command hooks.");
44
45 ###
46 ### Start of commands for hooks.
47 ###
48
49 sub chaninfo {
50     my $chan = lc shift(@_);
51     my $mode;
52
53     if ($chan eq "") {          # all channels.
54         my $i           = keys %channels;
55         my $reply       = "i am on \002$i\002 ".&fixPlural("channel",$i);
56         my $tucount     = 0;    # total user count.
57         my $uucount     = 0;    # unique user count.
58         my @array;
59
60         ### line 1.
61         foreach (sort keys %channels) {
62             if ( /^\s*$/ or / / ) {
63                 &status("chanstats: fe channels: chan == NULL.");
64                 #&ircCheck();
65                 next;
66             }
67             next if (/^_default$/);
68
69             my $str = sprintf("%s (%d)", $_, scalar(keys %{ $channels{$_}{''} }));
70             push(@array, $str);
71         }
72         &pSReply($reply.": ".join(', ', @array));
73         &ircCheck();
74
75         ### total user count.
76         foreach $chan (keys %channels) {
77             $tucount += scalar(keys %{ $channels{$chan}{''} });
78         }
79
80         ### unique user count.
81         my @nicks;
82         foreach $chan (keys %channels) {
83             foreach (keys %{ $channels{$chan}{''} }) {
84                 next if (grep /^\Q$_\E$/, @nicks);
85                 $uucount++;
86                 push(@nicks, $_);
87             }
88         }
89
90         if (scalar @nicks != $uucount) {
91             &DEBUG("nicks != uucount...");
92         }
93
94         my $chans = scalar(keys %channels);
95         &pSReply(
96             "i've cached \002$tucount\002 ". &fixPlural("user",$tucount).
97             ", \002$uucount\002 unique ". &fixPlural("user",$uucount).
98             ", distributed over \002$chans\002 ".
99             &fixPlural("channel", $chans)."."
100         );
101
102         return;
103     }
104
105     # channel specific.
106
107     if (&validChan($chan) == 0) {
108         &msg($who,"error: invalid channel \002$chan\002");
109         return;
110     }
111
112     # Step 1:
113     my @array;
114     foreach (sort keys %{ $chanstats{$chan} }) {
115         my $int = $chanstats{$chan}{$_};
116         next unless ($int);
117
118         push(@array, "\002$int\002 ". &fixPlural($_,$int));
119     }
120     my $reply = "On \002$chan\002, there ".
121                 &fixPlural("has",scalar(@array)). " been ".
122                 &IJoin(@array);
123
124     # Step 1b: check channel inconstencies.
125     $chanstats{$chan}{'Join'}           ||= 0;
126     $chanstats{$chan}{'SignOff'}        ||= 0;
127     $chanstats{$chan}{'Part'}           ||= 0;
128
129     my $delta_stats = $chanstats{$chan}{'Join'}
130                 - $chanstats{$chan}{'SignOff'}
131                 - $chanstats{$chan}{'Part'};
132
133     if ($delta_stats) {
134         my $total = scalar(keys %{ $channels{$chan}{''} });
135         &status("chaninfo: join ~= signoff + part (drift of $delta_stats < $total).");
136
137         if ($delta_stats > $total) {
138             &ERROR("chaninfo: delta_stats exceeds total users.");
139         }
140     }
141
142     # Step 2:
143     undef @array;
144     my $type;
145     foreach ("v","o","") {
146         my $int = scalar(keys %{ $channels{$chan}{$_} });
147         next unless ($int);
148
149         $type = "Voice" if ($_ eq "v");
150         $type = "Opped" if ($_ eq "o");
151         $type = "Total" if ($_ eq "");
152
153         push(@array,"\002$int\002 $type");
154     }
155     $reply .= ".  At the moment, ". &IJoin(@array);
156
157     # Step 3:
158     my %new;
159     foreach (keys %userstats) {
160         next unless (exists $userstats{$_}{'Count'});
161         if ($userstats{$_}{'Count'} =~ /^\D+$/) {
162             &WARN("userstats{$_}{Count} is non-digit.");
163             next;
164         }
165
166         $new{$_} = $userstats{$_}{'Count'};
167     }
168
169     # TODO: show top 3 with percentages?
170     my($count) = (sort { $new{$a} <=> $new{$b} } keys %new)[0];
171     if ($count) {
172         $reply .= ".  \002$count\002 has said the most with a total of \002$new{$count}\002 messages";
173     }
174     &pSReply("$reply.");
175 }
176
177 # Command statistics.
178 sub cmdstats {
179     my @array;
180
181     if (!scalar(keys %cmdstats)) {
182         &performReply("no-one has run any commands yet");
183         return;
184     }
185
186     my %countstats;
187     foreach (keys %cmdstats) {
188         $countstats{ $cmdstats{$_} }{$_} = 1;
189     }
190
191     foreach (sort {$b <=> $a} keys %countstats) {
192         my $int = $_;
193         next unless ($int);
194
195         foreach (keys %{ $countstats{$int} }) {
196             push(@array, "\002$int\002 of $_");
197         }
198     }
199     &pSReply("command usage include ". &IJoin(@array).".");
200 }
201
202 # Factoid extension info. xk++
203 sub factinfo {
204     my $faqtoid = lc shift(@_);
205     my $query   = "";
206
207     if ($faqtoid =~ /^\-(\S+)(\s+(.*))$/) {
208         &msg($who,"error: individual factoid info queries not supported as yet.");
209         &msg($who,"it's possible that the factoid mistakenly begins with '-'.");
210         return;
211
212         $query   = lc $1;
213         $faqtoid = lc $3;
214     }
215
216     &CmdFactInfo($faqtoid, $query);
217 }
218
219 sub factstats {
220     my $type = shift(@_);
221
222     &Forker("factoids", sub {
223         &pSReply( &CmdFactStats($type) );
224     } );
225 }
226
227 sub karma {
228     my $target  = lc( shift || $who );
229     my $karma   = &sqlSelect("stats", "counter",
230         { nick => $target, type => "karma" }) || 0;
231
232     if ($karma != 0) {
233         &pSReply("$target has karma of $karma");
234     } else {
235         &pSReply("$target has neutral karma");
236     }
237 }
238
239 sub ispell {
240     my $query = shift;
241
242     if (! -x "/usr/bin/ispell") {
243         &msg($who, "no binary found.");
244         return;
245     }
246
247     if (!&validExec($query)) {
248         &msg($who,"argument appears to be fuzzy.");
249         return;
250     }
251
252     my $reply = "I can't find alternate spellings for '$query'";
253
254     foreach (`/bin/echo '$query' | /usr/bin/ispell -a -S`) {
255         chop;
256         last if !length;                # end of query.
257
258         if (/^\@/) {            # intro line.
259             next;
260         } elsif (/^\*/) {               # possibly correct.
261             $reply = "'$query' may be spelled correctly";
262             last;
263         } elsif (/^\&/) {               # possible correction(s).
264             s/^\& (\S+) \d+ \d+: //;
265             my @array = split(/,? /);
266
267             $reply = "possible spellings for $query: @array";
268             last;
269         } elsif (/^\+/) {
270             &DEBUG("spell: '+' found => '$_'.");
271             last;
272         } elsif (/^# (.*?) 0$/) {
273             # none found.
274             last;
275         } else {
276             &DEBUG("spell: unknown: '$_'.");
277         }
278     }
279
280     &pSReply($reply);
281 }
282
283 sub nslookup {
284     my $query = shift;
285     &status("DNS Lookup: $query");
286     &DNS($query);
287 }
288
289 sub tell {
290     my $args = shift;
291     my ($target, $tell_obj) = ('','');
292     my $dont_tell_me    = 0;
293     my $reply;
294
295     ### is this fixed elsewhere?
296     $args =~ s/\s+/ /g;         # fix up spaces.
297     $args =~ s/^\s+|\s+$//g;    # again.
298
299     # this one catches most of them
300     if ($args =~ /^(\S+) (-?)about (.*)$/i) {
301         $target         = $1;
302         $tell_obj       = $3;
303         $dont_tell_me   = ($2) ? 1 : 0;
304
305         $tell_obj       = $who  if ($tell_obj =~ /^(me|myself)$/i);
306         $query          = $tell_obj;
307     } elsif ($args =~ /^(\S+) where (\S+) can (\S+) (.*)$/i) {
308         # i'm sure this could all be nicely collapsed
309         $target         = $1;
310         $tell_obj       = $4;
311         $query          = $tell_obj;
312
313     } elsif ($args =~ /^(\S+) (what|where) (.*?) (is|are)[.?!]*$/i) {
314         $target         = $1;
315         $qWord          = $2;
316         $tell_obj       = $3;
317         $verb           = $4;
318         $query          = "$qWord $verb $tell_obj";
319
320     } elsif ($args =~ /^(.*?) to (\S+)$/i) {
321         $target         = $3;
322         $tell_obj       = $2;
323         $query          = $tell_obj;
324     }
325
326     # check target type. Deny channel targets.
327     if ($target !~ /^$mask{nick}$/ or $target =~ /^$mask{chan}$/) {
328         &msg($who,"No, $who, I won't. (target invalid?)");
329         return;
330     }
331
332     $target     = $talkchannel  if ($target =~ /^us$/i);
333     $target     = $who          if ($target =~ /^(me|myself)$/i);
334
335     &status("tell: target = $target, query = $query");
336
337     # "intrusive".
338 #    if ($target !~ /^$mask{chan}$/ and !&IsNickInAnyChan($target)) {
339 #       &msg($who, "No, $target is not in any of my chans.");
340 #       return;
341 #    }
342
343     # self.
344     if ($target =~  /^\Q$ident\E$/i) {
345         &msg($who, "Isn't that a bit silly?");
346         return;
347     }
348
349     my $oldwho          = $who;
350     my $oldmtype        = $msgType;
351     $who                = $target;
352     my $result = &doQuestion($tell_obj);
353         # ^ returns '0' if nothing was found.
354     $who                = $oldwho;
355
356     # no such factoid.
357     if (!defined $result || $result =~ /^0?$/) {
358         $who            = $target;
359         $msgType        = "private";
360
361         # support command redirection.
362         # recursive cmdHooks aswell :)
363         my $done = 0;
364         $done++ if &parseCmdHook("main", $tell_obj);
365         $done++ if &parseCmdHook("extra", $tell_obj);
366         $message        = $tell_obj;
367         $done++ unless (&Modules());
368
369         &VERB("tell: setting old values of who and msgType.",2);
370         $who            = $oldwho;
371         $msgType        = $oldmtype;
372
373         if ($done) {
374             &msg($who, "told $target about CMD '$tell_obj'");
375         } else {
376             &msg($who, "i dunno what is '$tell_obj'.");
377         }
378
379         return;
380     }
381
382     # success.
383     &status("tell: <$who> telling $target about $tell_obj.");
384     if ($who ne $target) {
385         if ($dont_tell_me) {
386             &msg($who, "told $target about $tell_obj.");
387         } else {
388             &msg($who, "told $target about $tell_obj ($result)");
389         }
390
391         $reply = "$who wants you to know: $result";
392     } else {
393         $reply = "telling yourself: $result";
394     }
395
396     &msg($target, $reply);
397 }
398
399 sub DNS {
400     my $dns = shift;
401     my($match, $x, $y, $result);
402     my $pid;
403     $dns =~ s/^\s+|\s+$//g;
404
405     if (!defined $dns or $dns =~ /^\s*$/ or $dns =~ / /) {
406         &help("dns");
407         return;
408     }
409
410     if ($dns =~ /(\d+\.\d+\.\d+\.\d+)/) {
411         $match = $1;
412         &status("DNS query by IP address: $match");
413
414         $y = pack('C4', split(/\./, $match));
415         $x = (gethostbyaddr($y, &AF_INET));
416
417         if ($x !~ /^\s*$/) {
418             $result = $match." is ".$x unless ($x =~ /^\s*$/);
419         } else {
420             $result = "I can't seem to find that address in DNS";
421         }
422
423     } else {
424
425         &status("DNS query by name: $dns");
426         $x = join('.',unpack('C4',(gethostbyname($dns))[4]));
427
428         if ($x !~ /^\s*$/) {
429             $result = $dns." is ".$x;
430         } else {
431             $result = "I can\'t find that machine name";
432         }
433     }
434
435     &performReply($result);
436 }
437
438 sub countryStats {
439     if (exists $cache{countryStats}) {
440         &msg($who,"countrystats is already running!");
441         return;
442     }
443
444     if ($chan eq "") {
445         $chan = $_[0];
446     }
447
448     if ($chan eq "") {
449         &help("countrystats");
450         return;
451     }
452
453     $conn->who($chan);
454     $cache{countryStats}{chan}  = $chan;
455     $cache{countryStats}{mtype} = $msgType;
456     $cache{countryStats}{who}   = $who;
457     $cache{on_who_Hack}         = 1;
458 }
459
460 sub do_countrystats {
461     $chan       = $cache{countryStats}{chan};
462     $msgType    = $cache{countryStats}{mtype};
463     $who        = $cache{countryStats}{who};
464
465     my $total   = 0;
466     my %cstats;
467     foreach (keys %{ $cache{nuhInfo} }) {
468         my $h = $cache{nuhInfo}{$_}{Host};
469
470         if ($h =~ /^.*\.(\D+)$/) {      # host
471             $cstats{$1}++;
472         } else {                        # ip
473             $cstats{unresolve}++;
474         }
475         $total++;
476     }
477     my %count;
478     foreach (keys %cstats) {
479         $count{ $cstats{$_} }{$_} = 1;
480     }
481
482     my @list;
483     foreach (sort {$b <=> $a} keys %count) {
484         my $str = join(", ", sort keys %{ $count{$_} });
485 #       push(@list, "$str ($_)");
486         my $perc        = sprintf("%.01f", 100 * $_ / $total);
487         $perc           =~ s/\.0+$//;
488         push(@list, "$str ($_, $perc %)");
489     }
490
491     # TODO: move this into a scheduler like nickometer
492     $msgType    = "private";
493     &pSReply( &formListReply(0, "Country Stats ", @list) );
494
495     delete $cache{countryStats};
496     delete $cache{on_who_Hack};
497 }
498
499 ###
500 ### amalgamated commands.
501 ###
502
503 sub userCommands {
504     # conversion: ascii.
505     if ($message =~ /^(asci*|chr) (\d+)$/) {
506         &DEBUG("ascii/chr called ...");
507         return unless (&hasParam("allowConv"));
508
509         &DEBUG("ascii/chr called");
510
511         $arg    = $2;
512         $result = chr($arg);
513         $result = "NULL"        if ($arg == 0);
514
515         &performReply( sprintf("ascii %s is '%s'", $arg, $result) );
516
517         return;
518     }
519
520     # conversion: ord.
521     if ($message =~ /^ord(\s+(.*))$/) {
522         return unless (&hasParam("allowConv"));
523
524         $arg = $2;
525
526         if (!defined $arg or length $arg != 1) {
527             &help("ord");
528             return;
529         }
530
531         if (ord($arg) < 32) {
532             $arg = chr(ord($arg) + 64);
533             if ($arg eq chr(64)) {
534                 $arg = 'NULL';
535             } else {
536                 $arg = '^'.$arg;
537             }
538         }
539
540         &performReply( sprintf("'%s' is ascii %s", $arg, ord $arg) );
541         return;
542     }
543
544     # hex.
545     if ($message =~ /^hex(\s+(.*))?$/i) {
546         return unless (&hasParam("allowConv"));
547         my $arg = $2;
548
549         if (!defined $arg) {
550             &help("hex");
551             return;
552         }
553
554         if (length $arg > 80) {
555             &msg($who, "Too long.");
556             return;
557         }
558
559         my $retval;
560         foreach (split //, $arg) {
561             $retval .= sprintf(" %X", ord($_));
562         }
563
564         &pSReply("$arg is$retval");
565
566         return;
567     }
568
569     # crypt.
570     if ($message =~ /^crypt(\s+(.*))?$/i) {
571         my @args        = split /\s+/, $2;
572
573         if (!scalar @args or scalar @args > 2) {
574             &help("crypt");
575             return;
576         }
577
578         if (scalar @args == 2) {
579             if (length $args[0] != 2) {
580                 &msg($who, "invalid format...");
581                 return;
582             }
583
584             &pSReply( crypt($args[1], $args[0]) );
585         } else {
586             &pSReply( &mkcrypt($args[0]) );
587         }
588
589         return;
590     }
591
592     # cycle.
593     if ($message =~ /^(cycle)(\s+(\S+))?$/i) {
594         return unless (&hasFlag("o"));
595         my $chan = lc $3;
596
597         if ($chan eq "") {
598             if ($msgType =~ /public/) {
599                 $chan = $talkchannel;
600                 &DEBUG("cycle: setting chan to '$chan'.");
601             } else {
602                 &help("cycle");
603                 return;
604             }
605         }
606
607         if (&validChan($chan) == 0) {
608             &msg($who,"error: invalid channel \002$chan\002");
609             return;
610         }
611
612         &msg($chan, "I'm coming back. (courtesy of $who)");
613         &part($chan);
614 ###     &ScheduleThis(5, "getNickInUse") if (@_);
615         &status("Schedule rejoin in 5secs to $chan by $who.");
616         $conn->schedule(5, sub { &joinchan($chan); });
617
618         return;
619     }
620
621     # reload.
622     if ($message =~ /^reload$/i) {
623         return unless (&hasFlag("n"));
624
625         &status("USER reload $who");
626         &pSReply("reloading...");
627         my $modules = &reloadAllModules();
628         &pSReply("reloaded:$modules");
629         return;
630     }
631
632     # redir.
633     if ($message =~ /^redir(\s+(.*))?/i) {
634         return unless (&hasFlag("o"));
635         my $factoid = $2;
636
637         if (!defined $factoid) {
638             &help("redir");
639             return;
640         }
641
642         my $val  = &getFactInfo($factoid, "factoid_value");
643         if (!defined $val or $val eq "") {
644             &msg($who, "error: '$factoid' does not exist.");
645             return;
646         }
647         &DEBUG("val => '$val'.");
648         my @list = &searchTable("factoids", "factoid_key",
649                                         "factoid_value", "^$val\$");
650
651         if (scalar @list == 1) {
652             &msg($who, "hrm... '$factoid' is unique.");
653             return;
654         }
655         if (scalar @list > 5) {
656             &msg($who, "A bit too many factoids to be redirected, hey?");
657             return;
658         }
659
660         my @redir;
661         &status("Redirect '$factoid' (". ($#list) .")...");
662         for (@list) {
663             my $x = $_;
664             next if (/^\Q$factoid\E$/i);
665
666             &status("  Redirecting '$_'.");
667             my $was = &getFactoid($_);
668             if ($was =~ /<REPLY> see/i) {
669                 &status("warn: not redirecting a redirection.");
670                 next;
671             }
672
673             &DEBUG("  was '$was'.");
674             push(@redir,$x);
675             &setFactInfo($x, "factoid_value", "<REPLY> see $factoid");
676         }
677         &status("Done.");
678
679         &msg($who, &formListReply(0, "'$factoid' is redirected to by '", @redir));
680
681         return;
682     }
683
684     # rot13 it.
685     if ($message =~ /^rot13(\s+(.*))?/i) {
686         my $reply = $2;
687
688         if (!defined $reply) {
689             &help("rot13");
690             return;
691         }
692
693         $reply =~ y/A-Za-z/N-ZA-Mn-za-m/;
694         &pSReply($reply);
695
696         return;
697     }
698
699     # cpustats.
700     if ($message =~ /^cpustats$/i) {
701         if ($^O !~ /linux/) {
702             &ERROR("cpustats: your OS is not supported yet.");
703             return;
704         }
705
706         ### poor method to get info out of file, please fix.
707         open(STAT,"/proc/$$/stat");
708         my $line = <STAT>;
709         chop $line;
710         my @data = split(/ /, $line);
711         close STAT;
712
713         # utime(13) + stime(14).
714         my $cpu_usage   = sprintf("%.01f", ($data[13]+$data[14]) / 100 );
715         # cutime(15) + cstime (16).
716         my $cpu_usage2  = sprintf("%.01f", ($data[15]+$data[16]) / 100 );
717         my $time        = time() - $^T;
718         my $raw_perc    = $cpu_usage*100/$time;
719         my $raw_perc2   = $cpu_usage2*100/$time;
720         my $perc;
721         my $perc2;
722         my $total;
723         my $ratio;
724
725         if ($raw_perc > 1) {
726             $perc       = sprintf("%.01f", $raw_perc);
727             $perc2      = sprintf("%.01f", $raw_perc2);
728             $total      = sprintf("%.01f", $raw_perc+$raw_perc2);
729         } elsif ($raw_perc > 0.1) {
730             $perc       = sprintf("%.02f", $raw_perc);
731             $perc2      = sprintf("%.02f", $raw_perc2);
732             $total      = sprintf("%.02f", $raw_perc+$raw_perc2);
733         } else {                        # <=0.1
734             $perc       = sprintf("%.03f", $raw_perc);
735             $perc2      = sprintf("%.03f", $raw_perc2);
736             $total      = sprintf("%.03f", $raw_perc+$raw_perc2);
737         }
738         $ratio  = sprintf("%.01f", 100*$perc/($perc+$perc2) );
739
740         &pSReply("Total CPU usage: \002$cpu_usage\002 s ... ".
741                 "Total used: \002$total\002 % ".
742                 "(parent/child ratio: $ratio %)"
743         );
744
745         return;
746     }
747
748     # ircstats.
749     if ($message =~ /^ircstats?$/i) {
750         $ircstats{'TotalTime'}  ||= 0;
751         $ircstats{'OffTime'}    ||= 0;
752
753         my $count       = $ircstats{'ConnectCount'};
754         my $format_time = &Time2String(time() - $ircstats{'ConnectTime'});
755         my $total_time  = time() - $ircstats{'ConnectTime'} +
756                                 $ircstats{'TotalTime'};
757         my $reply;
758
759         my $connectivity = 100 * ($total_time - $ircstats{'OffTime'}) /
760                                 $total_time;
761         my $p = sprintf("%.03f", $connectivity);
762         $p =~ s/(\.\d*)0+$/$1/;
763         if ($p =~ s/\.0$//) {
764             # this should not happen... but why...
765         } else {
766             $p =~ s/\.$//
767         }
768
769         if ($total_time != (time() - $ircstats{'ConnectTime'}) ) {
770             my $tt_format = &Time2String($total_time);
771             &DEBUG("tt_format => $tt_format");
772         }
773
774         ### RECONNECT COUNT.
775         if ($count == 1) {      # good.
776             $reply = "I'm connected to $ircstats{'Server'} and have been so".
777                 " for $format_time";
778         } else {
779             $reply = "Currently I'm hooked up to $ircstats{'Server'} but only".
780                 " for $format_time.  ".
781                 "I had to reconnect \002$count\002 times.".
782                 "   Connectivity: $p %";
783         }
784
785         ### REASON.
786         my $reason = $ircstats{'DisconnectReason'};
787         if (defined $reason) {
788             $reply .= ".  I was last disconnected for '$reason'.";
789         }
790
791         &pSReply($reply);
792
793         return;
794     }
795
796     # status.
797     if ($message =~ /^statu?s$/i) {
798         my $startString = scalar(gmtime $^T);
799         my $upString    = &Time2String(time() - $^T);
800         my ($puser,$psystem,$cuser,$csystem) = times;
801         my $factoids    = &countKeys("factoids");
802         my $forks = 0;
803         foreach (keys %forked) {
804             $forks += scalar keys %{ $forked{$_} };
805         }
806         $forks /= 2;
807         $count{'Commands'}      = 0;
808         foreach (keys %cmdstats) {
809             $count{'Commands'} += $cmdstats{$_};
810         }
811
812         &pSReply(
813         "Since $startString, there have been".
814           " \002$count{'Update'}\002 ".
815                 &fixPlural("modification", $count{'Update'}).
816           ", \002$count{'Question'}\002 ".
817                 &fixPlural("question",$count{'Question'}).
818           ", \002$count{'Dunno'}\002 ".
819                 &fixPlural("dunno",$count{'Dunno'}).
820           ", \002$count{'Moron'}\002 ".
821                 &fixPlural("moron",$count{'Moron'}).
822           " and \002$count{'Commands'}\002 ".
823                 &fixPlural("command",$count{'Commands'}).
824           ".  I have been awake for $upString this session, and ".
825           "currently reference \002$factoids\002 factoids.  ".
826           "I'm using about \002$memusage\002 ".
827           "kB of memory. With \002$forks\002 active ".
828                 &fixPlural("fork",$forks).
829           ". Process time user/system $puser/$psystem child $cuser/$csystem"
830         );
831
832         return;
833
834         my %hash = &sqlSelectColHash("stats", "nick,counter",
835                 { type => "cmdstats" }, 1);
836 # ORDER won't be retained in a hash
837 #                       " ORDER BY counter DESC", 1);
838
839 if (0) {
840         foreach (keys %hash) {
841             my $i = $_;
842             foreach (keys %{ $hash{$i} }) {
843                 &DEBUG("cmdstats: $hash{$i}{$_} = $_");
844             }
845         }
846         &DEBUG("end of cmdstats.");
847 }
848
849         return;
850     }
851
852     # wantNick. xk++
853 #    if ($message =~ /^wantNick(\+)?$/i) {
854 #       my ($force) = ($1) ? 1 : 0;
855 #       $force = 0 unless (&IsFlag("n"));
856 #
857 #       # cannot trust Net::IRC's nick() (TimRiker asks why?)
858 #       if ($param{'ircNick'} eq $ident) {
859 #           &msg($who, "I hope you're right. I'll try anyway.");
860 #           &DEBUG("ircNick => $param{'ircNick'}");
861 #           &DEBUG("ident => $ident");
862 #       }
863 #
864 #       # fallback check, I guess.  needed?
865 #       if (! &IsNickInAnyChan( $param{'ircNick'} ) ) {
866 #           my $str = "attempting to change nick to $param{'ircNick'}";
867 #           &status($str);
868 #           &msg($who, $str);
869 #           &nick($param{ 'ircNick' });
870 #           return;
871 #       }
872 #
873 #       # idea from dondelecarlo :)
874 #       # TODO: use cache{nickserv}
875 #       if ($param{'nickServ_pass'}) {
876 #           return if ($param{'ircNick'} eq $ident or $force == 0);
877 #
878 #           &status("someone is using our nick; GHOSTing");
879 #           &msg($who, "using GHOST on $param{'ircNick'}.");
880 #           &msg("NickServ", "GHOST $param{'ircNick'} $param{'nickServ_pass'}");
881 #
882 #           $conn->schedule(5, sub {
883 #               &status("going to change nick after GHOST.");
884 #               &nick( $param{'ircNick'} );
885 #           } );
886 #
887 #           return;
888 #       }
889 #
890 #       return;
891 #   }
892
893     return "CONTINUE";
894 }
895
896 1;