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