2 # User Command Extension Stubs
5 if (&IsParam("useStrict")) { use strict; }
7 use vars qw(@W3Search_engines $W3Search_regex);
8 @W3Search_engines = qw(AltaVista Dejanews Excite Gopher HotBot Infoseek
9 Lycos Magellan PLweb SFgate Simple Verity Google);
10 $W3Search_regex = join '|', @W3Search_engines;
11 $babel::lang_regex = ""; # lame fix.
13 ### PROPOSED COMMAND HOOK IMPLEMENTATION.
14 # addCmdHook('TEXT_HOOK', $code_ref,
17 # Identifier => 'config_label',
18 # Help => 'help_label',
19 # Cmdstats => 'text_label',)
22 # addCmdHook('d?find', (
23 # CODEREF => \&debianFind(),
25 # Forker => 1, # if simple function.
26 # Identifier => "debian",
28 # Cmdstats => "Debian Search",) );
34 my ($ident, %hash) = @_;
36 &DEBUG("aCH: added $ident to command hooks.");
37 $cmdhooks{$ident} = \%hash;
42 my @args = split(' ', $message);
46 foreach (keys %cmdhooks) {
48 &DEBUG("cmdhooks{$ident} => ...");
50 next unless ($args[0] =~ /^$ident$/i);
51 shift(@args); # just gotta do it.
53 &DEBUG("pCH: MATCHED!");
54 my %hash = %{ $cmdhooks{$ident} };
57 foreach (keys %hash) {
58 &DEBUG(" $ident->$_ => '$hash{$_}'.");
62 if (exists $hash{'Identifier'}) {
63 return $noreply unless (&hasParam($hash{'Identifier'}));
66 ### FORKER,IDENTIFIER,CODEREF.
67 if (exists $hash{'Forker'}) {
68 &Forker($hash{'Identifier'}, sub { \&{$hash{'CODEREF'}}(@args) } );
72 if (exists $hash{'Cmdstats'}) {
73 $cmdstats{$hash{'Cmdstats'}}++;
79 &DEBUG("pCH: ended.");
83 &addCmdHook('d?bugs', ('CODEREF' => 'debianBugs',
84 'Forker' => 1, 'Identifier' => 'debianExtra',
85 'Cmdstats' => 'Debian Bugs') );
86 &addCmdHook('dauthor', ('CODEREF' => 'Debian::searchAuthor',
87 'Forker' => 1, 'Identifier' => 'debian',
88 'Cmdstats' => 'Debian Author Search', 'Help' => "dauthor" ) );
89 &addCmdHook('(d|search)desc', ('CODEREF' => 'Debian::searchDesc',
90 'Forker' => 1, 'Identifier' => 'debian',
91 'Cmdstats' => 'Debian Desc Search', 'Help' => "ddesc" ) );
92 &addCmdHook('dincoming', ('CODEREF' => 'Debian::generateIncoming',
93 'Forker' => 1, 'Identifier' => 'debian' ) );
94 &addCmdHook('dstats', ('CODEREF' => 'Debian::infoStats',
95 'Forker' => 1, 'Identifier' => 'debian',
96 'Cmdstats' => 'Debian Statistics' ) );
97 &addCmdHook('d?contents', ('CODEREF' => 'Debian::searchContents',
98 'Forker' => 1, 'Identifier' => 'debian',
99 'Cmdstats' => 'Debian Contents Search', 'Help' => "contents" ) );
100 &addCmdHook('d?find', ('CODEREF' => 'Debian::DebianFind',
101 'Forker' => 1, 'Identifier' => 'debian',
102 'Cmdstats' => 'Debian Search', 'Help' => "find" ) );
106 if (!defined $message) {
107 &WARN("Modules: message is undefined. should never happen.");
111 # babel bot: Jonathan Feinberg++
112 if (&IsParam("babelfish") and $message =~ m{
114 (?:babel(?:fish)?|x|xlate|translate)
116 (to|from) # direction of translation (through)
118 ($babel::lang_regex)\w* # which language?
120 (.+) # The phrase to be translated
123 &Forker("babelfish", sub { &babel::babelfish(lc $1, lc $2, $3); } );
125 $cmdstats{'BabelFish'}++;
129 # cookie (random). xk++
130 if ($message =~ /^(cookie|random)(\s+(.*))?$/i) {
131 return $noreply unless (&hasParam("cookie"));
135 # lets find that secret cookie.
136 my $target = $talkchannel;
137 $target = $who if ($msgType ne 'public');
139 my $cookiemsg = &getRandom(keys %{$lang{'cookie'}});
141 ### WILL CHEW TONS OF MEM.
142 ### TODO: convert this to a Forker function!
144 my @list = &searchTable("factoids", "factoid_key", "factoid_value", $arg);
145 $key = &getRandom(@list);
146 $val = &getFactInfo("factoids", $key, "factoid_value");
148 ($key,$value) = &randKey("factoids","factoid_key,factoid_value");
151 $cookiemsg =~ s/##KEY/\002$key\002/;
152 $cookiemsg =~ s/##VALUE/$value/;
153 $cookiemsg =~ s/##WHO/$who/;
154 $cookiemsg =~ s/\$who/$who/; # cheap fix.
155 $cookiemsg =~ s/(\S+)?\s*<\S+>/$1 /;
156 $cookiemsg =~ s/\s+/ /g;
158 if ($cookiemsg =~ s/^ACTION //i) {
159 &action($target, $cookiemsg);
161 &msg($target, $cookiemsg);
164 $cmdstats{'Random Cookie'}++;
168 if (&IsParam("debian")) {
169 my $debiancmd = 'conflicts?|depends?|desc|file|info|provides?';
170 $debiancmd .= '|recommends?|suggests?|maint|maintainer';
171 if ($message =~ /^($debiancmd)(\s+(.*))?$/i) {
174 if (defined $package) {
175 &Forker("debian", sub { &Debian::infoPackages($1, $package); } );
185 if ($message =~ /^dict(\s+(.*))?$/i) {
186 return $noreply unless (&hasParam("dict"));
189 $query =~ s/^[\s\t]+//;
190 $query =~ s/[\s\t]+$//;
191 $query =~ s/[\s\t]+/ /;
193 if (!defined $query) {
198 if (length $query > 30) {
199 &msg($who,"dictionary word is too long.");
203 &Forker("dict", sub { &Dict::Dict($query); } );
210 if ($message =~ /^(fm|freshmeat)(\s+(.*))?$/i) {
211 return $noreply unless (&hasParam("freshmeat"));
215 if (!defined $query) {
217 &msg($who, "I have \002".&countKeys("freshmeat")."\002 entries.");
221 &loadMyModule($myModules{'freshmeat'});
222 &Freshmeat::Freshmeat($query);
224 $cmdstats{'Freshmeat'}++;
228 # google searching. Simon++
229 if (&IsParam("wwwsearch") and $message =~ /^(?:search\s+)?($W3Search_regex)\s+for\s+['"]?(.*?)['"]?\s*\?*$/i) {
230 return $noreply unless (&hasParam("wwwsearch"));
232 &Forker("wwwsearch", sub { &W3Search::W3Search($1,$2,$param{'wwwsearch'}); } );
234 $cmdstats{'WWWSearch'}++;
238 # insult server. patch thanks to michael@limit.org
239 if ($message =~ /^insult(\s+(\S+))?$/) {
240 return $noreply unless (&hasParam("insult"));
243 if (!defined $person) {
248 &Forker("insult", sub { &Insult::Insult($person); } );
254 if ($message =~ /^kernel$/i) {
255 return $noreply unless (&hasParam("kernel"));
257 &Forker("kernel", sub { &Kernel::Kernel(); } );
259 $cmdstats{'Kernel'}++;
263 # LART. originally by larne/cerb.
264 if ($message =~ /^lart(\s+(.*))?$/i) {
265 return $noreply unless (&hasParam("lart"));
266 my ($target) = &fixString($2);
268 if (!defined $target) {
274 my $chan = $talkchannel;
275 if ($msgType eq 'private') {
276 if ($target =~ /^($mask{chan})\s+(.*)$/) {
281 &msg($who, "error: invalid format or missing arguments.");
287 my $line = &getRandomLineFromFile($bot_misc_dir. "/blootbot.lart");
289 if ($target =~ /^(me|you|itself|\Q$ident\E)$/i) {
290 $line =~ s/WHO/$who/g;
292 $line =~ s/WHO/$target/g;
294 $line .= ", courtesy of $who" if ($extra);
296 &action($chan, $line);
298 &status("lart: error reading file?");
304 # Search factoid extensions by 'author'. xk++
305 if ($message =~ /^listauth(\s+(\S+))?$/i) {
306 return $noreply unless (&hasParam("search"));
310 if (!defined $query) {
315 &loadMyModule($myModules{'factoids'});
316 &performStrictReply( &CmdListAuth($query) );
320 # list{keys|values}. xk++. Idea taken from #linuxwarez@EFNET
321 if ($message =~ /^list(\S+)( (.*))?$/i) {
322 return $noreply unless (&hasParam("search"));
324 my $thiscmd = lc($1);
327 $thiscmd =~ s/^vals$/values/;
328 return $noreply if ($thiscmd ne "keys" && $thiscmd ne "values");
331 if (!defined $args) {
332 &help("list". $thiscmd);
336 if (length $args == 1) {
337 &msg($who,"search string is too short.");
341 &Forker("search", sub { &Search::Search($thiscmd, $args); } );
343 $cmdstats{'Factoid Search'}++;
347 # Nickometer. Adam Spiers++
348 if ($message =~ /^(?:lame|nick)ometer(?: for)? (\S+)/i) {
349 return $noreply unless (&hasParam("nickometer"));
351 my $term = (lc $1 eq 'me') ? $who : $1;
354 &loadMyModule($myModules{'nickometer'});
355 my $percentage = &nickometer($term);
357 if ($percentage =~ /NaN/) {
358 $percentage = "off the scale";
360 $percentage = sprintf("%0.4f", $percentage);
361 $percentage =~ s/\.?0+$//;
365 if ($msgType eq 'public') {
366 &say("'$term' is $percentage lame, $who");
368 &msg($who, "the 'lame nick-o-meter' reading for $term is $percentage, $who");
375 if ($message =~ /^quote(\s+(\S+))?$/i) {
376 return $noreply unless (&hasParam("quote"));
385 &Forker("quote", sub { &Quote::Quote($query); } );
387 $cmdstats{'Quote'}++;
392 if ($message =~ /^rootWarn$/i) {
393 return $noreply unless (&hasParam("rootWarn"));
395 &loadMyModule($myModules{'rootwarn'});
396 &performStrictReply( &CmdrootWarn() );
401 if ($message =~ /^seen(\s+(\S+))?$/) {
402 return $noreply unless (&hasParam("seen"));
405 if (!defined $person) {
408 my $i = &countKeys("seen");
409 &msg($who,"there ". &fixPlural("is",$i) ." \002$i\002 ".
410 "seen ". &fixPlural("entry",$i) ." that I know of.");
418 &seenFlush(); # very evil hack. oh well, better safe than sorry.
420 ### TODO: Support &dbGetRowInfo(); like in &FactInfo();
421 my $select = "nick,time,channel,host,message";
422 if ($person eq "random") {
423 @seen = &randKey("seen", $select);
425 @seen = &dbGet("seen", "nick", $person, $select);
428 if (scalar @seen < 2) {
430 &DEBUG("seen: _ => '$_'.");
432 &performReply("i haven't seen '$person'");
438 ### TODO: multi channel support. may require &IsNick() to return
439 ### all channels or something.
440 my @chans = &GetNickInChans($seen[0]);
442 $reply = "$seen[0] is currently on";
446 next unless (exists $userstats{lc $seen[0]}{'Join'});
447 $reply .= " (".&Time2String(time() - $userstats{lc $seen[0]}{'Join'}).")";
450 if (&IsParam("seenStats")) {
452 $i = $userstats{lc $seen[0]}{'Count'};
453 $reply .= ". Has said a total of \002$i\002 messages" if (defined $i);
454 $i = $userstats{lc $seen[0]}{'Time'};
455 $reply .= ". Is idling for ".&Time2String(time() - $i) if (defined $i);
458 my $howlong = &Time2String(time() - $seen[1]);
459 $reply = "$seen[0] <$seen[3]> was last seen on IRC ".
460 "in channel $seen[2], $howlong ago, ".
461 "saying\002:\002 '$seen[4]'.";
464 &performStrictReply($reply);
468 # slashdot headlines: from Chris Tessone.
469 if ($message =~ /^slashdot$/i) {
470 return $noreply unless (&hasParam("slashdot"));
472 &Forker("slashdot", sub { &Slashdot::Slashdot() });
474 $cmdstats{'Slashdot'}++;
478 # Topic management. xk++
479 # may want to add a flag(??) for topic in the near future. -xk
480 if ($message =~ /^topic(\s+(.*))?$/i) {
481 return $noreply unless (&hasParam("topic"));
483 my $chan = $talkchannel;
484 my @args = split(/ /, $2);
487 &msg($who,"Try 'help topic'");
491 $chan = lc(shift @args) if ($msgType eq 'private');
492 my $thiscmd = shift @args;
495 if ($msgType eq 'public' && $thiscmd =~ /^#/) {
496 &msg($who, "error: channel argument is not required.");
497 &msg($who, "\002Usage\002: topic <CMD>");
501 # topic over private:
502 if ($msgType eq 'private' && $chan !~ /^#/) {
503 &msg($who, "error: channel argument is required.");
504 &msg($who, "\002Usage\002: topic #channel <CMD>");
508 if (&validChan($chan) == 0) {
509 &msg($who,"error: invalid channel \002$chan\002");
513 # for semi-outsiders.
514 if (!&IsNickInChan($who,$chan)) {
515 &msg($who, "Failed. You ($who) are not in $chan, hey?");
520 &loadMyModule($myModules{'topic'});
521 &Topic($chan, $thiscmd, join(' ', @args));
522 $cmdstats{'Topic'}++;
527 if ($message =~ /^countdown(\s+(\S+))?$/i) {
528 return $noreply unless (&hasParam("countdown"));
532 &loadMyModule($myModules{'countdown'});
535 $cmdstats{'Countdown'}++;
540 # User Information Services. requested by Flugh.
541 if ($message =~ /^u(ser)?info(\s+(.*))?$/i) {
542 return $noreply unless (&hasParam("userinfo"));
543 &loadMyModule($myModules{'userinfo'});
546 if (!defined $arg or $arg eq "") {
551 if ($arg =~ /^set(\s+(.*))?$/i) {
554 &help("userinfo set");
558 &UserInfoSet(split /\s+/, $arg, 2);
559 } elsif ($arg =~ /^unset(\s+(.*))?$/i) {
562 &help("userinfo unset");
566 &UserInfoSet($arg, "");
576 if ($message =~ /^uptime$/i) {
577 return $noreply unless (&hasParam("uptime"));
580 &msg($who, "- Uptime for $ident -");
581 &msg($who, "Now: ". &Time2String(&uptimeNow()) ." running $bot_version");
582 foreach (&uptimeGetInfo()) {
584 my $time = &Time2String($1);
587 &msg($who, "$count: $time $2");
591 $cmdstats{'Uptime'}++;
596 if ($message =~ /^wingate$/i) {
597 return $noreply unless (&hasParam("wingate"));
599 my $reply = "Wingate statistics: scanned \002"
600 .scalar(keys %wingate)."\002 hosts";
601 my $queue = scalar(keys %wingateToDo);
603 $reply .= ". I have \002$queue\002 hosts in the queue";
604 $reply .= ". Started the scan ".&Time2String(time() - $wingaterun)." ago";
607 &performStrictReply("$reply.");
613 if ($message =~ /^convert(\s+(.*))?$/i) {
614 return $noreply unless (&hasParam("units"));
623 ($from,$to) = ($1,$2) if ($str =~ /^(.*) to (.*)$/);
624 ($from,$to) = ($2,$1) if ($str =~ /^(.*) from (.*)$/);
625 if (!defined $from or !defined $to or $to eq "" or $from eq "") {
626 &msg($who, "Invalid format!");
631 &Forker("units", sub { &Units::convertUnits($from, $to); } );
636 # do nothing and let the other routines have a go