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