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