]> git.donarmstrong.com Git - infobot.git/commitdiff
merge in changes from 1810:1870
authordondelelcaro <dondelelcaro@c11ca15a-4712-0410-83d8-924469b57eb5>
Tue, 25 Aug 2009 23:35:13 +0000 (23:35 +0000)
committerdondelelcaro <dondelelcaro@c11ca15a-4712-0410-83d8-924469b57eb5>
Tue, 25 Aug 2009 23:35:13 +0000 (23:35 +0000)
git-svn-id: https://svn.code.sf.net/p/infobot/code/branches/don/dpkg@1871 c11ca15a-4712-0410-83d8-924469b57eb5

33 files changed:
BUGS
ChangeLog
FAQ
INSTALL
README
README.quick [deleted file]
THANKS
TODO
VERSION
files/infobot.help
files/sample/infobot.chan
files/sample/infobot.config
scripts/factpack.pl [new file with mode: 0755]
scripts/insertDB.pl [deleted file]
src/CommandStubs.pl
src/Factoids/Core.pl
src/Factoids/Question.pl
src/Factoids/Statement.pl
src/IRC/Irc.pl
src/IRC/IrcHooks.pl
src/IRC/Schedulers.pl
src/IRC/UserDCC.pl [new file with mode: 0644]
src/Misc.pl
src/Modules/BZFlag.pl [changed mode: 0755->0644]
src/Modules/Google.pl [new file with mode: 0644]
src/Modules/UserDCC.pl [deleted file]
src/Modules/W3Search.pl [deleted file]
src/Modules/babelfish.pl
src/Modules/dice.pl [changed mode: 0755->0644]
src/Modules/upsidedown.pl
src/PoCiCommon.pm [new file with mode: 0644]
src/Process.pl
src/logger.pl

diff --git a/BUGS b/BUGS
index 79763d46fd7aa6d7a42d5aaa0e1f1a5ea310e84a..c3b3eb8a6ad615b6c9888dd6a4f027165621aea3 100644 (file)
--- a/BUGS
+++ b/BUGS
@@ -1,5 +1,8 @@
 Known bugs that should be dealt with soon as possible:
 
+    * Multiple nick per server is in need of some testing and make be
+    incomplete or buggy in some parts of the bot
+
     * Older CMD: foo's cannot be used or removed. Must be removed manually from
     the database with SQL
 
index 4864c93edd84be4ef1ac7e68f3fb306494008b81..ff2ced08c633538fda37d1bc4a424caa298e3290 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,50 @@
+1.5.4
+=====
+* scripts/insertDB.pl
+- renamed to factpack.pl
+- should work properly now (needs testing)
+
+* Updates to DebianExtra.pl:
+- use soap for bug_info
+- totally replace do_id with the copy from DebianBugs.pl
+- fix ||= typo
+- fix url->uri typo
+- fix -> typo
+- add some more debugging code to the do_id code
+- remove useless bug =>
+- disable debugging output of the soap object returned
+- soap returns multiple bugs in a single shot
+- use strftime on the date
+
+* check for and use of old blootbot.(users|chan) files
+
+* Retired the old broken W3Search module in favour of a working Google search
+
+* Removed +W3Search in infobot.chan, added +Google (don't forget to update your
+configs)
+
+
+1.5.3
+=====
+
+* New maxVolunteerLength to govern max size of non addressed replies
+
+* Cleaned up some of the regex code for what the bot considers a question
+
+* Fixed an obscure and undocumented variable that was disabling "query?"
+questions in non addressed mode
+
+* Fixed bug in babelfish module due to site/html changes
+
+* Added chanset and help for verstats to allow disabling of command to prevent abuse
+
+* Fixed verstats to warn the channel about who involked the command and bug that
+was causing the output to go to random channels due to loss of variable scope
+
+* Added infobot client to verstats and cleaned up a few other client regex
+
+* Fixed talkok bug with 'how are you' statement
+
 1.5.2
 =====
 
diff --git a/FAQ b/FAQ
index f7a8dcc35cb1cdc3f08117116d0d803ab3cfd7e2..6f24c06ee85c49ebeaa5e55bc10d66f861bb93a4 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -1,12 +1,49 @@
 # $Id$
 
+Q: What are factpacks and how do I install them?
+A: A factpack is a partial database of factoids that you can import into
+   your configured infobot by running the script called "factpack.pl".
+
+   # IMPORTANT: Run from the bots base directory!
+   eg: ./scripts/factpack.pl /path/to/areacodes.fact
+
+Q: Can I remove a fact pack I have installed?
+A: Yes, but at the moment, you need to manually use SQL commands in your
+   favorite SQL program. The "created_by" field in the factoids will be
+   set to the filename of the fact pack you installed. For example, if
+   you installed the "areacodes.fact" file, "created_by" would be set to
+   "areacodes.fact". The SQL to remove the above example would be:
+
+   sql> DELETE FROM factoids WHERE created_by = 'areacodes.fact';
+
+Q: Where can I download some existing fact packs?
+A: As of yet, the fact packs aren't available in SVN. You can try the
+   original infobot web site at:
+
+     http://www.infobot.org/factpacks/
+
+   NOTE: The import script can't yet handle compressed files, so you must
+   extract the factpacks in advance.
+
+Q: How can I make my own fact packs?
+A: The syntax is pretty basic. You just need a plain text file that has
+   one key/value pair per line. Comment lines begin with a "#" character
+   and are ignored, as are blank lines. Extra whitespace around key/value
+   pairs should hopefully be stripped out as well. A few example lines from
+   the areacodes.fact file:
+
+     # Sample comment here
+     area code 011 => the International Access Code
+     area code 201 => Hackensack, Morristown and Newark, New Jersey
+     area code 202 => Washington, District of Columbia
+
+
 Q: The bot exits after I run 'factstats testing' or 'kernel' or anything
    that uses fork(). Is this a bug in the bot?
 
 A: No, this is not a bug in the bot but rather Net::IRC.  A cheap hack is
    to edit /usr/lib/perl5/Net/IRC/Connection.pm, search for DESTROY, and
    comment out '$self->quit();'
-A: Apply the patches in the patches/ directory.
 
 
 Q: I notice that, in the bot logs, the bot attempts to close all current
diff --git a/INSTALL b/INSTALL
index 12f1b3ea1acd5647f07a77d1dc64ac56435ffa99..9729d65a857259dc4b7a29fad9295b2ce71e1d8f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -40,28 +40,6 @@ Finally, start your bot by changing to the base dir and type:
 
 
 
-                                 -------------
-                                 -- Patches --
-                                 -------------
-
-- apply *.patch patches inside patches/
-    - cd /usr/lib/perl5/WWW/Search
-    - patch -p0 < WWW::Search::Google.patch
-
-- alternatively, move the files from patches/
-    - mv patches/Google.pm /usr/lib/perl5/WWW/Search/
-
-
-Net::IRC DCC CHAT
------------------
-Unfortunately, Net::IRC 0.70 has buggy code that does not detect DCC CHAT
-properly. to patch:
-    - cd /usr/share/perl5/Net/IRC/
-    - cat ~bot/patches/Net_IRC_Connection_pm.patch | patch -p0
-
-
-
-
                                 ----------------
                                 -- PostgreSQL --
                                 ----------------
diff --git a/README b/README
index 32197d3ccb47eda5e6bd496458fc6dcaadf6972f..4629cda7a63184c3a0e6553c7d84bae39b38590d 100644 (file)
--- a/README
+++ b/README
@@ -9,35 +9,30 @@ INTRODUCTION
 Kevin Lenzo <lenzo@cs.cmu.edu>, which is now officially rebranded back to
 infobot!  The basis of infobot is still there but _many_ wild features have
 been added.  Along the way, many issues were spotted in the original
-infobot source and fixed in this version. Many new bugs have been added as well
+infobot source and fixed in this version. Many new bugs have been added as well.
 Thanks to kevin for bringing infobot in the first place.
 
 
 FEATURES
        * Additional information stored with factoids. (factinfo)
-       * Wide range of statistics for Bot, Factoids, IRC, Debian.
+       * Wide range of statistics for Bot, Factoids, IRC, Debian
          (status, factstats, ircstats, chanstats, cmdstats)
-       * Advanced topic management. (the first cool feature)
-       * Improved factoid search, allowing search by key or value.
+       * Advanced topic management
+       * Improved factoid search, allowing search by key or value
        * Freshmeat support (freshmeat.net)
-       * Debian Contents and Packages, search and info.
-       * ChanServ/NickServ (OPN) support.
-       * WWW-Search (eg: google for BLAH)
-       * Slashdot, Kernel and Freshmeat auto-update announcements.
-       * Units conversion (provided by external module, Units-Module)
+       * Debian Contents and Packages, search and info
+       * ChanServ/NickServ (freenode) support
+       * WWW-Search (eg: google for BLAH) (NOTE: Currently broken)
+       * Slashdot, Kernel, RSS and Freshmeat auto-update announcements
+       * Units conversion (provided by external "units" program)
 
 
 DESIGN
-       - Modularity. Ability to disable IRC or Factoid support.
+       - Modularity. Ability to disable IRC or Factoid support
        - Funky pseudo Module autoloader support
-       - Eleet Forker() function
-
-
-IMPROVEMENTS
-       * log file is not opened and closed for each line of data
-               => unblocked logging is used.
-       * seen data is not flushed for each public message on IRC
-               => caching and flushing over an interval is used.
+       - Modules called via Forker() to avoid blocking
+    - Non blocking logs
+    - Seen data periodicaly flushed for efficiency
 
 
 HISTORY
@@ -77,10 +72,6 @@ However, these people do not realize the potential of open wingates.
 test all modifications properly (and extensively).  Suggestions are
 welcomed.
 
-       gp@OPN is currently working on a C version of infobot or
-blootbot, not based on the above source base.  Core factoid code and
-mysql support works - but that is it.
-
 
 MODIFICATIONS
        All modifications are that of the blootbot author unless otherwise
@@ -126,11 +117,11 @@ through DCC CHAT. /chat <BOT NICK>.  All commands must be prepended by
 
 
 UNTESTED:
-       - user statistics shown by 'seen'. bug in this?
+       - user statistics shown by 'seen'.
        - User Information Services.
        - new wingate caching/file-read code.
        - disabling IRC/factoid support code.
-       - PG supports need to be worked and thoroughly tested.
+       - PGSql support needs to be more thoroughly tested.
 
 
 CONTRIBUTIONS
@@ -139,13 +130,13 @@ modifications, aswell as giving suggestions and ideas in the early
 development stages. Bashing of modifications courtesy of larne, irq, lilo
 and \broken.
 
-       mu@OPN for the SAR (=~ s///) and Topic history patch.
+       mu@freenode for the SAR (=~ s///) and Topic history patch.
 
        someone emailed me a patch to fix up telnet but I accidently
 deleted the message together with the patch after replying to the guy. I
 hope to get that same guy to re-send me the patch...
 
-       MbM@OPN sent a patch to clean up behaviour of factoids
+       MbM@freenode sent a patch to clean up behaviour of factoids
 (adding, removing, modifying).  Thanks.
 
 
diff --git a/README.quick b/README.quick
deleted file mode 100644 (file)
index 6ffa1a2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-See INSTALL file on how to install the bot.
-
-Quick usage instructions:
--------------------------
-
-DCC CHAT:
-.+chan #chan
-.chanset #chan +autojoin
-.chanset +autojoin
-.chanunset -autojoin
-.chanset -autojoin
-
-for list of configuration options, run:
-       perl scripts/findparam.pl
-
-
-# vim:ts=4:sw=4:expandtab:tw=80
diff --git a/THANKS b/THANKS
index 20cb9494f33eb9baaa13237da948259b5eb4ea2d..3699e191f5cfa07d52826dcdcc341cf962c761ab 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -12,6 +12,10 @@ other another, or people who we feel we owe thanks to in one way or another.
 If we missed you from this list, drop us a line and we will be sure to give
 credit where it is due.
 
+#People
+-------
+
+Brett Cave (Google search)
 
 #perl@freenode.net
 ------------------
@@ -28,5 +32,9 @@ Limbic_Region
 mmap__
 jagerman
 
+#infobot@freenode.net
+---------------------
+quin - many thanks for ctcp version bug help
+
 
 # vim:ts=4:sw=4:expandtab:tw=80
diff --git a/TODO b/TODO
index 579c6c34afc11436a09ed37547f5593f33076268..cca767e9503f377747c4b6929af2b711cd5fd5b6 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,7 +1,10 @@
+Other TODO items may be listed on sourceforge. Please access it from the
+website or this link:
+
+    http://sourceforge.net/tracker/?group_id=2241
+
+
 TODO:
-       - Normalize the SQL tables a little better to reduce size and increase speed
-       - Keep the Changelog, TODO and BUGS files up to date. Clean things up a bit
-       - rename ^[+-] commands
        - remind - like this and others: http://jibble.org/reminderbot/
        - kill SHM and and move to a pipe
        - add CIA like support - http://cia.navi.cx/
@@ -32,26 +35,17 @@ TODO:
                - add &checkSet() or &_chanset();
        - attempt to move userDCC to hooks.
                - need to modify parseCmdHooks for user flags?
-       - make timers below 5 or 10 mins non-random values.
        - create a .csv import/export program
--- EFFORT 1.
        - make IRC/Schedulers.pl work 100%.
                - intervals must be multiple of the smallest one otherwise
                  auto-fixed.
                - make intervals chan-specific
                        - need to store info in $sched{$what}{$chan} =
                        time(); when last run or next run?
-
-Other TODO items may be listed on sourceforge. Please access it from the
-website or this link:
-http://sourceforge.net/pm/task.php?group_id=2241
-
-----------------------------------------------------
------------- FUTURE, NON-IMPORTANT
        - <greycat> ~country ua
-       - <irq_w> xk: add it :) and my imdb feature :)
-       - <greycat> xk: and ~bugs :)
-       - "HACKING" text file, documentation of where things start,
+         <irq_w> xk: add it :) and my imdb feature :)
+         <greycat> xk: and ~bugs :)    
+    - "HACKING" text file, documentation of where things start,
          what "core" or reuseable functions are used and what for.
        - web interface
        - on join message - customizeable, option.
diff --git a/VERSION b/VERSION
index 4cda8f19edc7ffa01ba13d5dbe4909a6dc2ce3c8..0475227f296e79aacc0f6cecf14adbfd245af9e0 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.5.2
+1.5.4 (SVN)
index 5b3853c1e82971c6d42221021e2744d3fdc3402f..0d37789738fa8cea308f789587eb5c148ca29003 100644 (file)
@@ -1,5 +1,6 @@
-# Revised: 20071016
+# Revised: 20090320
 #  Author: Tim Riker <Tim@Rikers.org>
+#  Contributors: Simon C., Tim M., others (see AUTHORS)
 ###
 
 # Special entry
@@ -205,6 +206,11 @@ freshmeat: D: Frontend to www.freshmeat.net
 freshmeat: U: ## <query>
 freshmeat: E: ## infobot
 
+google: D: What Is: Google Search frontend
+google: D: Configuration: "chanset [_default or channelname] +Google"
+google: U: ## google <query>
+google: E: ## google infobot
+
 hex: D: Convert ascii to hex
 hex: U: ## <string>
 hex: E: ## carrot
@@ -481,6 +487,11 @@ userdel: D: Administrative command to remove a user from the .users file
 userdel: U: ## <user>
 userdel: E: ## SomeAccount
 
+verstats: D: Commnd to CTCP VERSION the specified channel for client statistics
+verstats: U: ## <channel>
+verstats: E: ## #infobot
+verstats: E: < infobot> IRC Client versions for #infobot (2): unknown/cloak - 3 (75%) ;; irssi - 1 (25%).
+
 wantnick: If someone's taken my nick (I hope not) and I'm using some temporary nick, I can change back to my original nick if it's not taken (again).
 
 whois: D: List available information for an account on the bot
index adb5dc1f5dd4597314a0f812818bad689d506d07..07a2811ca327f307364cc0fcb9f5137661d4798c 100644 (file)
@@ -37,6 +37,7 @@ _default
     +Dict
     +Exchange
     +Factoids
+    +Google
     +HTTPDtype
     +Kernel
     +Math
@@ -48,7 +49,6 @@ _default
     +Topic
     +Units
     +UserInfo
-    +W3Search
     +Weather
     +Zippy
     addressCharacter ~
@@ -76,7 +76,8 @@ _default
     maxListReplyCount 15
     maxListReplyLength 400
     +md5
-    minVolunteerLength 50
+    minVolunteerLength 2
+    maxVolunteerLength 512
     +nickometer
     +pager
     +piglatin
@@ -100,6 +101,7 @@ _default
     +spell
     +tell
     +upsidedown
+    +verstats
     +wikipedia
     +wtf
     +zfi
index 073a81ed63a2cce97350eb1dab3b2d4df9547483..837461dba22a5c3b9d25db6ce9c045cd021a6fa5 100644 (file)
@@ -4,6 +4,8 @@
 #####
 # Basic IRC info
 #####
+# NOTE: If you would like multiple nick support, use the following format:
+# set ircNick          nick1,nick2,nick3
 set ircNick            infobot
 set ircUser            infobot
 set ircName            infobot experimental bot
diff --git a/scripts/factpack.pl b/scripts/factpack.pl
new file mode 100755 (executable)
index 0000000..6a6e60c
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/perl -w
+
+$| = 1;
+
+use strict;
+use File::Basename;
+use vars qw($bot_base_dir $bot_src_dir $bot_misc_dir $bot_state_dir
+  $bot_data_dir $bot_config_dir $bot_log_dir $bot_run_dir
+    $bot_pid $memusage %param
+);
+
+
+# Check for arguments
+if ( !scalar @ARGV ) {
+    print "Usage: $0 <pack1.fact> [<pack2.fact> <pack2.fact> ...]\n";
+    print "Example: $0 areacodes.fact usazips.fact\n";
+    exit 1;
+}
+
+
+# set any $bot_*_dir var's
+$bot_base_dir   = '.';
+$bot_config_dir = 'files/';
+$bot_data_dir   = 'files/';
+$bot_state_dir  = 'files/';
+$bot_run_dir    = '.';
+$bot_src_dir    = "$bot_base_dir/src";
+$bot_log_dir    = "$bot_base_dir/log";
+$bot_misc_dir   = "$bot_base_dir/files";
+$bot_pid        = $$;
+
+require "$bot_src_dir/logger.pl";
+require "$bot_src_dir/core.pl";
+require "$bot_src_dir/modules.pl";
+
+# Initialize enough to get DB access
+&setupConfig();
+&loadCoreModules();
+&loadDBModules();
+&loadFactoidsModules();
+&setup();
+
+if ( !scalar @ARGV ) {
+    print "Usage: $0 <pack1.fact> [<pack2.fact> <pack2.fact> ...]\n";
+    print "Example: $0 areacodes.fact usazips.fact\n";
+    exit 0;
+}
+
+foreach (@ARGV) {
+    next unless ( -f $_ );
+    my $file = $_;
+
+    open( IN, $file ) or die "error: cannot open $file\n";
+    print "Opened $file for input...\n";
+
+    print "inserting... ";
+    while (<IN>) {
+        chomp;
+        next if /^#/;
+        next unless (/=>/);
+
+        # Split into "key => value" pairs
+        my ($key, $value) = split(/=>/,$_,2);
+
+        # Strip extra begin/end whitespace
+        $key =~ s/^\s*(.*?)\s*$/$1/;
+        $value =~ s/^\s*(.*?)\s*$/$1/;
+        
+        # convert tabs
+        $key =~ s/\t/ /g;
+        $value =~ s/\t/ /g;
+
+        # The key needs to be lower case to match query case
+        $key = lc $key;
+
+        ### TODO: check if it already exists. if so, don't add.
+        ### TODO: combine 2 setFactInfo's into single
+        &setFactInfo( $key, "factoid_value", $value );
+        &setFactInfo( $key, "created_by", basename($file) );
+        print ":: $key ";
+    }
+
+    close IN;
+}
+print "...Done!\n";
+
+# vim:ts=4:sw=4:expandtab:tw=80
diff --git a/scripts/insertDB.pl b/scripts/insertDB.pl
deleted file mode 100755 (executable)
index 1206414..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/perl -w
-
-$| = 1;
-
-use strict;
-
-require "src/core.pl";
-require "src/logger.pl";
-require "src/modules.pl";
-require "src/Factoids/DBCommon.pl";
-
-&loadConfig( $bot_config_dir . "/infobot.config" );
-&loadDBModules();
-
-unless (@_) {
-    print "hrm.. usage\n";
-    exit 0;
-}
-
-foreach (@_) {
-    next unless ( -f $_ );
-
-    open( IN, $_ ) or die "error: cannot open $_\n";
-    print "Opened $_ for input...\n";
-
-    print "inserting... ";
-    while (<IN>) {
-        next unless (/^(.*?) => (.*)$/);
-
-        ### TODO: check if it already exists. if so, don't add.
-        &setFactInfo( $1, "factoid_value", $2 );
-        print ":: $1 ";
-    }
-
-    close IN;
-}
-
-# vim:ts=4:sw=4:expandtab:tw=80
index d3c4e4d5f8b070ffe132eeadd23cbd5267f113fd..e66e6d8dcd99b823f518444d08f3a3144b5e2dee 100644 (file)
@@ -182,17 +182,16 @@ sub Modules {
         return;
     }
 
-    # google searching. Simon++
-    my $w3search_regex = 'google';
+    # google searching -- thanks Brett Cave
     if ( $message =~
-        /^(?:search\s+)?($w3search_regex)\s+(?:for\s+)?['"]?(.*?)["']?\s*\?*$/i
+        /^(\s+)?google\s+['"]?(.*?)["']?\s*\?*$/i
       )
     {
-        return unless ( &IsChanConfOrWarn('W3Search') );
+        return unless ( &IsChanConfOrWarn('Google') );
 
-        &Forker( 'W3Search', sub { &W3Search::W3Search( $1, $2 ); } );
+        &Forker( 'Google', sub { &Google::GoogleSearch( $2 ); } );
 
-        $cmdstats{'W3Search'}++;
+        $cmdstats{'Google'}++;
         return;
     }
 
@@ -591,8 +590,14 @@ sub do_verstats {
         return;
     }
 
-    &msg( $who, "Sending CTCP VERSION to $chan; results in 60s." );
-    $conn->ctcp( 'VERSION', $chan );
+    &msg( $who,  "Sending CTCP VERSION to $chan; results in 60s." );
+    &msg( $chan, "WARNING: $who has forced me to CTCP VERSION the channel!" );
+
+    # Workaround for bug in Net::Irc, provided by quin@freenode. Details at:
+    # http://rt.cpan.org/Public/Bug/Display.html?id=11421
+    # $conn->ctcp( 'VERSION', $chan );
+    $conn->sl("PRIVMSG $chan :\001VERSION\001");
+
     $cache{verstats}{chan}    = $chan;
     $cache{verstats}{who}     = $who;
     $cache{verstats}{msgType} = $msgType;
@@ -618,7 +623,6 @@ sub do_verstats {
             my $vtotal = 0;
             my $c      = lc $cache{verstats}{chan};
             my $total  = keys %{ $channels{$c}{''} };
-            $chan    = $c;
             $who     = $cache{verstats}{who};
             $msgType = $cache{verstats}{msgType};
             delete $cache{verstats};    # sufficient?
@@ -651,8 +655,7 @@ sub do_verstats {
             }
 
             # hack. this is one major downside to scheduling.
-            $chan = $c;
-            &performStrictReply(
+            &msg($c,
                 &formListReply( 0, "IRC Client versions for $c ", @list ) );
 
             # clean up not-needed data structures.
@@ -669,7 +672,9 @@ sub verstats_flush {
         last unless ( scalar @vernicktodo );
 
         my $n = shift(@vernicktodo);
-        $conn->ctcp( 'VERSION', $n );
+        #$conn->ctcp( 'VERSION', $n );
+        # See do_verstats $conn->sl for explantaion
+        $conn->sl("PRIVMSG $n :\001VERSION\001");
     }
 
     return unless ( scalar @vernicktodo );
@@ -837,7 +842,7 @@ sub do_text_counters {
 &addCmdHook('upsidedown', ('CODEREF' => 'upsidedown::upsidedown', 'Identifier' => 'upsidedown', 'Cmdstats' => 'upsidedown', 'Forker' => 1, 'Module' => 'upsidedown') );
 &addCmdHook('Uptime', ('CODEREF' => 'uptime', 'Identifier' => 'Uptime', 'Cmdstats' => 'Uptime') );
 &addCmdHook('u(ser)?info', ('CODEREF' => 'userinfo', 'Identifier' => 'UserInfo', 'Help' => 'userinfo', 'Module' => 'UserInfo') );
-&addCmdHook('verstats', ('CODEREF' => 'do_verstats' ) );
+&addCmdHook('verstats', ('CODEREF' => 'do_verstats', 'Identifier' => 'verstats', 'Help' => 'verstats', 'Cmdstats' => 'verstats') );
 &addCmdHook('Weather', ('CODEREF' => 'Weather::Weather', 'Identifier' => 'Weather', 'Help' => 'weather', 'Cmdstats' => 'Weather', 'Forker' => 1, 'Module' => 'Weather') );
 &addCmdHook('wiki(pedia)?', ('CODEREF' => 'wikipedia::wikipedia', 'Identifier' => 'wikipedia', 'Cmdstats' => 'wikipedia', 'Forker' => 1, 'Help' => 'wikipedia', 'Module' => 'wikipedia') );
 &addCmdHook('wtf', ('CODEREF' => 'wtf::query', 'Identifier' => 'wtf', 'Cmdstats' => 'wtf', 'Forker' => 1, 'Help' => 'wtf', 'Module' => 'wtf') );
index c2552070d98886c372dd886d1046fb821e5bb3a0..7e5f26c1a2f460857d265910ce5b98374a6f1804 100644 (file)
@@ -20,7 +20,7 @@ sub validFactoid {
 
         # allow the following only if they have been made on purpose.
         if ( $rhs ne '' and $rhs !~ /^</ ) {
-            / \Q$ident$/i and last;    # someone said i'm something.
+            / \Q$ident\E$/i and last;    # someone said i'm something.
             /^i('m)? /    and last;
             /^(it|that|there|what)('s)?(\s+|$)/ and last;
             /^you('re)?(\s+|$)/                 and last;
@@ -84,7 +84,7 @@ sub validFactoid {
         $rhs =~ /( \Q$ident\E's|\Q$ident\E's )/i and last;    # ownership.
 
         # duplication.
-        $rhs =~ /^\Q$lhs /i and last;
+        $rhs =~ /^\Q$lhs\E /i and last;
         last if ( $rhs =~ /^is /i and / is$/ );
 
         $valid++;
@@ -552,12 +552,14 @@ sub FactoidStuff {
     for ($question) {
 
         # fix the string.
-        s/^hey([, ]+)where/where/i;
+        s/^where is //i;
         s/\s+\?$/?/;
-        s/^whois /who is /i
-          ;    # Must match ^, else factoids with "whois" anywhere break
-        s/where can i find/where is/i;
-        s/how about/where is/i;
+        s/^whois //i;   # Must match ^, else factoids with "whois" anywhere break
+        s/^who is //i;
+        s/^what is (a|an)?//i;
+        s/^how do i //i;
+        s/^where can i (find|get|download)//i;
+        s/^how about //i;
         s/ da / the /ig;
 
         # clear the string of useless words.
index 71ae23c171d4156d0d7fb946a92b32dc542c913e..3fdccee7aa06fe6ffaae23cce76db205828c0f26 100644 (file)
@@ -23,7 +23,6 @@ sub doQuestion {
     local ($query) = @_;
     local ($reply) = '';
     local $finalQMark = $query =~ s/\?+\s*$//;
-    $finalQMark += $query =~ s/\?\s*$//;
     $query =~ s/^\s+|\s+$//g;
 
     if ( !defined $query or $query =~ /^\s*$/ ) {
@@ -34,8 +33,16 @@ sub doQuestion {
 
     if ( !$addressed ) {
         return '' unless ($finalQMark);
-        return '' unless &IsChanConf('minVolunteerLength') > 0;
-        return '' if ( length $query < &::getChanConf('minVolunteerLength') );
+        return ''
+          if (
+            length $query <
+            &::getChanConfDefault( 'minVolunteerLength', 2, $chan ) or
+            $param{'addressing'} =~ m/require/i );
+        return ''
+          if (
+            length $query >
+            &::getChanConfDefault( 'maxVolunteerLength', 512, $chan ) or
+            $param{'addressing'} =~ m/require/i );
     }
     else {
         ### TODO: this should be caught in Process.pl?
index 2c5a8e219451af9fa2fcf8f037c608c8c502b0b3..48b10cb70944afcd70ce7c6bc0b2e95c8afec48a 100644 (file)
@@ -58,6 +58,24 @@ sub doStatement {
     if ( $in =~ /(^|\s)(is|are)(\s|$)/i ) {
         my ( $lhs, $mhs, $rhs ) = ( $`, $&, $' );
 
+        # Quit if they are over the limits. Check done here since Core.pl calls
+        # this mid sub and Question.pl needs its own check as well. NOTE: $in is
+        # used in this place since lhs and rhs are really undefined for unwanted
+        # teaching. Mainly, the "is" could be anywhere within a 510 byte or so
+        # block of text, so the total size was choosen since the sole purpose of
+        # this logic is to not hammer the db with pointless factoids that were
+        # only meant to be general conversation.
+        return ''
+          if (
+            length $in <
+            &::getChanConfDefault( 'minVolunteerLength', 2, $chan ) or
+            $param{'addressing'} =~ m/require/i ) and not $addressed;
+        return ''
+          if (
+            length $in >
+            &::getChanConfDefault( 'maxVolunteerLength', 512, $chan ) or
+            $param{'addressing'} =~ m/require/i ) and not $addressed;
+
         # allows factoid arguments to be updated. -lear.
         $lhs =~ s/^(cmd: )?(.*)/$1||'' . lc $2/e;
 
index e4c43a88916b354b38b34f59c19192d830db2a86..b3e5a90c7643180e59fe3a185512070ccc22652e 100644 (file)
@@ -117,6 +117,7 @@ sub irc {
 "Connecting to port $port of server $server ($resolve) as $mynick ..."
         );
         $args{'Nick'} = $mynick;
+        $args{'Username'} = $mynick;
         $conns{$mynick} = $irc->newconn(%args);
         if ( !defined $conns{$mynick} ) {
             &ERROR('IRC: connection failed.');
@@ -172,7 +173,7 @@ sub irc {
         $conns{$mynick}->add_global_handler( 352, \&on_who );
         $conns{$mynick}->add_global_handler( 353, \&on_names );
         $conns{$mynick}->add_global_handler( 366, \&on_endofnames );
-        $conns{$mynick}->add_global_handler( 376, \&on_endofmotd )
+        $conns{$mynick}->add_global_handler( "001", \&on_connected )
           ;    # on_connect.
         $conns{$mynick}->add_global_handler( 433, \&on_nick_taken );
         $conns{$mynick}->add_global_handler( 439, \&on_targettoofast );
@@ -522,6 +523,7 @@ sub joinchan {
     my ( $chan, $key ) = @_;
     $key ||= &getChanConf( 'chankey', $chan );
     $key ||= '';
+    my $mynick = $conn->nick();
 
     # forgot for about 2 years to implement channel keys when moving
     # over to Net::IRC...
@@ -532,7 +534,7 @@ sub joinchan {
     }
 
     #} else {
-    &status("joining $b_blue$chan $key$ob");
+    &status("$mynick joining $b_blue$chan $key$ob");
 
     return if ( $conn->join( $chan, $key ) );
     return if ( &validChan($chan) );
index af96470589624f5854bdf4d98501566ae5a73045..409b818c4e91b70287d7333996e5e79eb36131a3 100644 (file)
@@ -150,7 +150,7 @@ sub on_ison {
     &DEBUG("on_ison: x1 = '$x1', x2 => '$x2'");
 }
 
-sub on_endofmotd {
+sub on_connected {
     $conn = shift(@_);
 
     # update IRCStats.
@@ -1201,11 +1201,17 @@ sub on_crversion {
     }
     push( @vernick, $nick );
 
+    &DEBUG("on_crversion: Got '$ver' from $nick");
+
     if ( $ver =~ /bitchx/i ) {
         $ver{bitchx}{$nick} = $ver;
 
     }
-    elsif ( $ver =~ /xc\!|xchat/i ) {
+    elsif ( $ver =~ /infobot/i ) {
+        $ver{infobot}{$nick} = $ver;
+
+    }
+    elsif ( $ver =~ /(xc\!|xchat)/i ) {
         $ver{xchat}{$nick} = $ver;
 
     }
@@ -1213,20 +1219,19 @@ sub on_crversion {
         $ver{irssi}{$nick} = $ver;
 
     }
-    elsif ( $ver =~ /epic|(Third Eye)/i ) {
+    elsif ( $ver =~ /(epic|Third Eye)/i ) {
         $ver{epic}{$nick} = $ver;
 
     }
-    elsif ( $ver =~ /ircII|PhoEniX/i ) {
+    elsif ( $ver =~ /(ircII|PhoEniX)/i ) {
         $ver{ircII}{$nick} = $ver;
 
     }
     elsif ( $ver =~ /mirc/i ) {
-
-        #      &DEBUG("verstats: mirc: $nick => '$ver'.");
+        # Apparently, mIRC gets the reply as "VERSION " and doesnt like the
+        # space, so mirc matching is considered bugged.
         $ver{mirc}{$nick} = $ver;
 
-        # ok... then we get to the lesser known/used clients.
     }
     elsif ( $ver =~ /ircle/i ) {
         $ver{ircle}{$nick} = $ver;
index 925ce1e97ca12153b424c7afc827a9bbfc989072..14721dceea5844324407fda5edb39a89086ef4e4 100644 (file)
@@ -752,9 +752,9 @@ sub ircCheck {
         $conn = $conns{$_};
         my $mynick = $conn->nick();
         &DEBUG("ircCheck for $_");
-        my @join =
-          &getJoinChans(900)
-          ;    # Display with min of 900sec delay between redisplay
+        # Display with min of 900sec delay between redisplay
+        # FIXME: should only use 900sec when we are on the LAST %conns
+        my @join = &getJoinChans(900);
         if ( scalar @join ) {
             &FIXME( 'ircCheck: found channels to join! ' . join( ',', @join ) );
             &joinNextChan();
diff --git a/src/IRC/UserDCC.pl b/src/IRC/UserDCC.pl
new file mode 100644 (file)
index 0000000..157b139
--- /dev/null
@@ -0,0 +1,1520 @@
+#
+#  UserDCC.pl: User Commands, DCC CHAT.
+#      Author: dms
+#     Version: v0.2 (20010119)
+#     Created: 20000707 (from UserExtra.pl)
+#
+
+use strict;
+
+use vars qw(%users %ignore %sched %bans %mask %cache %channels %param
+  %chanconf %dcc);
+use vars qw($who $chan $message $msgType $user $chnick $conn $ident
+  $verifyUser $ucount_userfile $utime_userfile $lobotomized
+  $utime_chanfile $ucount_chanfile);
+use vars qw(@backlog);
+
+sub userDCC {
+
+    # hrm...
+    $message =~ s/\s+$//;
+
+    ### for all users.
+    # quit.
+    if ( $message =~ /^(exit|quit)$/i ) {
+
+        # do ircII clients support remote close? if so, cool!
+        &FIXME("userDCC: quit called.");
+        &dcc_close($who);
+        &status("userDCC: after dcc_close!");
+
+        return;
+    }
+
+    # who.
+    if ( $message =~ /^who$/ ) {
+        my $count   = scalar( keys %{ $dcc{'CHAT'} } );
+        my $dccCHAT = $message;
+
+        &performStrictReply("Start of who ($count users).");
+        foreach ( keys %{ $dcc{'CHAT'} } ) {
+            &performStrictReply("=> $_");
+        }
+        &performStrictReply("End of who.");
+
+        return;
+    }
+
+    ### for those users with enough flags.
+
+    if ( $message =~ /^tellme(\s+(.*))?$/i ) {
+        my $args = $2;
+        if ( $args =~ /^\s*$/ ) {
+            &help('tellme');
+            return;
+        }
+
+        my $result = &doQuestion($args);
+        &performStrictReply($result);
+
+        return;
+    }
+
+    # 4op.
+    if ( $message =~ /^4op(\s+($mask{chan}))?$/i ) {
+        return unless ( &hasFlag('o') );
+
+        my $chan = $2;
+
+        if ( $chan eq '' ) {
+            &help('4op');
+            return;
+        }
+
+        if ( !$channels{$chan}{'o'}{$ident} ) {
+            &msg( $who, "i don't have ops on $chan to do that." );
+            return;
+        }
+
+        # on non-4mode(<4) servers, this may be exploited.
+        if ( $channels{$chan}{'o'}{$who} ) {
+            rawout( "MODE $chan -o+o-o+o" . ( " $who" x 4 ) );
+        }
+        else {
+            rawout( "MODE $chan +o-o+o-o" . ( " $who" x 4 ) );
+        }
+
+        return;
+    }
+
+    # opme.
+    if ( $message =~ /^opme(\s+($mask{chan}))?$/i ) {
+        return unless ( &hasFlag('o') );
+        return unless ( &hasFlag('A') );
+
+        my $chan = $2;
+
+        if ( $chan eq '' ) {
+            &help('4op');
+            return;
+        }
+
+        # can this be exploited?
+        rawout("MODE $chan +o $who");
+
+        return;
+    }
+
+    # backlog.
+    if ( $message =~ /^backlog(\s+(.*))?$/i ) {
+        return unless ( &hasFlag('o') );
+        return unless ( &IsParam('backlog') );
+        my $num = $2;
+        my $max = $param{'backlog'};
+
+        if ( !defined $num ) {
+            &help('backlog');
+            return;
+        }
+        elsif ( $num !~ /^\d+/ ) {
+            &msg( $who, "error: argument is not positive integer." );
+            return;
+        }
+        elsif ( $num > $max or $num < 0 ) {
+            &msg( $who, "error: argument is out of range (max $max)." );
+            return;
+        }
+
+        &msg( $who, "Start of backlog..." );
+        for ( 0 .. $num - 1 ) {
+            sleep 1 if ( $_ % 4 == 0 and $_ != 0 );
+            $conn->privmsg( $who,
+                "[" . ( $_ + 1 ) . "]: $backlog[$max-$num+$_]" );
+        }
+        &msg( $who, "End of backlog." );
+
+        return;
+    }
+
+    # dump variables.
+    if ( $message =~ /^dumpvars$/i ) {
+        return unless ( &hasFlag('o') );
+        return unless ( &IsParam('DumpVars') );
+
+        &status("Dumping all variables...");
+        &dumpallvars();
+
+        return;
+    }
+
+    # dump variables ][.
+    if ( $message =~ /^symdump$/i ) {
+        return unless ( &hasFlag('o') );
+        return unless ( &IsParam('DumpVars2') );
+
+        &status("Dumping all variables...");
+        &symdumpAllFile();
+
+        return;
+    }
+
+    # kick.
+    if ( $message =~ /^kick(\s+(.*?))$/ ) {
+        return unless ( &hasFlag('o') );
+
+        my $arg = $2;
+
+        if ( $arg eq '' ) {
+            &help('kick');
+            return;
+        }
+        my @args = split( /\s+/, $arg );
+        my ( $nick, $chan, $reason ) = @args;
+
+        if ( &validChan($chan) == 0 ) {
+            &msg( $who, "error: invalid channel \002$chan\002" );
+            return;
+        }
+
+        if ( &IsNickInChan( $nick, $chan ) == 0 ) {
+            &msg( $who, "$nick is not in $chan." );
+            return;
+        }
+
+        &kick( $nick, $chan, $reason );
+
+        return;
+    }
+
+    # mode.
+    if ( $message =~ /^mode(\s+(.*))?$/ ) {
+        return unless ( &hasFlag('n') );
+        my ( $chan, $mode ) = split /\s+/, $2, 2;
+
+        if ( $chan eq '' ) {
+            &help('mode');
+            return;
+        }
+
+        if ( &validChan($chan) == 0 ) {
+            &msg( $who, "error: invalid channel \002$chan\002" );
+            return;
+        }
+
+        if ( !$channels{$chan}{o}{$ident} ) {
+            &msg( $who, "error: don't have ops on \002$chan\002" );
+            return;
+        }
+
+        &mode( $chan, $mode );
+
+        return;
+    }
+
+    # part.
+    if ( $message =~ /^part(\s+(\S+))?$/i ) {
+        return unless ( &hasFlag('o') );
+        my $jchan = $2;
+
+        if ( $jchan !~ /^$mask{chan}$/ ) {
+            &msg( $who, "error, invalid chan." );
+            &help('part');
+            return;
+        }
+
+        if ( !&validChan($jchan) ) {
+            &msg( $who, "error, I'm not on that chan." );
+            return;
+        }
+
+        &msg( $jchan, "Leaving. (courtesy of $who)." );
+        &part($jchan);
+        return;
+    }
+
+    # lobotomy. sometimes we want the bot to be _QUIET_.
+    if ( $message =~ /^(lobotomy|bequiet)$/i ) {
+        return unless ( &hasFlag('o') );
+
+        if ($lobotomized) {
+            &performReply("i'm already lobotomized");
+        }
+        else {
+            &performReply('i have been lobotomized');
+            $lobotomized = 1;
+        }
+
+        return;
+    }
+
+    # unlobotomy.
+    if ( $message =~ /^(unlobotomy|benoisy)$/i ) {
+        return unless ( &hasFlag('o') );
+
+        if ($lobotomized) {
+            &performReply('i have been unlobotomized, woohoo');
+            $lobotomized = 0;
+            delete $cache{lobotomy};
+
+            #      undef $cache{lobotomy};     # ??
+        }
+        else {
+            &performReply("i'm not lobotomized");
+        }
+
+        return;
+    }
+
+    # op.
+    if ( $message =~ /^op(\s+(.*))?$/i ) {
+        return unless ( &hasFlag('o') );
+        my ($opee) = lc $2;
+        my @chans;
+
+        if ( $opee =~ / / ) {
+            if ( $opee =~ /^(\S+)\s+(\S+)$/ ) {
+                $opee  = $1;
+                @chans = ($2);
+                if ( !&validChan($2) ) {
+                    &msg( $who, "error: invalid chan ($2)." );
+                    return;
+                }
+            }
+            else {
+                &msg( $who, "error: invalid params." );
+                return;
+            }
+        }
+        else {
+            @chans = keys %channels;
+        }
+
+        my $found = 0;
+        my $op    = 0;
+        foreach (@chans) {
+            next unless ( &IsNickInChan( $opee, $_ ) );
+            $found++;
+            if ( $channels{$_}{'o'}{$opee} ) {
+                &performStrictReply("op: $opee already has ops on $_");
+                next;
+            }
+            $op++;
+
+            &performStrictReply("opping $opee on $_");
+            &op( $_, $opee );
+        }
+
+        if ( $found != $op ) {
+            &performStrictReply("op: opped on all possible channels.");
+        }
+        else {
+            &DEBUG("op: found => '$found'.");
+            &DEBUG("op:    op => '$op'.");
+        }
+
+        return;
+    }
+
+    # deop.
+    if ( $message =~ /^deop(\s+(.*))?$/i ) {
+        return unless ( &hasFlag('o') );
+        my ($opee) = lc $2;
+        my @chans;
+
+        if ( $opee =~ / / ) {
+            if ( $opee =~ /^(\S+)\s+(\S+)$/ ) {
+                $opee  = $1;
+                @chans = ($2);
+                if ( !&validChan($2) ) {
+                    &msg( $who, "error: invalid chan ($2)." );
+                    return;
+                }
+            }
+            else {
+                &msg( $who, "error: invalid params." );
+                return;
+            }
+        }
+        else {
+            @chans = keys %channels;
+        }
+
+        my $found = 0;
+        my $op    = 0;
+        foreach (@chans) {
+            next unless ( &IsNickInChan( $opee, $_ ) );
+            $found++;
+            if ( !exists $channels{$_}{'o'}{$opee} ) {
+                &status("deop: $opee already has no ops on $_");
+                next;
+            }
+            $op++;
+
+            &status("deopping $opee on $_ at ${who}'s request");
+            &deop( $_, $opee );
+        }
+
+        if ( $found != $op ) {
+            &status("deop: deopped on all possible channels.");
+        }
+        else {
+            &DEBUG("deop: found => '$found'.");
+            &DEBUG("deop: op => '$op'.");
+        }
+
+        return;
+    }
+
+    # say.
+    if ( $message =~ s/^say\s+(\S+)\s+(.*)// ) {
+        return unless ( &hasFlag('o') );
+        my ( $chan, $msg ) = ( lc $1, $2 );
+
+        &DEBUG("chan => '$1', msg => '$msg'.");
+
+        &msg( $chan, $msg );
+
+        return;
+    }
+
+    # do.
+    if ( $message =~ s/^do\s+(\S+)\s+(.*)// ) {
+        return unless ( &hasFlag('o') );
+        my ( $chan, $msg ) = ( lc $1, $2 );
+
+        &DEBUG("chan => '$1', msg => '$msg'.");
+
+        &action( $chan, $msg );
+
+        return;
+    }
+
+    # die.
+    if ( $message =~ /^die$/ ) {
+        return unless ( &hasFlag('n') );
+
+        &doExit();
+
+        &status("Dying by $who\'s request");
+        exit 0;
+    }
+
+    # global factoid substitution.
+    if ( $message =~ m|^\* =~ s([/,#])(.+?)\1(.*?)\1;?\s*$| ) {
+        my ( $delim, $op, $np ) = ( $1, $2, $3 );
+        return unless ( &hasFlag('n') );
+        ### TODO: support flags to do full-on global.
+
+        # incorrect format.
+        if ( $np =~ /$delim/ ) {
+            &performReply(
+"looks like you used the delimiter too many times. You may want to use a different delimiter, like ':' or '#'."
+            );
+            return;
+        }
+
+        ### TODO: fix up $op to support mysql/sqlite/pgsql
+        ### TODO: => add db/sql specific function to fix this.
+        my @list =
+          &searchTable( 'factoids', 'factoid_key', 'factoid_value', $op );
+
+        if ( !scalar @list ) {
+            &performReply("Expression didn't match anything.");
+            return;
+        }
+
+        if ( scalar @list > 100 ) {
+            &performReply("regex found more than 100 matches... not doing.");
+            return;
+        }
+
+        &status( "gsubst: going to alter " . scalar(@list) . " factoids." );
+        &performReply( 'going to alter ' . scalar(@list) . " factoids." );
+
+        my $error = 0;
+        foreach (@list) {
+            my $faqtoid = $_;
+
+            next if ( &IsLocked($faqtoid) == 1 );
+            my $result = &getFactoid($faqtoid);
+            my $was    = $result;
+            &DEBUG("was($faqtoid) => '$was'.");
+
+            # global global
+            # we could support global local (once off).
+            if ( $result =~ s/\Q$op/$np/gi ) {
+                if ( length $result > $param{'maxDataSize'} ) {
+                    &performReply("that's too long (or was long)");
+                    return;
+                }
+                &setFactInfo( $faqtoid, 'factoid_value', $result );
+                &status("update: '$faqtoid' =is=> '$result'; was '$was'");
+            }
+            else {
+                &WARN(
+"subst: that's weird... thought we found the string ($op) in '$faqtoid'."
+                );
+                $error++;
+            }
+        }
+
+        if ($error) {
+            &ERROR("Some warnings/errors?");
+        }
+
+        &performReply( "Ok... did s/$op/$np/ for "
+              . ( scalar(@list) - $error )
+              . ' factoids' );
+
+        return;
+    }
+
+    # jump.
+    if ( $message =~ /^jump(\s+(\S+))?$/i ) {
+        return unless ( &hasFlag('n') );
+
+        if ( $2 eq '' ) {
+            &help('jump');
+            return;
+        }
+
+        my ( $server, $port );
+        if ( $2 =~ /^(\S+)(:(\d+))?$/ ) {
+            $server = $1;
+            $port = $3 || 6667;
+        }
+        else {
+            &msg( $who, "invalid format." );
+            return;
+        }
+
+        &status("jumping servers... $server...");
+        $conn->quit("jumping to $server");
+
+        if ( &irc( $server, $port ) == 0 ) {
+            &ircloop();
+        }
+    }
+
+    # reload.
+    if ( $message =~ /^reload$/i ) {
+        return unless ( &hasFlag('n') );
+
+        &status("USER reload $who");
+        &performStrictReply("reloading...");
+        &reloadAllModules();
+        &performStrictReply("reloaded.");
+
+        return;
+    }
+
+    # reset.
+    if ( $message =~ /^reset$/i ) {
+        return unless ( &hasFlag('n') );
+
+        &msg( $who, "resetting..." );
+        my @done;
+        foreach ( keys %channels, keys %chanconf ) {
+            my $c = $_;
+            next if ( grep /^\Q$c\E$/i, @done );
+
+            &part($_);
+
+            push( @done, $_ );
+            sleep 1;
+        }
+        &DEBUG('before clearircvars');
+        &clearIRCVars();
+        &DEBUG('before joinnextchan');
+        &joinNextChan();
+        &DEBUG('after joinnextchan');
+
+        &status("USER reset $who");
+        &msg( $who, 'reset complete' );
+
+        return;
+    }
+
+    # rehash.
+    if ( $message =~ /^rehash$/ ) {
+        return unless ( &hasFlag('n') );
+
+        &msg( $who, "rehashing..." );
+        &restart('REHASH');
+        &status("USER rehash $who");
+        &msg( $who, 'rehashed' );
+
+        return;
+    }
+
+    #####
+    ##### USER//CHAN SPECIFIC CONFIGURATION COMMANDS
+    #####
+
+    if ( $message =~ /^chaninfo(\s+(.*))?$/ ) {
+        my @args = split /[\s\t]+/, $2;    # hrm.
+
+        if ( scalar @args != 1 ) {
+            &help('chaninfo');
+            return;
+        }
+
+        if ( !exists $chanconf{ $args[0] } ) {
+            &performStrictReply("no such channel $args[0]");
+            return;
+        }
+
+        &performStrictReply("showing channel conf.");
+        foreach ( sort keys %{ $chanconf{ $args[0] } } ) {
+            &performStrictReply("$chan: $_ => $chanconf{$args[0]}{$_}");
+        }
+        &performStrictReply("End of chaninfo.");
+
+        return;
+    }
+
+    # chanadd.
+    if ( $message =~ /^(chanset|chanadd)(\s+(.*?))?$/ ) {
+        my $cmd     = $1;
+        my $args    = $3;
+        my $no_chan = 0;
+
+        if ( !defined $args ) {
+            &help($cmd);
+            return;
+        }
+
+        my @chans;
+        while ( $args =~ s/^($mask{chan})\s*// ) {
+            push( @chans, lc($1) );
+        }
+
+        if ( !scalar @chans ) {
+            push( @chans, '_default' );
+            $no_chan = 1;
+        }
+
+        my ( $what, $val ) = split /[\s\t]+/, $args, 2;
+
+        ### TODO: "cannot set values without +m".
+        return unless ( &hasFlag('n') );
+
+        # READ ONLY.
+        if ( defined $what and $what !~ /^[-+]/ and !defined $val and $no_chan )
+        {
+            &performStrictReply("Showing $what values on all channels...");
+
+            my %vals;
+            foreach ( keys %chanconf ) {
+                my $val;
+                if ( defined $chanconf{$_}{$what} ) {
+                    $val = $chanconf{$_}{$what};
+                }
+                else {
+                    $val = "NOT-SET";
+                }
+                $vals{$val}{$_} = 1;
+            }
+
+            foreach ( keys %vals ) {
+                &performStrictReply( "  $what = $_("
+                      . scalar( keys %{ $vals{$_} } ) . "): "
+                      . join( ' ', sort keys %{ $vals{$_} } ) );
+            }
+
+            &performStrictReply("End of list.");
+
+            return;
+        }
+
+        ### TODO: move to UserDCC again.
+        if ( $cmd eq 'chanset' and !defined $what ) {
+            &DEBUG("showing channel conf.");
+
+            foreach $chan (@chans) {
+                if ( $chan eq '_default' ) {
+                    &performStrictReply('Default channel settings');
+                }
+                else {
+                    &performStrictReply("chan: $chan (see _default also)");
+                }
+                my @items;
+                my $str = '';
+                foreach ( sort keys %{ $chanconf{$chan} } ) {
+                    my $newstr = join( ', ', @items );
+                    ### TODO: make length use channel line limit?
+                    if ( length $newstr > 370 ) {
+                        &performStrictReply(" $str");
+                        @items = ();
+                    }
+                    $str = $newstr;
+                    push( @items, "$_ => $chanconf{$chan}{$_}" );
+                }
+                if (@items) {
+                    my $str = join( ', ', @items );
+                    &performStrictReply(" $str");
+                }
+            }
+            return;
+        }
+
+        $cache{confvars}{$what} = $val;
+        &rehashConfVars();
+
+        foreach (@chans) {
+            &chanSet( $cmd, $_, $what, $val );
+        }
+
+        return;
+    }
+
+    if ( $message =~ /^(chanunset|chandel)(\s+(.*))?$/ ) {
+        return unless ( &hasFlag('n') );
+        my $cmd     = $1;
+        my $args    = $3;
+        my $no_chan = 0;
+
+        if ( !defined $args ) {
+            &help($cmd);
+            return;
+        }
+
+        my ($chan);
+        my $delete = 0;
+        if ( $args =~ s/^(\-)?($mask{chan})\s*// ) {
+            $chan = $2;
+            $delete = ($1) ? 1 : 0;
+        }
+        else {
+            &VERB( "no chan arg; setting to default.", 2 );
+            $chan    = '_default';
+            $no_chan = 1;
+        }
+
+        if ( !exists $chanconf{$chan} ) {
+            &performStrictReply("no such channel $chan");
+            return;
+        }
+
+        if ( $args ne '' ) {
+
+            if ( !&getChanConf( $args, $chan ) ) {
+                &performStrictReply("$args does not exist for $chan");
+                return;
+            }
+
+            my @chans = &ChanConfList($args);
+            &DEBUG( "scalar chans => " . scalar(@chans) );
+            if ( scalar @chans == 1 and $chans[0] eq '_default' and !$no_chan )
+            {
+                &performStrictReply(
+"ok, $args was set only for _default; unsetting for _defaul but setting for other chans."
+                );
+
+                my $val = $chanconf{$_}{_default};
+                foreach ( keys %chanconf ) {
+                    $chanconf{$_}{$args} = $val;
+                }
+                delete $chanconf{_default}{$args};
+                $cache{confvars}{$args} = 0;
+                &rehashConfVars();
+
+                return;
+            }
+
+            if ( $no_chan and !exists( $chanconf{_default}{$args} ) ) {
+                &performStrictReply(
+"ok, $args for _default does not exist, removing from all chans."
+                );
+
+                foreach ( keys %chanconf ) {
+                    next unless ( exists $chanconf{$_}{$args} );
+                    &DEBUG("delete chanconf{$_}{$args};");
+                    delete $chanconf{$_}{$args};
+                }
+                $cache{confvars}{$args} = 0;
+                &rehashConfVars();
+
+                return;
+            }
+
+            &performStrictReply(
+"Unsetting channel ($chan) option $args. (was $chanconf{$chan}{$args})"
+            );
+            delete $chanconf{$chan}{$args};
+
+            return;
+        }
+
+        if ($delete) {
+            &performStrictReply("Deleting channel $chan for sure!");
+            $utime_chanfile = time();
+            $ucount_chanfile++;
+
+            &part($chan);
+            &performStrictReply("Leaving $chan...");
+
+            delete $chanconf{$chan};
+        }
+        else {
+            &performStrictReply("Prefix channel with '-' to delete for sure.");
+        }
+
+        return;
+    }
+
+    if ( $message =~ /^newpass(\s+(.*))?$/ ) {
+        my (@args) = split /[\s\t]+/, $2 || '';
+
+        if ( scalar @args != 1 ) {
+            &help('newpass');
+            return;
+        }
+
+        my $u     = &getUser($who);
+        my $crypt = &mkcrypt( $args[0] );
+
+        &performStrictReply("Set your passwd to '$crypt'");
+        $users{$u}{PASS} = $crypt;
+
+        $utime_userfile = time();
+        $ucount_userfile++;
+
+        return;
+    }
+
+    if ( $message =~ /^chpass(\s+(.*))?$/ ) {
+        my (@args) = split /[\s\t]+/, $2 || '';
+
+        if ( !scalar @args ) {
+            &help('chpass');
+            return;
+        }
+
+        if ( !&IsUser( $args[0] ) ) {
+            &performStrictReply("user $args[0] is not valid.");
+            return;
+        }
+
+        my $u = &getUser( $args[0] );
+        if ( !defined $u ) {
+            &performStrictReply("Internal error, u = NULL.");
+            return;
+        }
+
+        if ( scalar @args == 1 ) {
+
+            # del pass.
+            if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
+                &performStrictReply("cannot remove passwd of others.");
+                return;
+            }
+
+            if ( !exists $users{$u}{PASS} ) {
+                &performStrictReply("$u does not have pass set anyway.");
+                return;
+            }
+
+            &performStrictReply("Deleted pass from $u.");
+
+            $utime_userfile = time();
+            $ucount_userfile++;
+
+            delete $users{$u}{PASS};
+
+            return;
+        }
+
+        my $crypt = &mkcrypt( $args[1] );
+        &performStrictReply("Set $u's passwd to '$crypt'");
+        $users{$u}{PASS} = $crypt;
+
+        $utime_userfile = time();
+        $ucount_userfile++;
+
+        return;
+    }
+
+    if ( $message =~ /^chattr(\s+(.*))?$/ ) {
+        my (@args) = split /[\s\t]+/, $2 || '';
+
+        if ( !scalar @args ) {
+            &help('chattr');
+            return;
+        }
+
+        my $chflag;
+        my $user;
+        if ( $args[0] =~ /^$mask{nick}$/i ) {
+
+            # <nick>
+            $user   = &getUser( $args[0] );
+            $chflag = $args[1];
+        }
+        else {
+
+            # <flags>
+            $user = &getUser($who);
+            &DEBUG("user $who... nope.") unless ( defined $user );
+            $user   = &getUser($verifyUser);
+            $chflag = $args[0];
+        }
+
+        if ( !defined $user ) {
+            &performStrictReply("user does not exist.");
+            return;
+        }
+
+        my $flags = $users{$user}{FLAGS};
+        if ( !defined $chflag ) {
+            &performStrictReply("Flags for $user: $flags");
+            return;
+        }
+
+        &DEBUG("who => $who");
+        &DEBUG("verifyUser => $verifyUser");
+        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
+            &performStrictReply("cannto change attributes of others.");
+            return 'REPLY';
+        }
+
+        my $state;
+        my $change = 0;
+        foreach ( split //, $chflag ) {
+            if ( $_ eq "+" ) { $state = 1; next; }
+            if ( $_ eq "-" ) { $state = 0; next; }
+
+            if ( !defined $state ) {
+                &performStrictReply("no initial + or - was found in attr.");
+                return;
+            }
+
+            if ($state) {
+                next if ( $flags =~ /\Q$_\E/ );
+                $flags .= $_;
+            }
+            else {
+                if (    &IsParam('owner')
+                    and $param{owner} =~ /^\Q$user\E$/i
+                    and $flags        =~ /[nmo]/ )
+                {
+                    &performStrictReply("not removing flag $_ for $user.");
+                    next;
+                }
+                next unless ( $flags =~ s/\Q$_\E// );
+            }
+
+            $change++;
+        }
+
+        if ($change) {
+            $utime_userfile = time();
+            $ucount_userfile++;
+
+            #$flags.*FLAGS sort
+            $flags = join( '', sort split( '', $flags ) );
+            &performStrictReply("Current flags: $flags");
+            $users{$user}{FLAGS} = $flags;
+        }
+        else {
+            &performStrictReply("No flags changed: $flags");
+        }
+
+        return;
+    }
+
+    if ( $message =~ /^chnick(\s+(.*))?$/ ) {
+        my (@args) = split /[\s\t]+/, $2 || '';
+
+        if ( $who eq '_default' ) {
+            &WARN("$who or verifyuser tried to run chnick.");
+            return 'REPLY';
+        }
+
+        if ( !scalar @args or scalar @args > 2 ) {
+            &help('chnick');
+            return;
+        }
+
+        if ( scalar @args == 1 ) {    # 1
+            $user = &getUser($who);
+            &DEBUG("nope, not $who.") unless ( defined $user );
+            $user ||= &getUser($verifyUser);
+            $chnick = $args[0];
+        }
+        else {                        # 2
+            $user   = &getUser( $args[0] );
+            $chnick = $args[1];
+        }
+
+        if ( !defined $user ) {
+            &performStrictReply("user $who or $args[0] does not exist.");
+            return;
+        }
+
+        if ( $user =~ /^\Q$chnick\E$/i ) {
+            &performStrictReply("user == chnick. why should I do that?");
+            return;
+        }
+
+        if ( &getUser($chnick) ) {
+            &performStrictReply("user $chnick is already used!");
+            return;
+        }
+
+        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
+            &performStrictReply("cannto change nick of others.");
+            return 'REPLY' if ( $who eq '_default' );
+            return;
+        }
+
+        foreach ( keys %{ $users{$user} } ) {
+            $users{$chnick}{$_} = $users{$user}{$_};
+            delete $users{$user}{$_};
+        }
+        undef $users{$user};    # ???
+
+        $utime_userfile = time();
+        $ucount_userfile++;
+
+        &performStrictReply("Changed '$user' to '$chnick' successfully.");
+
+        return;
+    }
+
+    if ( $message =~ /^(hostadd|hostdel)(\s+(.*))?$/ ) {
+        my $cmd = $1;
+        my (@args) = split /[\s\t]+/, $3 || '';
+        my $state = ( $1 eq "hostadd" ) ? 1 : 0;
+
+        if ( !scalar @args ) {
+            &help($cmd);
+            return;
+        }
+
+        if ( $who eq '_default' ) {
+            &WARN("$who or verifyuser tried to run $cmd.");
+            return 'REPLY';
+        }
+
+        my ( $user, $mask );
+        if ( $args[0] =~ /^$mask{nick}$/i ) {    # <nick>
+            return unless ( &hasFlag('n') );
+            $user = &getUser( $args[0] );
+            $mask = $args[1];
+        }
+        else {                                   # <mask>
+                # FIXME: who or verifyUser. (don't remember why)
+            $user = &getUser($who);
+            $mask = $args[0];
+        }
+
+        if ( !defined $user ) {
+            &performStrictReply("user $user does not exist.");
+            return;
+        }
+
+        if ( !defined $mask ) {
+            &performStrictReply( "Hostmasks for $user: "
+                  . join( ' ', keys %{ $users{$user}{HOSTS} } ) );
+            return;
+        }
+
+        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
+            &performStrictReply("cannto change masks of others.");
+            return;
+        }
+
+        my $count = scalar keys %{ $users{$user}{HOSTS} };
+
+        if ($state) {    # add.
+            if ( $mask !~ /^$mask{nuh}$/ ) {
+                &performStrictReply(
+                    "error: mask ($mask) is not a real hostmask.");
+                return;
+            }
+
+            if ( exists $users{$user}{HOSTS}{$mask} ) {
+                &performStrictReply("mask $mask already exists.");
+                return;
+            }
+
+            ### TODO: override support.
+            $users{$user}{HOSTS}{$mask} = 1;
+
+            &performStrictReply("Added $mask to list of masks.");
+
+        }
+        else {    # delete.
+
+            if ( !exists $users{$user}{HOSTS}{$mask} ) {
+                &performStrictReply("mask $mask does not exist.");
+                return;
+            }
+
+            ### TODO: wildcard support. ?
+            delete $users{$user}{HOSTS}{$mask};
+
+            if ( scalar keys %{ $users{$user}{HOSTS} } != $count ) {
+                &performStrictReply("Removed $mask from list of masks.");
+            }
+            else {
+                &performStrictReply(
+                    "error: could not find $mask in list of masks.");
+                return;
+            }
+        }
+
+        $utime_userfile = time();
+        $ucount_userfile++;
+
+        return;
+    }
+
+    if ( $message =~ /^(banadd|bandel)(\s+(.*))?$/ ) {
+        my $cmd     = $1;
+        my $flatarg = $3;
+        my (@args) = split /[\s\t]+/, $3 || '';
+        my $state = ( $1 eq "banadd" ) ? 1 : 0;
+
+        if ( !scalar @args ) {
+            &help($cmd);
+            return;
+        }
+
+        my ( $mask, $chan, $time, $reason );
+
+        if ( $flatarg =~ s/^($mask{nuh})\s*// ) {
+            $mask = $1;
+        }
+        else {
+            &DEBUG("arg does not contain nuh mask?");
+        }
+
+        if ( $flatarg =~ s/^($mask{chan})\s*// ) {
+            $chan = $1;
+        }
+        else {
+            $chan = '*';    # _default instead?
+        }
+
+        if ( $state == 0 ) {    # delete.
+            my @c = &banDel($mask);
+
+            foreach (@c) {
+                &unban( $mask, $_ );
+            }
+
+            if (@c) {
+                &performStrictReply("Removed $mask from chans: @c");
+            }
+            else {
+                &performStrictReply("$mask was not found in ban list.");
+            }
+
+            return;
+        }
+
+        ###
+        # add ban.
+        ###
+
+        # time.
+        if ( $flatarg =~ s/^(\d+)\s*// ) {
+            $time = $1;
+            &DEBUG("time = $time.");
+            if ( $time < 0 ) {
+                &performStrictReply("error: time cannot be negatime?");
+                return;
+            }
+        }
+        else {
+            $time = 0;
+        }
+
+        if ( $flatarg =~ s/^(.*)$// ) {    # need length?
+            $reason = $1;
+        }
+
+        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
+            &performStrictReply("cannto change masks of others.");
+            return;
+        }
+
+        if ( $mask !~ /^$mask{nuh}$/ ) {
+            &performStrictReply("error: mask ($mask) is not a real hostmask.");
+            return;
+        }
+
+        if ( &banAdd( $mask, $chan, $time, $reason ) == 2 ) {
+            &performStrictReply("ban already exists; overwriting.");
+        }
+        &performStrictReply(
+            "Added $mask for $chan (time => $time, reason => $reason)");
+
+        return;
+    }
+
+    if ( $message =~ /^whois(\s+(.*))?$/ ) {
+        my $arg = $2;
+
+        if ( !defined $arg ) {
+            &help('whois');
+            return;
+        }
+
+        my $user = &getUser($arg);
+        if ( !defined $user ) {
+            &performStrictReply("whois: user '$arg' does not exist.");
+            return;
+        }
+
+        ### TODO: better (eggdrop-like) output.
+        &performStrictReply("user: $user");
+        foreach ( keys %{ $users{$user} } ) {
+            my $ref = ref $users{$user}{$_};
+
+            if ( $ref eq 'HASH' ) {
+                my $type = $_;
+                ### DOES NOT WORK???
+                foreach ( keys %{ $users{$user}{$type} } ) {
+                    &performStrictReply("    $type => $_");
+                }
+                next;
+            }
+
+            &performStrictReply("    $_ => $users{$user}{$_}");
+        }
+        &performStrictReply("End of USER whois.");
+
+        return;
+    }
+
+    if ( $message =~ /^bans(\s+(.*))?$/ ) {
+        my $arg = $2;
+
+        if ( defined $arg ) {
+            if ( $arg ne '_default' and !&validChan($arg) ) {
+                &performStrictReply("error: chan $chan is invalid.");
+                return;
+            }
+        }
+
+        if ( !scalar keys %bans ) {
+            &performStrictReply("Ban list is empty.");
+            return;
+        }
+
+        my $c;
+        &performStrictReply(
+            "     mask: expire, time-added, count, who-by, reason");
+        foreach $c ( keys %bans ) {
+            next unless ( !defined $arg or $arg =~ /^\Q$c\E$/i );
+            &performStrictReply("  $c:");
+
+            foreach ( keys %{ $bans{$c} } ) {
+                my $val = $bans{$c}{$_};
+
+                if ( ref $val eq 'ARRAY' ) {
+                    my @array = @{$val};
+                    &performStrictReply("    $_: @array");
+                }
+                else {
+                    &DEBUG("unknown ban: $val");
+                }
+            }
+        }
+        &performStrictReply("END of bans.");
+
+        return;
+    }
+
+    if ( $message =~ /^banlist(\s+(.*))?$/ ) {
+        my $arg = $2;
+
+        if ( defined $arg and $arg !~ /^$mask{chan}$/ ) {
+            &performStrictReply("error: chan $chan is invalid.");
+            return;
+        }
+
+        &DEBUG("bans for global or arg => $arg.");
+        foreach ( keys %bans ) {    #CHANGE!!!
+            &DEBUG("  $_ => $bans{$_}.");
+        }
+
+        &DEBUG("End of bans.");
+        &performStrictReply("END of bans.");
+
+        return;
+    }
+
+    if ( $message =~ /^save$/ ) {
+        return unless ( &hasFlag('o') );
+
+        &writeUserFile();
+        &writeChanFile();
+        &performStrictReply('saved user and chan files');
+
+        return;
+    }
+
+    ### ALIASES.
+    $message =~ s/^addignore/+ignore/;
+    $message =~ s/^(del|un)ignore/-ignore/;
+
+    # ignore.
+    if ( $message =~ /^(\+|\-)ignore(\s+(.*))?$/i ) {
+        return unless ( &hasFlag('o') );
+        my $state = ( $1 eq "+" ) ? 1 : 0;
+        my $str   = $1 . 'ignore';
+        my $args  = $3;
+
+        if ( !$args ) {
+            &help($str);
+            return;
+        }
+
+        my ( $mask, $chan, $time, $comment );
+
+        # mask.
+        if ( $args =~ s/^($mask{nuh})\s*// ) {
+            $mask = $1;
+        }
+        else {
+            &ERROR("no NUH mask?");
+            return;
+        }
+
+        if ( !$state ) {    # delignore.
+            if ( &ignoreDel($mask) ) {
+                &performStrictReply("ok, deleted ignores for $mask.");
+            }
+            else {
+                &performStrictReply("could not find $mask in ignore list.");
+            }
+            return;
+        }
+
+        ###
+        # addignore.
+        ###
+
+        # chan.
+        if ( $args =~ s/^($mask{chan}|\*)\s*// ) {
+            $chan = $1;
+        }
+        else {
+            $chan = '*';
+        }
+
+        # time.
+        if ( $args =~ s/^(\d+)\s*// ) {
+            $time = $1;    # time is in minutes
+        }
+        else {
+            $time = 0;
+        }
+
+        # time.
+        if ($args) {
+            $comment = $args;
+        }
+        else {
+            $comment = "added by $who";
+        }
+
+        if ( &ignoreAdd( $mask, $chan, $time, $comment ) > 1 ) {
+            &performStrictReply(
+                "FIXME: $mask already in ignore list; written over anyway.");
+        }
+        else {
+            &performStrictReply("added $mask to ignore list.");
+        }
+
+        return;
+    }
+
+    if ( $message =~ /^ignore(\s+(.*))?$/ ) {
+        my $arg = $2;
+
+        if ( defined $arg ) {
+            if ( $arg !~ /^$mask{chan}$/ ) {
+                &performStrictReply("error: chan $chan is invalid.");
+                return;
+            }
+
+            if ( !&validChan($arg) ) {
+                &performStrictReply("error: chan $arg is invalid.");
+                return;
+            }
+
+            &performStrictReply("Showing bans for $arg only.");
+        }
+
+        if ( !scalar keys %ignore ) {
+            &performStrictReply("Ignore list is empty.");
+            return;
+        }
+
+        ### TODO: proper (eggdrop-like) formatting.
+        my $c;
+        &performStrictReply("    mask: expire, time-added, who, comment");
+        foreach $c ( keys %ignore ) {
+            next unless ( !defined $arg or $arg =~ /^\Q$c\E$/i );
+            &performStrictReply("  $c:");
+
+            foreach ( keys %{ $ignore{$c} } ) {
+                my $ref = ref $ignore{$c}{$_};
+                if ( $ref eq 'ARRAY' ) {
+                    my @array = @{ $ignore{$c}{$_} };
+                    &performStrictReply("      $_: @array");
+                }
+                else {
+                    &DEBUG("unknown ignore line?");
+                }
+            }
+        }
+        &performStrictReply("END of ignore.");
+
+        return;
+    }
+
+    # useradd/userdel.
+    if ( $message =~ /^(useradd|userdel)(\s+(.*))?$/i ) {
+        my $cmd    = $1;
+        my @args   = split /\s+/, $3 || '';
+        my $args   = $3;
+        my $state  = ( $cmd eq "useradd" ) ? 1 : 0;
+
+        if ( !scalar @args ) {
+            &help($cmd);
+            return;
+        }
+
+        if ( $cmd eq 'useradd' ) {
+            if ( scalar @args != 2 ) {
+                &performStrictReply('useradd requires hostmask argument.');
+                return;
+            }
+        }
+        elsif ( scalar @args != 1 ) {
+            &performStrictReply('too many arguments.');
+            return;
+        }
+
+        if ($state) {
+
+            # adduser.
+            if ( scalar @args == 1 ) {
+                $args[1] = &getHostMask( $args[0] );
+                &performStrictReply(
+                    "Attemping to guess $args[0]'s hostmask...");
+
+                # crude hack... crappy Net::IRC
+                $conn->schedule(
+                    5,
+                    sub {
+
+                        # hopefully this is right.
+                        my $nick = ( keys %{ $cache{nuhInfo} } )[0];
+                        if ( !defined $nick ) {
+                            &performStrictReply(
+"couldn't get nuhinfo... adding user without a hostmask."
+                            );
+                            &userAdd($nick);
+                            return;
+                        }
+                        my $mask = &makeHostMask( $cache{nuhInfo}{$nick}{NUH} );
+
+                        if ( &userAdd( $nick, $mask ) ) {
+
+                            # success.
+                            &performStrictReply(
+                                "Added $nick with flags $users{$nick}{FLAGS}");
+                            my @hosts = keys %{ $users{$nick}{HOSTS} };
+                            &performStrictReply("hosts: @hosts");
+                        }
+                    }
+                );
+                return;
+            }
+
+            &DEBUG("args => @args");
+            if ( &userAdd(@args) ) {    # success.
+                &performStrictReply(
+                    "Added $args[0] with flags $users{$args[0]}{FLAGS}");
+                my @hosts = keys %{ $users{ $args[0] }{HOSTS} };
+                &performStrictReply("hosts: @hosts");
+
+            }
+            else {                      # failure.
+                &performStrictReply("User $args[0] already exists");
+            }
+
+        }
+        else {                          # deluser.
+
+            if ( &userDel( $args[0] ) ) {    # success.
+                &performStrictReply("Deleted $args[0] successfully.");
+
+            }
+            else {                           # failure.
+                &performStrictReply("User $args[0] does not exist.");
+            }
+
+        }
+        return;
+    }
+
+    if ( $message =~ /^sched$/ ) {
+        my @list;
+        my @run;
+
+        my %time;
+        foreach ( keys %sched ) {
+            next unless ( exists $sched{$_}{TIME} );
+            $time{ $sched{$_}{TIME} - time() }{$_} = 1;
+            push( @list, $_ );
+
+            next unless ( exists $sched{$_}{RUNNING} );
+            push( @run, $_ );
+        }
+
+        my @time;
+        foreach ( sort { $a <=> $b } keys %time ) {
+            my $str = join( ', ', sort keys %{ $time{$_} } );
+            &DEBUG("time => $_, str => $str");
+            push( @time, "$str (" . &Time2String($_) . ")" );
+        }
+
+        &performStrictReply( &formListReply( 0, "Schedulers: ", @time ) );
+        &performStrictReply(
+            &formListReply( 0, "Scheds to run: ", sort @list ) );
+        &performStrictReply(
+            &formListReply(
+                0, "Scheds running(should not happen?) ",
+                sort @run
+            )
+        );
+
+        return;
+    }
+
+    # quite a cool hack: reply in DCC CHAT.
+    $msgType = 'chat' if ( exists $dcc{'CHAT'}{$who} );
+
+    my $done = 0;
+    $done++ if &parseCmdHook($message);
+    $done++ unless ( &Modules() );
+
+    if ($done) {
+        &DEBUG("running non DCC CHAT command inside DCC CHAT!");
+        return;
+    }
+
+    return 'REPLY';
+}
+
+1;
+
+# vim:ts=4:sw=4:expandtab:tw=80
index e999a2fdcd1978b57c940b09bacd83c28b977dcf..9545f6c6f4c54c5fd1e3dbb384d248e30ae3b852 100644 (file)
@@ -688,8 +688,8 @@ sub closeStats {
             'stats',
             'counter',
             {
-                nick => $type,
-                type => 'cmdstats',
+                'nick' => $type,
+                'type' => 'cmdstats',
             }
         );
         my $z = 0;
@@ -699,11 +699,13 @@ sub closeStats {
 
         &sqlSet(
             'stats',
-            { 'nick' => $type },
             {
-                type    => 'cmdstats',
-                'time'  => time(),
-                counter => $i,
+               'nick' => $type,
+                'type' => 'cmdstats',
+           },
+            {
+                'time' => time(),
+                'counter' => $i,
             }
         );
     }
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/src/Modules/Google.pl b/src/Modules/Google.pl
new file mode 100644 (file)
index 0000000..dd585cc
--- /dev/null
@@ -0,0 +1,79 @@
+# W3Search drastically altered back to GoogleSearch as Search::Google 
+# was deprecated and requires a key that google no longer provides. 
+# This new module uses REST::Google::Search 
+# Modified by db <db@cave.za.net> 12-01-2008. 
+#
+# Usage: 'chanset _default +Google' in query window with your bot
+#        to enable it in all channels
+#        /msg botnick google <query> OR <addressCharacter>google <query> to use
+
+package Google;
+
+use strict;
+
+my $maxshow = 5;
+
+sub GoogleSearch {
+    my ( $what, $type ) = @_;
+    # $where set to official google colors ;)
+    my $where  = "\00312G\0034o\0038o\00312g\0033l\0034e\003";
+    my $retval = "$where can't find \002$what\002";
+    my $Search;
+    my $referer = "irc://$::server/$::chan/$::who";
+
+    return unless &::loadPerlModule("REST::Google::Search");
+
+    &::DEBUG( "Google::GoogleSearch->referer = $referer" );
+    &::status( "Google::GoogleSearch> Searching Google for: $what");
+    REST::Google::Search->http_referer( $referer );
+    $Search = REST::Google::Search->new( q => $what );
+
+    if ( !defined $Search ) {
+        &::msg( $::who, "$where is invalid search." );
+        &::WARN( "Google::GoogleSearch> $::who generated an invalid search: $where");
+        return;
+    }
+
+    if ( $Search->responseStatus != 200 ) {
+        &::msg( $::who, "http error returned." );
+        &::WARN( "Google::GoogleSearch> http error returned: $Search->responseStatus");
+        return;
+    }
+
+    # No results found
+    if ( not $Search->responseData->results ) {
+        &::DEBUG( "Google::GoogleSearch> $retval" );
+        &::msg( $::who, $retval);
+        return;
+    }
+
+    my $data    = $Search->responseData;
+    my $cursor  = $data->cursor;
+    my @results = $data->results;
+    my $count;
+
+    $retval = "$where says \"\002$what\002\" is at ";
+    foreach my $r (@results) {
+        my $url = $r->url;
+
+        # Returns a string with each %XX sequence replaced with the actual byte
+        # (octet). From URI::Escape uri_unescape()
+        $url =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
+
+        $retval .= " \002or\002 " if ( $count > 0 );
+        $retval .= $url;
+        last if ++$count >= $maxshow; # Only seems to return max of 4?
+    }
+
+    &::performStrictReply($retval);
+}
+
+1;
+# vim:ts=4:sw=4:expandtab:tw=80 
+# Local Variables:
+# mode: cperl
+# tab-width: 4
+# fill-column: 80
+# indent-tabs-mode: nil
+# End:
diff --git a/src/Modules/UserDCC.pl b/src/Modules/UserDCC.pl
deleted file mode 100644 (file)
index 157b139..0000000
+++ /dev/null
@@ -1,1520 +0,0 @@
-#
-#  UserDCC.pl: User Commands, DCC CHAT.
-#      Author: dms
-#     Version: v0.2 (20010119)
-#     Created: 20000707 (from UserExtra.pl)
-#
-
-use strict;
-
-use vars qw(%users %ignore %sched %bans %mask %cache %channels %param
-  %chanconf %dcc);
-use vars qw($who $chan $message $msgType $user $chnick $conn $ident
-  $verifyUser $ucount_userfile $utime_userfile $lobotomized
-  $utime_chanfile $ucount_chanfile);
-use vars qw(@backlog);
-
-sub userDCC {
-
-    # hrm...
-    $message =~ s/\s+$//;
-
-    ### for all users.
-    # quit.
-    if ( $message =~ /^(exit|quit)$/i ) {
-
-        # do ircII clients support remote close? if so, cool!
-        &FIXME("userDCC: quit called.");
-        &dcc_close($who);
-        &status("userDCC: after dcc_close!");
-
-        return;
-    }
-
-    # who.
-    if ( $message =~ /^who$/ ) {
-        my $count   = scalar( keys %{ $dcc{'CHAT'} } );
-        my $dccCHAT = $message;
-
-        &performStrictReply("Start of who ($count users).");
-        foreach ( keys %{ $dcc{'CHAT'} } ) {
-            &performStrictReply("=> $_");
-        }
-        &performStrictReply("End of who.");
-
-        return;
-    }
-
-    ### for those users with enough flags.
-
-    if ( $message =~ /^tellme(\s+(.*))?$/i ) {
-        my $args = $2;
-        if ( $args =~ /^\s*$/ ) {
-            &help('tellme');
-            return;
-        }
-
-        my $result = &doQuestion($args);
-        &performStrictReply($result);
-
-        return;
-    }
-
-    # 4op.
-    if ( $message =~ /^4op(\s+($mask{chan}))?$/i ) {
-        return unless ( &hasFlag('o') );
-
-        my $chan = $2;
-
-        if ( $chan eq '' ) {
-            &help('4op');
-            return;
-        }
-
-        if ( !$channels{$chan}{'o'}{$ident} ) {
-            &msg( $who, "i don't have ops on $chan to do that." );
-            return;
-        }
-
-        # on non-4mode(<4) servers, this may be exploited.
-        if ( $channels{$chan}{'o'}{$who} ) {
-            rawout( "MODE $chan -o+o-o+o" . ( " $who" x 4 ) );
-        }
-        else {
-            rawout( "MODE $chan +o-o+o-o" . ( " $who" x 4 ) );
-        }
-
-        return;
-    }
-
-    # opme.
-    if ( $message =~ /^opme(\s+($mask{chan}))?$/i ) {
-        return unless ( &hasFlag('o') );
-        return unless ( &hasFlag('A') );
-
-        my $chan = $2;
-
-        if ( $chan eq '' ) {
-            &help('4op');
-            return;
-        }
-
-        # can this be exploited?
-        rawout("MODE $chan +o $who");
-
-        return;
-    }
-
-    # backlog.
-    if ( $message =~ /^backlog(\s+(.*))?$/i ) {
-        return unless ( &hasFlag('o') );
-        return unless ( &IsParam('backlog') );
-        my $num = $2;
-        my $max = $param{'backlog'};
-
-        if ( !defined $num ) {
-            &help('backlog');
-            return;
-        }
-        elsif ( $num !~ /^\d+/ ) {
-            &msg( $who, "error: argument is not positive integer." );
-            return;
-        }
-        elsif ( $num > $max or $num < 0 ) {
-            &msg( $who, "error: argument is out of range (max $max)." );
-            return;
-        }
-
-        &msg( $who, "Start of backlog..." );
-        for ( 0 .. $num - 1 ) {
-            sleep 1 if ( $_ % 4 == 0 and $_ != 0 );
-            $conn->privmsg( $who,
-                "[" . ( $_ + 1 ) . "]: $backlog[$max-$num+$_]" );
-        }
-        &msg( $who, "End of backlog." );
-
-        return;
-    }
-
-    # dump variables.
-    if ( $message =~ /^dumpvars$/i ) {
-        return unless ( &hasFlag('o') );
-        return unless ( &IsParam('DumpVars') );
-
-        &status("Dumping all variables...");
-        &dumpallvars();
-
-        return;
-    }
-
-    # dump variables ][.
-    if ( $message =~ /^symdump$/i ) {
-        return unless ( &hasFlag('o') );
-        return unless ( &IsParam('DumpVars2') );
-
-        &status("Dumping all variables...");
-        &symdumpAllFile();
-
-        return;
-    }
-
-    # kick.
-    if ( $message =~ /^kick(\s+(.*?))$/ ) {
-        return unless ( &hasFlag('o') );
-
-        my $arg = $2;
-
-        if ( $arg eq '' ) {
-            &help('kick');
-            return;
-        }
-        my @args = split( /\s+/, $arg );
-        my ( $nick, $chan, $reason ) = @args;
-
-        if ( &validChan($chan) == 0 ) {
-            &msg( $who, "error: invalid channel \002$chan\002" );
-            return;
-        }
-
-        if ( &IsNickInChan( $nick, $chan ) == 0 ) {
-            &msg( $who, "$nick is not in $chan." );
-            return;
-        }
-
-        &kick( $nick, $chan, $reason );
-
-        return;
-    }
-
-    # mode.
-    if ( $message =~ /^mode(\s+(.*))?$/ ) {
-        return unless ( &hasFlag('n') );
-        my ( $chan, $mode ) = split /\s+/, $2, 2;
-
-        if ( $chan eq '' ) {
-            &help('mode');
-            return;
-        }
-
-        if ( &validChan($chan) == 0 ) {
-            &msg( $who, "error: invalid channel \002$chan\002" );
-            return;
-        }
-
-        if ( !$channels{$chan}{o}{$ident} ) {
-            &msg( $who, "error: don't have ops on \002$chan\002" );
-            return;
-        }
-
-        &mode( $chan, $mode );
-
-        return;
-    }
-
-    # part.
-    if ( $message =~ /^part(\s+(\S+))?$/i ) {
-        return unless ( &hasFlag('o') );
-        my $jchan = $2;
-
-        if ( $jchan !~ /^$mask{chan}$/ ) {
-            &msg( $who, "error, invalid chan." );
-            &help('part');
-            return;
-        }
-
-        if ( !&validChan($jchan) ) {
-            &msg( $who, "error, I'm not on that chan." );
-            return;
-        }
-
-        &msg( $jchan, "Leaving. (courtesy of $who)." );
-        &part($jchan);
-        return;
-    }
-
-    # lobotomy. sometimes we want the bot to be _QUIET_.
-    if ( $message =~ /^(lobotomy|bequiet)$/i ) {
-        return unless ( &hasFlag('o') );
-
-        if ($lobotomized) {
-            &performReply("i'm already lobotomized");
-        }
-        else {
-            &performReply('i have been lobotomized');
-            $lobotomized = 1;
-        }
-
-        return;
-    }
-
-    # unlobotomy.
-    if ( $message =~ /^(unlobotomy|benoisy)$/i ) {
-        return unless ( &hasFlag('o') );
-
-        if ($lobotomized) {
-            &performReply('i have been unlobotomized, woohoo');
-            $lobotomized = 0;
-            delete $cache{lobotomy};
-
-            #      undef $cache{lobotomy};     # ??
-        }
-        else {
-            &performReply("i'm not lobotomized");
-        }
-
-        return;
-    }
-
-    # op.
-    if ( $message =~ /^op(\s+(.*))?$/i ) {
-        return unless ( &hasFlag('o') );
-        my ($opee) = lc $2;
-        my @chans;
-
-        if ( $opee =~ / / ) {
-            if ( $opee =~ /^(\S+)\s+(\S+)$/ ) {
-                $opee  = $1;
-                @chans = ($2);
-                if ( !&validChan($2) ) {
-                    &msg( $who, "error: invalid chan ($2)." );
-                    return;
-                }
-            }
-            else {
-                &msg( $who, "error: invalid params." );
-                return;
-            }
-        }
-        else {
-            @chans = keys %channels;
-        }
-
-        my $found = 0;
-        my $op    = 0;
-        foreach (@chans) {
-            next unless ( &IsNickInChan( $opee, $_ ) );
-            $found++;
-            if ( $channels{$_}{'o'}{$opee} ) {
-                &performStrictReply("op: $opee already has ops on $_");
-                next;
-            }
-            $op++;
-
-            &performStrictReply("opping $opee on $_");
-            &op( $_, $opee );
-        }
-
-        if ( $found != $op ) {
-            &performStrictReply("op: opped on all possible channels.");
-        }
-        else {
-            &DEBUG("op: found => '$found'.");
-            &DEBUG("op:    op => '$op'.");
-        }
-
-        return;
-    }
-
-    # deop.
-    if ( $message =~ /^deop(\s+(.*))?$/i ) {
-        return unless ( &hasFlag('o') );
-        my ($opee) = lc $2;
-        my @chans;
-
-        if ( $opee =~ / / ) {
-            if ( $opee =~ /^(\S+)\s+(\S+)$/ ) {
-                $opee  = $1;
-                @chans = ($2);
-                if ( !&validChan($2) ) {
-                    &msg( $who, "error: invalid chan ($2)." );
-                    return;
-                }
-            }
-            else {
-                &msg( $who, "error: invalid params." );
-                return;
-            }
-        }
-        else {
-            @chans = keys %channels;
-        }
-
-        my $found = 0;
-        my $op    = 0;
-        foreach (@chans) {
-            next unless ( &IsNickInChan( $opee, $_ ) );
-            $found++;
-            if ( !exists $channels{$_}{'o'}{$opee} ) {
-                &status("deop: $opee already has no ops on $_");
-                next;
-            }
-            $op++;
-
-            &status("deopping $opee on $_ at ${who}'s request");
-            &deop( $_, $opee );
-        }
-
-        if ( $found != $op ) {
-            &status("deop: deopped on all possible channels.");
-        }
-        else {
-            &DEBUG("deop: found => '$found'.");
-            &DEBUG("deop: op => '$op'.");
-        }
-
-        return;
-    }
-
-    # say.
-    if ( $message =~ s/^say\s+(\S+)\s+(.*)// ) {
-        return unless ( &hasFlag('o') );
-        my ( $chan, $msg ) = ( lc $1, $2 );
-
-        &DEBUG("chan => '$1', msg => '$msg'.");
-
-        &msg( $chan, $msg );
-
-        return;
-    }
-
-    # do.
-    if ( $message =~ s/^do\s+(\S+)\s+(.*)// ) {
-        return unless ( &hasFlag('o') );
-        my ( $chan, $msg ) = ( lc $1, $2 );
-
-        &DEBUG("chan => '$1', msg => '$msg'.");
-
-        &action( $chan, $msg );
-
-        return;
-    }
-
-    # die.
-    if ( $message =~ /^die$/ ) {
-        return unless ( &hasFlag('n') );
-
-        &doExit();
-
-        &status("Dying by $who\'s request");
-        exit 0;
-    }
-
-    # global factoid substitution.
-    if ( $message =~ m|^\* =~ s([/,#])(.+?)\1(.*?)\1;?\s*$| ) {
-        my ( $delim, $op, $np ) = ( $1, $2, $3 );
-        return unless ( &hasFlag('n') );
-        ### TODO: support flags to do full-on global.
-
-        # incorrect format.
-        if ( $np =~ /$delim/ ) {
-            &performReply(
-"looks like you used the delimiter too many times. You may want to use a different delimiter, like ':' or '#'."
-            );
-            return;
-        }
-
-        ### TODO: fix up $op to support mysql/sqlite/pgsql
-        ### TODO: => add db/sql specific function to fix this.
-        my @list =
-          &searchTable( 'factoids', 'factoid_key', 'factoid_value', $op );
-
-        if ( !scalar @list ) {
-            &performReply("Expression didn't match anything.");
-            return;
-        }
-
-        if ( scalar @list > 100 ) {
-            &performReply("regex found more than 100 matches... not doing.");
-            return;
-        }
-
-        &status( "gsubst: going to alter " . scalar(@list) . " factoids." );
-        &performReply( 'going to alter ' . scalar(@list) . " factoids." );
-
-        my $error = 0;
-        foreach (@list) {
-            my $faqtoid = $_;
-
-            next if ( &IsLocked($faqtoid) == 1 );
-            my $result = &getFactoid($faqtoid);
-            my $was    = $result;
-            &DEBUG("was($faqtoid) => '$was'.");
-
-            # global global
-            # we could support global local (once off).
-            if ( $result =~ s/\Q$op/$np/gi ) {
-                if ( length $result > $param{'maxDataSize'} ) {
-                    &performReply("that's too long (or was long)");
-                    return;
-                }
-                &setFactInfo( $faqtoid, 'factoid_value', $result );
-                &status("update: '$faqtoid' =is=> '$result'; was '$was'");
-            }
-            else {
-                &WARN(
-"subst: that's weird... thought we found the string ($op) in '$faqtoid'."
-                );
-                $error++;
-            }
-        }
-
-        if ($error) {
-            &ERROR("Some warnings/errors?");
-        }
-
-        &performReply( "Ok... did s/$op/$np/ for "
-              . ( scalar(@list) - $error )
-              . ' factoids' );
-
-        return;
-    }
-
-    # jump.
-    if ( $message =~ /^jump(\s+(\S+))?$/i ) {
-        return unless ( &hasFlag('n') );
-
-        if ( $2 eq '' ) {
-            &help('jump');
-            return;
-        }
-
-        my ( $server, $port );
-        if ( $2 =~ /^(\S+)(:(\d+))?$/ ) {
-            $server = $1;
-            $port = $3 || 6667;
-        }
-        else {
-            &msg( $who, "invalid format." );
-            return;
-        }
-
-        &status("jumping servers... $server...");
-        $conn->quit("jumping to $server");
-
-        if ( &irc( $server, $port ) == 0 ) {
-            &ircloop();
-        }
-    }
-
-    # reload.
-    if ( $message =~ /^reload$/i ) {
-        return unless ( &hasFlag('n') );
-
-        &status("USER reload $who");
-        &performStrictReply("reloading...");
-        &reloadAllModules();
-        &performStrictReply("reloaded.");
-
-        return;
-    }
-
-    # reset.
-    if ( $message =~ /^reset$/i ) {
-        return unless ( &hasFlag('n') );
-
-        &msg( $who, "resetting..." );
-        my @done;
-        foreach ( keys %channels, keys %chanconf ) {
-            my $c = $_;
-            next if ( grep /^\Q$c\E$/i, @done );
-
-            &part($_);
-
-            push( @done, $_ );
-            sleep 1;
-        }
-        &DEBUG('before clearircvars');
-        &clearIRCVars();
-        &DEBUG('before joinnextchan');
-        &joinNextChan();
-        &DEBUG('after joinnextchan');
-
-        &status("USER reset $who");
-        &msg( $who, 'reset complete' );
-
-        return;
-    }
-
-    # rehash.
-    if ( $message =~ /^rehash$/ ) {
-        return unless ( &hasFlag('n') );
-
-        &msg( $who, "rehashing..." );
-        &restart('REHASH');
-        &status("USER rehash $who");
-        &msg( $who, 'rehashed' );
-
-        return;
-    }
-
-    #####
-    ##### USER//CHAN SPECIFIC CONFIGURATION COMMANDS
-    #####
-
-    if ( $message =~ /^chaninfo(\s+(.*))?$/ ) {
-        my @args = split /[\s\t]+/, $2;    # hrm.
-
-        if ( scalar @args != 1 ) {
-            &help('chaninfo');
-            return;
-        }
-
-        if ( !exists $chanconf{ $args[0] } ) {
-            &performStrictReply("no such channel $args[0]");
-            return;
-        }
-
-        &performStrictReply("showing channel conf.");
-        foreach ( sort keys %{ $chanconf{ $args[0] } } ) {
-            &performStrictReply("$chan: $_ => $chanconf{$args[0]}{$_}");
-        }
-        &performStrictReply("End of chaninfo.");
-
-        return;
-    }
-
-    # chanadd.
-    if ( $message =~ /^(chanset|chanadd)(\s+(.*?))?$/ ) {
-        my $cmd     = $1;
-        my $args    = $3;
-        my $no_chan = 0;
-
-        if ( !defined $args ) {
-            &help($cmd);
-            return;
-        }
-
-        my @chans;
-        while ( $args =~ s/^($mask{chan})\s*// ) {
-            push( @chans, lc($1) );
-        }
-
-        if ( !scalar @chans ) {
-            push( @chans, '_default' );
-            $no_chan = 1;
-        }
-
-        my ( $what, $val ) = split /[\s\t]+/, $args, 2;
-
-        ### TODO: "cannot set values without +m".
-        return unless ( &hasFlag('n') );
-
-        # READ ONLY.
-        if ( defined $what and $what !~ /^[-+]/ and !defined $val and $no_chan )
-        {
-            &performStrictReply("Showing $what values on all channels...");
-
-            my %vals;
-            foreach ( keys %chanconf ) {
-                my $val;
-                if ( defined $chanconf{$_}{$what} ) {
-                    $val = $chanconf{$_}{$what};
-                }
-                else {
-                    $val = "NOT-SET";
-                }
-                $vals{$val}{$_} = 1;
-            }
-
-            foreach ( keys %vals ) {
-                &performStrictReply( "  $what = $_("
-                      . scalar( keys %{ $vals{$_} } ) . "): "
-                      . join( ' ', sort keys %{ $vals{$_} } ) );
-            }
-
-            &performStrictReply("End of list.");
-
-            return;
-        }
-
-        ### TODO: move to UserDCC again.
-        if ( $cmd eq 'chanset' and !defined $what ) {
-            &DEBUG("showing channel conf.");
-
-            foreach $chan (@chans) {
-                if ( $chan eq '_default' ) {
-                    &performStrictReply('Default channel settings');
-                }
-                else {
-                    &performStrictReply("chan: $chan (see _default also)");
-                }
-                my @items;
-                my $str = '';
-                foreach ( sort keys %{ $chanconf{$chan} } ) {
-                    my $newstr = join( ', ', @items );
-                    ### TODO: make length use channel line limit?
-                    if ( length $newstr > 370 ) {
-                        &performStrictReply(" $str");
-                        @items = ();
-                    }
-                    $str = $newstr;
-                    push( @items, "$_ => $chanconf{$chan}{$_}" );
-                }
-                if (@items) {
-                    my $str = join( ', ', @items );
-                    &performStrictReply(" $str");
-                }
-            }
-            return;
-        }
-
-        $cache{confvars}{$what} = $val;
-        &rehashConfVars();
-
-        foreach (@chans) {
-            &chanSet( $cmd, $_, $what, $val );
-        }
-
-        return;
-    }
-
-    if ( $message =~ /^(chanunset|chandel)(\s+(.*))?$/ ) {
-        return unless ( &hasFlag('n') );
-        my $cmd     = $1;
-        my $args    = $3;
-        my $no_chan = 0;
-
-        if ( !defined $args ) {
-            &help($cmd);
-            return;
-        }
-
-        my ($chan);
-        my $delete = 0;
-        if ( $args =~ s/^(\-)?($mask{chan})\s*// ) {
-            $chan = $2;
-            $delete = ($1) ? 1 : 0;
-        }
-        else {
-            &VERB( "no chan arg; setting to default.", 2 );
-            $chan    = '_default';
-            $no_chan = 1;
-        }
-
-        if ( !exists $chanconf{$chan} ) {
-            &performStrictReply("no such channel $chan");
-            return;
-        }
-
-        if ( $args ne '' ) {
-
-            if ( !&getChanConf( $args, $chan ) ) {
-                &performStrictReply("$args does not exist for $chan");
-                return;
-            }
-
-            my @chans = &ChanConfList($args);
-            &DEBUG( "scalar chans => " . scalar(@chans) );
-            if ( scalar @chans == 1 and $chans[0] eq '_default' and !$no_chan )
-            {
-                &performStrictReply(
-"ok, $args was set only for _default; unsetting for _defaul but setting for other chans."
-                );
-
-                my $val = $chanconf{$_}{_default};
-                foreach ( keys %chanconf ) {
-                    $chanconf{$_}{$args} = $val;
-                }
-                delete $chanconf{_default}{$args};
-                $cache{confvars}{$args} = 0;
-                &rehashConfVars();
-
-                return;
-            }
-
-            if ( $no_chan and !exists( $chanconf{_default}{$args} ) ) {
-                &performStrictReply(
-"ok, $args for _default does not exist, removing from all chans."
-                );
-
-                foreach ( keys %chanconf ) {
-                    next unless ( exists $chanconf{$_}{$args} );
-                    &DEBUG("delete chanconf{$_}{$args};");
-                    delete $chanconf{$_}{$args};
-                }
-                $cache{confvars}{$args} = 0;
-                &rehashConfVars();
-
-                return;
-            }
-
-            &performStrictReply(
-"Unsetting channel ($chan) option $args. (was $chanconf{$chan}{$args})"
-            );
-            delete $chanconf{$chan}{$args};
-
-            return;
-        }
-
-        if ($delete) {
-            &performStrictReply("Deleting channel $chan for sure!");
-            $utime_chanfile = time();
-            $ucount_chanfile++;
-
-            &part($chan);
-            &performStrictReply("Leaving $chan...");
-
-            delete $chanconf{$chan};
-        }
-        else {
-            &performStrictReply("Prefix channel with '-' to delete for sure.");
-        }
-
-        return;
-    }
-
-    if ( $message =~ /^newpass(\s+(.*))?$/ ) {
-        my (@args) = split /[\s\t]+/, $2 || '';
-
-        if ( scalar @args != 1 ) {
-            &help('newpass');
-            return;
-        }
-
-        my $u     = &getUser($who);
-        my $crypt = &mkcrypt( $args[0] );
-
-        &performStrictReply("Set your passwd to '$crypt'");
-        $users{$u}{PASS} = $crypt;
-
-        $utime_userfile = time();
-        $ucount_userfile++;
-
-        return;
-    }
-
-    if ( $message =~ /^chpass(\s+(.*))?$/ ) {
-        my (@args) = split /[\s\t]+/, $2 || '';
-
-        if ( !scalar @args ) {
-            &help('chpass');
-            return;
-        }
-
-        if ( !&IsUser( $args[0] ) ) {
-            &performStrictReply("user $args[0] is not valid.");
-            return;
-        }
-
-        my $u = &getUser( $args[0] );
-        if ( !defined $u ) {
-            &performStrictReply("Internal error, u = NULL.");
-            return;
-        }
-
-        if ( scalar @args == 1 ) {
-
-            # del pass.
-            if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
-                &performStrictReply("cannot remove passwd of others.");
-                return;
-            }
-
-            if ( !exists $users{$u}{PASS} ) {
-                &performStrictReply("$u does not have pass set anyway.");
-                return;
-            }
-
-            &performStrictReply("Deleted pass from $u.");
-
-            $utime_userfile = time();
-            $ucount_userfile++;
-
-            delete $users{$u}{PASS};
-
-            return;
-        }
-
-        my $crypt = &mkcrypt( $args[1] );
-        &performStrictReply("Set $u's passwd to '$crypt'");
-        $users{$u}{PASS} = $crypt;
-
-        $utime_userfile = time();
-        $ucount_userfile++;
-
-        return;
-    }
-
-    if ( $message =~ /^chattr(\s+(.*))?$/ ) {
-        my (@args) = split /[\s\t]+/, $2 || '';
-
-        if ( !scalar @args ) {
-            &help('chattr');
-            return;
-        }
-
-        my $chflag;
-        my $user;
-        if ( $args[0] =~ /^$mask{nick}$/i ) {
-
-            # <nick>
-            $user   = &getUser( $args[0] );
-            $chflag = $args[1];
-        }
-        else {
-
-            # <flags>
-            $user = &getUser($who);
-            &DEBUG("user $who... nope.") unless ( defined $user );
-            $user   = &getUser($verifyUser);
-            $chflag = $args[0];
-        }
-
-        if ( !defined $user ) {
-            &performStrictReply("user does not exist.");
-            return;
-        }
-
-        my $flags = $users{$user}{FLAGS};
-        if ( !defined $chflag ) {
-            &performStrictReply("Flags for $user: $flags");
-            return;
-        }
-
-        &DEBUG("who => $who");
-        &DEBUG("verifyUser => $verifyUser");
-        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
-            &performStrictReply("cannto change attributes of others.");
-            return 'REPLY';
-        }
-
-        my $state;
-        my $change = 0;
-        foreach ( split //, $chflag ) {
-            if ( $_ eq "+" ) { $state = 1; next; }
-            if ( $_ eq "-" ) { $state = 0; next; }
-
-            if ( !defined $state ) {
-                &performStrictReply("no initial + or - was found in attr.");
-                return;
-            }
-
-            if ($state) {
-                next if ( $flags =~ /\Q$_\E/ );
-                $flags .= $_;
-            }
-            else {
-                if (    &IsParam('owner')
-                    and $param{owner} =~ /^\Q$user\E$/i
-                    and $flags        =~ /[nmo]/ )
-                {
-                    &performStrictReply("not removing flag $_ for $user.");
-                    next;
-                }
-                next unless ( $flags =~ s/\Q$_\E// );
-            }
-
-            $change++;
-        }
-
-        if ($change) {
-            $utime_userfile = time();
-            $ucount_userfile++;
-
-            #$flags.*FLAGS sort
-            $flags = join( '', sort split( '', $flags ) );
-            &performStrictReply("Current flags: $flags");
-            $users{$user}{FLAGS} = $flags;
-        }
-        else {
-            &performStrictReply("No flags changed: $flags");
-        }
-
-        return;
-    }
-
-    if ( $message =~ /^chnick(\s+(.*))?$/ ) {
-        my (@args) = split /[\s\t]+/, $2 || '';
-
-        if ( $who eq '_default' ) {
-            &WARN("$who or verifyuser tried to run chnick.");
-            return 'REPLY';
-        }
-
-        if ( !scalar @args or scalar @args > 2 ) {
-            &help('chnick');
-            return;
-        }
-
-        if ( scalar @args == 1 ) {    # 1
-            $user = &getUser($who);
-            &DEBUG("nope, not $who.") unless ( defined $user );
-            $user ||= &getUser($verifyUser);
-            $chnick = $args[0];
-        }
-        else {                        # 2
-            $user   = &getUser( $args[0] );
-            $chnick = $args[1];
-        }
-
-        if ( !defined $user ) {
-            &performStrictReply("user $who or $args[0] does not exist.");
-            return;
-        }
-
-        if ( $user =~ /^\Q$chnick\E$/i ) {
-            &performStrictReply("user == chnick. why should I do that?");
-            return;
-        }
-
-        if ( &getUser($chnick) ) {
-            &performStrictReply("user $chnick is already used!");
-            return;
-        }
-
-        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
-            &performStrictReply("cannto change nick of others.");
-            return 'REPLY' if ( $who eq '_default' );
-            return;
-        }
-
-        foreach ( keys %{ $users{$user} } ) {
-            $users{$chnick}{$_} = $users{$user}{$_};
-            delete $users{$user}{$_};
-        }
-        undef $users{$user};    # ???
-
-        $utime_userfile = time();
-        $ucount_userfile++;
-
-        &performStrictReply("Changed '$user' to '$chnick' successfully.");
-
-        return;
-    }
-
-    if ( $message =~ /^(hostadd|hostdel)(\s+(.*))?$/ ) {
-        my $cmd = $1;
-        my (@args) = split /[\s\t]+/, $3 || '';
-        my $state = ( $1 eq "hostadd" ) ? 1 : 0;
-
-        if ( !scalar @args ) {
-            &help($cmd);
-            return;
-        }
-
-        if ( $who eq '_default' ) {
-            &WARN("$who or verifyuser tried to run $cmd.");
-            return 'REPLY';
-        }
-
-        my ( $user, $mask );
-        if ( $args[0] =~ /^$mask{nick}$/i ) {    # <nick>
-            return unless ( &hasFlag('n') );
-            $user = &getUser( $args[0] );
-            $mask = $args[1];
-        }
-        else {                                   # <mask>
-                # FIXME: who or verifyUser. (don't remember why)
-            $user = &getUser($who);
-            $mask = $args[0];
-        }
-
-        if ( !defined $user ) {
-            &performStrictReply("user $user does not exist.");
-            return;
-        }
-
-        if ( !defined $mask ) {
-            &performStrictReply( "Hostmasks for $user: "
-                  . join( ' ', keys %{ $users{$user}{HOSTS} } ) );
-            return;
-        }
-
-        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
-            &performStrictReply("cannto change masks of others.");
-            return;
-        }
-
-        my $count = scalar keys %{ $users{$user}{HOSTS} };
-
-        if ($state) {    # add.
-            if ( $mask !~ /^$mask{nuh}$/ ) {
-                &performStrictReply(
-                    "error: mask ($mask) is not a real hostmask.");
-                return;
-            }
-
-            if ( exists $users{$user}{HOSTS}{$mask} ) {
-                &performStrictReply("mask $mask already exists.");
-                return;
-            }
-
-            ### TODO: override support.
-            $users{$user}{HOSTS}{$mask} = 1;
-
-            &performStrictReply("Added $mask to list of masks.");
-
-        }
-        else {    # delete.
-
-            if ( !exists $users{$user}{HOSTS}{$mask} ) {
-                &performStrictReply("mask $mask does not exist.");
-                return;
-            }
-
-            ### TODO: wildcard support. ?
-            delete $users{$user}{HOSTS}{$mask};
-
-            if ( scalar keys %{ $users{$user}{HOSTS} } != $count ) {
-                &performStrictReply("Removed $mask from list of masks.");
-            }
-            else {
-                &performStrictReply(
-                    "error: could not find $mask in list of masks.");
-                return;
-            }
-        }
-
-        $utime_userfile = time();
-        $ucount_userfile++;
-
-        return;
-    }
-
-    if ( $message =~ /^(banadd|bandel)(\s+(.*))?$/ ) {
-        my $cmd     = $1;
-        my $flatarg = $3;
-        my (@args) = split /[\s\t]+/, $3 || '';
-        my $state = ( $1 eq "banadd" ) ? 1 : 0;
-
-        if ( !scalar @args ) {
-            &help($cmd);
-            return;
-        }
-
-        my ( $mask, $chan, $time, $reason );
-
-        if ( $flatarg =~ s/^($mask{nuh})\s*// ) {
-            $mask = $1;
-        }
-        else {
-            &DEBUG("arg does not contain nuh mask?");
-        }
-
-        if ( $flatarg =~ s/^($mask{chan})\s*// ) {
-            $chan = $1;
-        }
-        else {
-            $chan = '*';    # _default instead?
-        }
-
-        if ( $state == 0 ) {    # delete.
-            my @c = &banDel($mask);
-
-            foreach (@c) {
-                &unban( $mask, $_ );
-            }
-
-            if (@c) {
-                &performStrictReply("Removed $mask from chans: @c");
-            }
-            else {
-                &performStrictReply("$mask was not found in ban list.");
-            }
-
-            return;
-        }
-
-        ###
-        # add ban.
-        ###
-
-        # time.
-        if ( $flatarg =~ s/^(\d+)\s*// ) {
-            $time = $1;
-            &DEBUG("time = $time.");
-            if ( $time < 0 ) {
-                &performStrictReply("error: time cannot be negatime?");
-                return;
-            }
-        }
-        else {
-            $time = 0;
-        }
-
-        if ( $flatarg =~ s/^(.*)$// ) {    # need length?
-            $reason = $1;
-        }
-
-        if ( !&IsFlag('n') and $who !~ /^\Q$verifyUser\E$/i ) {
-            &performStrictReply("cannto change masks of others.");
-            return;
-        }
-
-        if ( $mask !~ /^$mask{nuh}$/ ) {
-            &performStrictReply("error: mask ($mask) is not a real hostmask.");
-            return;
-        }
-
-        if ( &banAdd( $mask, $chan, $time, $reason ) == 2 ) {
-            &performStrictReply("ban already exists; overwriting.");
-        }
-        &performStrictReply(
-            "Added $mask for $chan (time => $time, reason => $reason)");
-
-        return;
-    }
-
-    if ( $message =~ /^whois(\s+(.*))?$/ ) {
-        my $arg = $2;
-
-        if ( !defined $arg ) {
-            &help('whois');
-            return;
-        }
-
-        my $user = &getUser($arg);
-        if ( !defined $user ) {
-            &performStrictReply("whois: user '$arg' does not exist.");
-            return;
-        }
-
-        ### TODO: better (eggdrop-like) output.
-        &performStrictReply("user: $user");
-        foreach ( keys %{ $users{$user} } ) {
-            my $ref = ref $users{$user}{$_};
-
-            if ( $ref eq 'HASH' ) {
-                my $type = $_;
-                ### DOES NOT WORK???
-                foreach ( keys %{ $users{$user}{$type} } ) {
-                    &performStrictReply("    $type => $_");
-                }
-                next;
-            }
-
-            &performStrictReply("    $_ => $users{$user}{$_}");
-        }
-        &performStrictReply("End of USER whois.");
-
-        return;
-    }
-
-    if ( $message =~ /^bans(\s+(.*))?$/ ) {
-        my $arg = $2;
-
-        if ( defined $arg ) {
-            if ( $arg ne '_default' and !&validChan($arg) ) {
-                &performStrictReply("error: chan $chan is invalid.");
-                return;
-            }
-        }
-
-        if ( !scalar keys %bans ) {
-            &performStrictReply("Ban list is empty.");
-            return;
-        }
-
-        my $c;
-        &performStrictReply(
-            "     mask: expire, time-added, count, who-by, reason");
-        foreach $c ( keys %bans ) {
-            next unless ( !defined $arg or $arg =~ /^\Q$c\E$/i );
-            &performStrictReply("  $c:");
-
-            foreach ( keys %{ $bans{$c} } ) {
-                my $val = $bans{$c}{$_};
-
-                if ( ref $val eq 'ARRAY' ) {
-                    my @array = @{$val};
-                    &performStrictReply("    $_: @array");
-                }
-                else {
-                    &DEBUG("unknown ban: $val");
-                }
-            }
-        }
-        &performStrictReply("END of bans.");
-
-        return;
-    }
-
-    if ( $message =~ /^banlist(\s+(.*))?$/ ) {
-        my $arg = $2;
-
-        if ( defined $arg and $arg !~ /^$mask{chan}$/ ) {
-            &performStrictReply("error: chan $chan is invalid.");
-            return;
-        }
-
-        &DEBUG("bans for global or arg => $arg.");
-        foreach ( keys %bans ) {    #CHANGE!!!
-            &DEBUG("  $_ => $bans{$_}.");
-        }
-
-        &DEBUG("End of bans.");
-        &performStrictReply("END of bans.");
-
-        return;
-    }
-
-    if ( $message =~ /^save$/ ) {
-        return unless ( &hasFlag('o') );
-
-        &writeUserFile();
-        &writeChanFile();
-        &performStrictReply('saved user and chan files');
-
-        return;
-    }
-
-    ### ALIASES.
-    $message =~ s/^addignore/+ignore/;
-    $message =~ s/^(del|un)ignore/-ignore/;
-
-    # ignore.
-    if ( $message =~ /^(\+|\-)ignore(\s+(.*))?$/i ) {
-        return unless ( &hasFlag('o') );
-        my $state = ( $1 eq "+" ) ? 1 : 0;
-        my $str   = $1 . 'ignore';
-        my $args  = $3;
-
-        if ( !$args ) {
-            &help($str);
-            return;
-        }
-
-        my ( $mask, $chan, $time, $comment );
-
-        # mask.
-        if ( $args =~ s/^($mask{nuh})\s*// ) {
-            $mask = $1;
-        }
-        else {
-            &ERROR("no NUH mask?");
-            return;
-        }
-
-        if ( !$state ) {    # delignore.
-            if ( &ignoreDel($mask) ) {
-                &performStrictReply("ok, deleted ignores for $mask.");
-            }
-            else {
-                &performStrictReply("could not find $mask in ignore list.");
-            }
-            return;
-        }
-
-        ###
-        # addignore.
-        ###
-
-        # chan.
-        if ( $args =~ s/^($mask{chan}|\*)\s*// ) {
-            $chan = $1;
-        }
-        else {
-            $chan = '*';
-        }
-
-        # time.
-        if ( $args =~ s/^(\d+)\s*// ) {
-            $time = $1;    # time is in minutes
-        }
-        else {
-            $time = 0;
-        }
-
-        # time.
-        if ($args) {
-            $comment = $args;
-        }
-        else {
-            $comment = "added by $who";
-        }
-
-        if ( &ignoreAdd( $mask, $chan, $time, $comment ) > 1 ) {
-            &performStrictReply(
-                "FIXME: $mask already in ignore list; written over anyway.");
-        }
-        else {
-            &performStrictReply("added $mask to ignore list.");
-        }
-
-        return;
-    }
-
-    if ( $message =~ /^ignore(\s+(.*))?$/ ) {
-        my $arg = $2;
-
-        if ( defined $arg ) {
-            if ( $arg !~ /^$mask{chan}$/ ) {
-                &performStrictReply("error: chan $chan is invalid.");
-                return;
-            }
-
-            if ( !&validChan($arg) ) {
-                &performStrictReply("error: chan $arg is invalid.");
-                return;
-            }
-
-            &performStrictReply("Showing bans for $arg only.");
-        }
-
-        if ( !scalar keys %ignore ) {
-            &performStrictReply("Ignore list is empty.");
-            return;
-        }
-
-        ### TODO: proper (eggdrop-like) formatting.
-        my $c;
-        &performStrictReply("    mask: expire, time-added, who, comment");
-        foreach $c ( keys %ignore ) {
-            next unless ( !defined $arg or $arg =~ /^\Q$c\E$/i );
-            &performStrictReply("  $c:");
-
-            foreach ( keys %{ $ignore{$c} } ) {
-                my $ref = ref $ignore{$c}{$_};
-                if ( $ref eq 'ARRAY' ) {
-                    my @array = @{ $ignore{$c}{$_} };
-                    &performStrictReply("      $_: @array");
-                }
-                else {
-                    &DEBUG("unknown ignore line?");
-                }
-            }
-        }
-        &performStrictReply("END of ignore.");
-
-        return;
-    }
-
-    # useradd/userdel.
-    if ( $message =~ /^(useradd|userdel)(\s+(.*))?$/i ) {
-        my $cmd    = $1;
-        my @args   = split /\s+/, $3 || '';
-        my $args   = $3;
-        my $state  = ( $cmd eq "useradd" ) ? 1 : 0;
-
-        if ( !scalar @args ) {
-            &help($cmd);
-            return;
-        }
-
-        if ( $cmd eq 'useradd' ) {
-            if ( scalar @args != 2 ) {
-                &performStrictReply('useradd requires hostmask argument.');
-                return;
-            }
-        }
-        elsif ( scalar @args != 1 ) {
-            &performStrictReply('too many arguments.');
-            return;
-        }
-
-        if ($state) {
-
-            # adduser.
-            if ( scalar @args == 1 ) {
-                $args[1] = &getHostMask( $args[0] );
-                &performStrictReply(
-                    "Attemping to guess $args[0]'s hostmask...");
-
-                # crude hack... crappy Net::IRC
-                $conn->schedule(
-                    5,
-                    sub {
-
-                        # hopefully this is right.
-                        my $nick = ( keys %{ $cache{nuhInfo} } )[0];
-                        if ( !defined $nick ) {
-                            &performStrictReply(
-"couldn't get nuhinfo... adding user without a hostmask."
-                            );
-                            &userAdd($nick);
-                            return;
-                        }
-                        my $mask = &makeHostMask( $cache{nuhInfo}{$nick}{NUH} );
-
-                        if ( &userAdd( $nick, $mask ) ) {
-
-                            # success.
-                            &performStrictReply(
-                                "Added $nick with flags $users{$nick}{FLAGS}");
-                            my @hosts = keys %{ $users{$nick}{HOSTS} };
-                            &performStrictReply("hosts: @hosts");
-                        }
-                    }
-                );
-                return;
-            }
-
-            &DEBUG("args => @args");
-            if ( &userAdd(@args) ) {    # success.
-                &performStrictReply(
-                    "Added $args[0] with flags $users{$args[0]}{FLAGS}");
-                my @hosts = keys %{ $users{ $args[0] }{HOSTS} };
-                &performStrictReply("hosts: @hosts");
-
-            }
-            else {                      # failure.
-                &performStrictReply("User $args[0] already exists");
-            }
-
-        }
-        else {                          # deluser.
-
-            if ( &userDel( $args[0] ) ) {    # success.
-                &performStrictReply("Deleted $args[0] successfully.");
-
-            }
-            else {                           # failure.
-                &performStrictReply("User $args[0] does not exist.");
-            }
-
-        }
-        return;
-    }
-
-    if ( $message =~ /^sched$/ ) {
-        my @list;
-        my @run;
-
-        my %time;
-        foreach ( keys %sched ) {
-            next unless ( exists $sched{$_}{TIME} );
-            $time{ $sched{$_}{TIME} - time() }{$_} = 1;
-            push( @list, $_ );
-
-            next unless ( exists $sched{$_}{RUNNING} );
-            push( @run, $_ );
-        }
-
-        my @time;
-        foreach ( sort { $a <=> $b } keys %time ) {
-            my $str = join( ', ', sort keys %{ $time{$_} } );
-            &DEBUG("time => $_, str => $str");
-            push( @time, "$str (" . &Time2String($_) . ")" );
-        }
-
-        &performStrictReply( &formListReply( 0, "Schedulers: ", @time ) );
-        &performStrictReply(
-            &formListReply( 0, "Scheds to run: ", sort @list ) );
-        &performStrictReply(
-            &formListReply(
-                0, "Scheds running(should not happen?) ",
-                sort @run
-            )
-        );
-
-        return;
-    }
-
-    # quite a cool hack: reply in DCC CHAT.
-    $msgType = 'chat' if ( exists $dcc{'CHAT'}{$who} );
-
-    my $done = 0;
-    $done++ if &parseCmdHook($message);
-    $done++ unless ( &Modules() );
-
-    if ($done) {
-        &DEBUG("running non DCC CHAT command inside DCC CHAT!");
-        return;
-    }
-
-    return 'REPLY';
-}
-
-1;
-
-# vim:ts=4:sw=4:expandtab:tw=80
diff --git a/src/Modules/W3Search.pl b/src/Modules/W3Search.pl
deleted file mode 100644 (file)
index 27bdbd7..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# WWWSearch backend, with queries updating the is-db (optionally)
-# Uses WWW::Search::Google and WWW::Search
-# originally Google.pl, drastically altered.
-
-package W3Search;
-
-use strict;
-use vars qw(@W3Search_engines $W3Search_regex);
-
-@W3Search_engines = qw(AltaVista Dejanews Excite Gopher HotBot Infoseek
-  Lycos Magellan PLweb SFgate Simple Verity Google z);
-$W3Search_regex = join '|', @W3Search_engines;
-
-my $maxshow = 5;
-
-sub W3Search {
-    my ( $where, $what, $type ) = @_;
-    my $retval = "$where can't find \002$what\002";
-    my $Search;
-
-    my @matches = grep { lc($_) eq lc($where) ? $_ : undef } @W3Search_engines;
-    if (@matches) {
-        $where = shift @matches;
-    }
-    else {
-        &::msg( $::who, "i don't know how to check '$where'" );
-        return;
-    }
-
-    return unless &::loadPerlModule("WWW::Search");
-
-    eval { $Search = new WWW::Search( $where, agent_name => 'Mozilla/4.5' ); };
-
-    if ( !defined $Search ) {
-        &::msg( $::who, "$where is invalid search." );
-        return;
-    }
-
-    my $Query = WWW::Search::escape_query($what);
-    $Search->native_query(
-        $Query,
-        {
-            num => 10,
-
-            #          search_debug => 2,
-            #          search_parse_debug => 2,
-        }
-    );
-    $Search->http_proxy( $::param{'httpProxy'} ) if ( &::IsParam('httpProxy') );
-
-    #my $max = $Search->maximum_to_retrieve(10);       # DOES NOT WORK.
-
-    my ( @results, $count, $r );
-    $retval = "$where says \002$what\002 is at ";
-    while ( $r = $Search->next_result() ) {
-        my $url = $r->url();
-        $retval .= ' or ' if ( $count > 0 );
-        $retval .= $url;
-        last if ++$count >= $maxshow;
-    }
-
-    &::performStrictReply($retval);
-}
-
-1;
-
-# vim:ts=4:sw=4:expandtab:tw=80
index 8898bdaebfc03ccac4881933902faf1ab4fad6df..920bf2e10fcc3e2a00dcc4d3e9b2a0dbfff21388 100644 (file)
@@ -14,7 +14,8 @@ package babelfish;
 use strict;
 
 my $no_babelfish;
-my $url = 'http://babelfish.av.com/tr';
+#my $url = 'http://babelfish.av.com/tr';
+my $url = 'http://babelfish.yahoo.com/translate_txt';
 
 BEGIN {
     eval "use URI::Escape";    # utility functions for encoding the
@@ -73,11 +74,8 @@ sub babelfishParam {
 
     my $req = HTTP::Request->new( 'POST', $url );
 
-    # babelfish ignored this, but it SHOULD work
-    # Accept-Charset: iso-8859-1
-    #  $req->header('Accept-Charset' => 'iso-8859-1');
-    #  print $req->header('Accept-Charset');
-    $req->header( 'Accept-Language' => 'en' );
+    $req->header('Accept-Language' => 'en-us');
+    $req->header('Accept-Charset' => 'UTF-8,*');
     $req->content_type('application/x-www-form-urlencoded');
 
     return translate( $phrase, "${from}_${to}", $req, $ua );
@@ -98,27 +96,33 @@ sub translate {
     if ( $res->is_success ) {
         my $html = $res->content;
 
-        # This method subject to change with the whims of Altavista's design
-        # staff.
+        # This method subject to change with the whims of Babelfish design staff.
         ($translated) = $html;
-
-        $translated =~ s/<[^>]*>//sg;
+        # strip page head
+        $translated =~ s/.*<\/head>//sg;
+        # clean before doc-body
+        $translated =~ s/.*<div id="doc-body"[^>]*>//sg;
+        # clean after first form
+        $translated =~ s/<\/form>.*//sg;
+        # convert back to spaces
         $translated =~ s/&nbsp;/ /sg;
+        &::DEBUG("================================\n$translated\n========================\n");
+        # strip up to result
+        $translated =~ s/.*<div id="result">//sg;
+        # strip rest of page
+        $translated =~ s/<\/div.*//sg;
+        # strip all markup
+        $translated =~ s/<[^>]*>/ /sg;
+        # \n to space
+        $translated =~ s/[\n\r\t]/ /g;
+        # strip leading whitespace
+        $translated =~ s/^\s+//sg;
+        # strip trailing whitespace
+        $translated =~ s/\s+$//sg;
+        # strip multiple whitespace
         $translated =~ s/\s+/ /sg;
 
-        #&::DEBUG("$translated\n===remove <attributes>\n");
-
-        $translated =~ s/\s*Translate again.*//i;
-        &::DEBUG("$translated\n===remove after 'Translate again'\n");
-
-        $translated =~ s/[^:]*?:\s*(Help\s*)?//s;
-        &::DEBUG( "len="
-              . length($translated)
-              . " $translated\n===remove to first ':', optional Help\n" );
-
-        $translated =~ s/\n/ /g;
-
-        # FIXME: should we do unicode->iso (no. use utf8!)
+        # FIXME: any entities to utf8?
     }
     else {
         $translated = ":(";    # failure
@@ -148,20 +152,6 @@ sub babelfish {
     return;
 }
 
-if (0) {
-    if ( -t STDIN ) {
-
-#my $result = babelfish::babelfish('en sp hello world');
-#my $result = babelfish::babelfish('en sp The cheese is old and moldy, where is the bathroom?');
-        my $result =
-          babelfish::babelfish(
-            'en gr doesn\'t seem to translate things longer than 40 characters'
-          );
-        $result =~ s/; /\n/g;
-        print "Babelfish says: \"$result\"\n";
-    }
-}
-
 1;
 
 # vim:ts=4:sw=4:expandtab:tw=80
old mode 100755 (executable)
new mode 100644 (file)
index 81c6e0521678451047dada01cfcfd7482a4b1d56..751d298d22f9165f33b4682f6a1faeee8ea816a8 100644 (file)
@@ -126,7 +126,13 @@ sub turnedstr {
     my $turned = '';
     my $tlength = 0;
 
+    # add reverse mappings
+    foreach my $up (keys %updown) {
+        $updown{$updown{$up}} = $up if ! exists $updown{$updown{$up}};
+    }
+
     for my $char ( $str =~ /(\X)/g ) {
+#print STDERR "str=\"$str\" char=\"$char\"\n";
         if ( exists $updown{$char} ) {
             my $t = $updown{$char};
             $t = $missing if !length($t);
@@ -153,6 +159,8 @@ sub upsidedown {
     &::performStrictReply( turnedstr( $message ) );
 }
 
+#binmode(STDERR, "encoding(UTF-8)");
+#print STDERR turnedstr("upsidedown ɟǝpɔqɐabcdef") . "\n";
 1;
 
 # vim:ts=4:sw=4:expandtab:tw=80
diff --git a/src/PoCiCommon.pm b/src/PoCiCommon.pm
new file mode 100644 (file)
index 0000000..058a1fb
--- /dev/null
@@ -0,0 +1,597 @@
+# This is a transitional file
+#package POE::Component::IRC::Common;
+package src::PoCiCommon;
+
+use strict;
+use warnings;
+
+our $VERSION = '5.18';
+
+require Exporter;
+use base qw(Exporter);
+our @EXPORT_OK = qw(
+    u_irc l_irc parse_mode_line parse_ban_mask matches_mask matches_mask_array
+    parse_user irc_ip_get_version irc_ip_is_ipv4 irc_ip_is_ipv6 has_color
+    has_formatting strip_color strip_formatting NORMAL BOLD UNDERLINE REVERSE
+    WHITE BLACK DARK_BLUE DARK_GREEN RED BROWN PURPLE ORANGE YELLOW LIGHT_GREEN
+    TEAL CYAN LIGHT_BLUE MAGENTA DARK_GREY LIGHT_GREY
+);
+our %EXPORT_TAGS = ( ALL => [@EXPORT_OK] );
+
+my ($ERROR, $ERRNO);
+
+use constant {
+    NORMAL      => "\x0f",
+    
+    # formatting
+    BOLD        => "\x02",
+    UNDERLINE   => "\x1f",
+    REVERSE     => "\x16",
+    ITALIC      => "\x1d",
+    FIXED       => "\x11",
+    
+    # mIRC colors
+    WHITE       => "\x0300",
+    BLACK       => "\x0301",
+    DARK_BLUE   => "\x0302",
+    DARK_GREEN  => "\x0303",
+    RED         => "\x0304",
+    BROWN       => "\x0305",
+    PURPLE      => "\x0306",
+    ORANGE      => "\x0307",
+    YELLOW      => "\x0308",
+    LIGHT_GREEN => "\x0309",
+    TEAL        => "\x0310",
+    CYAN        => "\x0311",
+    LIGHT_BLUE  => "\x0312",
+    MAGENTA     => "\x0313",
+    DARK_GREY   => "\x0314",
+    LIGHT_GREY  => "\x0315",
+};
+
+sub u_irc {
+    my $value = shift || return;
+    my $type = shift || 'rfc1459';
+    $type = lc $type;
+
+    if ( $type eq 'ascii' ) {
+        $value =~ tr/a-z/A-Z/;
+    }
+    elsif ( $type eq 'strict-rfc1459' ) {
+        $value =~ tr/a-z{}|/A-Z[]\\/;
+    }
+    else {
+        $value =~ tr/a-z{}|^/A-Z[]\\~/;
+    }
+
+    return $value;
+}
+
+sub l_irc {
+    my $value = shift || return;
+    my $type = shift || 'rfc1459';
+    $type = lc $type;
+
+    if ( $type eq 'ascii' ) {
+        $value =~ tr/A-Z/a-z/;
+    }
+    elsif ( $type eq 'strict-rfc1459' ) {
+        $value =~ tr/A-Z[]\\/a-z{}|/;
+    }
+    else {
+        $value =~ tr/A-Z[]\\~/a-z{}|^/;
+    }
+
+    return $value;
+}
+
+sub parse_mode_line {
+    my @args = @_;
+
+    my $chanmodes = [qw(beI k l imnpstaqr)];
+    my $statmodes = 'ov';
+    my $hashref = { };
+    my $count = 0;
+    
+    while (my $arg = shift @args) {
+        if ( ref $arg eq 'ARRAY' ) {
+           $chanmodes = $arg;
+           next;
+        }
+        elsif ( ref $arg eq 'HASH' ) {
+           $statmodes = join '', keys %{ $arg };
+           next;
+        }
+        elsif ( $arg =~ /^(\+|-)/ or $count == 0 ) {
+            my $action = '+';
+            for my $char ( split (//,$arg) ) {
+                if ($char eq '+' or $char eq '-') {
+                   $action = $char;
+                }
+                else {
+                   push @{ $hashref->{modes} }, $action . $char;
+                }
+                
+                if ($char =~ /[$statmodes$chanmodes->[0]$chanmodes->[1]]/) {
+                    push @{ $hashref->{args} }, shift @args;
+                }
+                
+                if ($action eq '+' && $char =~ /[$chanmodes->[2]]/) {
+                    push @{ $hashref->{args} }, shift @args;
+                }
+            }
+        }
+        else {
+            push @{ $hashref->{args} }, $arg;
+        }
+        $count++;
+    }
+
+    return $hashref;
+}
+
+sub parse_ban_mask {
+    my $arg = shift || return;
+
+    $arg =~ s/\x2a{2,}/\x2a/g;
+    my @ban;
+    my $remainder;
+    if ($arg !~ /\x21/ and $arg =~ /\x40/) {
+        $remainder = $arg;
+    }
+    else {
+        ($ban[0], $remainder) = split /\x21/, $arg, 2;
+    }
+    
+    $remainder =~ s/\x21//g if defined $remainder;
+    @ban[1..2] = split(/\x40/, $remainder, 2) if defined $remainder;
+    $ban[2] =~ s/\x40//g if defined $ban[2];
+    
+    for my $i (1..2) {
+        $ban[$i] = '*' if !$ban[$i];
+    }
+    
+    return $ban[0] . '!' . $ban[1] . '@' . $ban[2];
+}
+
+sub matches_mask_array {
+    my ($masks, $matches, $mapping) = @_;
+    
+    return if !$masks || !$matches;
+    return if ref $masks ne 'ARRAY';
+    return if ref $matches ne 'ARRAY';
+    my $ref = { };
+    
+    for my $mask ( @{ $masks } ) {
+        for my $match ( @{ $matches } ) {
+            if ( matches_mask($mask, $match, $mapping) ) {
+                push @{ $ref->{ $mask } }, $match;
+            }
+        }
+    }
+    
+    return $ref;
+}
+
+sub matches_mask {
+    my ($mask,$match,$mapping) = @_;
+
+    return if !$mask || !$match;
+    $mask = parse_ban_mask($mask);
+    $mask =~ s/\x2A+/\x2A/g;
+
+    my $umask = quotemeta u_irc( $mask, $mapping );
+    $umask =~ s/\\\*/[\x01-\xFF]{0,}/g;
+    $umask =~ s/\\\?/[\x01-\xFF]{1,1}/g;
+    $match = u_irc $match, $mapping;
+
+    return 1 if $match =~ /^$umask$/;
+    return;
+}
+
+sub parse_user {
+    my $user = shift || return;
+    my ($n, $u, $h) = split /[!@]/, $user;
+    return ($n, $u, $h) if wantarray();
+    return $n;
+}
+
+sub has_color {
+    my $string = shift;
+    return 1 if $string =~ /[\x03\x04]/;
+    return;
+}
+
+sub has_formatting {
+    my $string = shift;
+    return 1 if $string =~/[\x02\x1f\x16\x1d\x11]/;
+    return;
+}
+
+sub strip_color {
+    my $string = shift;
+    
+    # mIRC colors
+    $string =~ s/\x03(?:,\d{1,2}|\d{1,2}(?:,\d{1,2})?)?//g;
+    $string =~ s/\x0f//g;
+    
+    # RGB colors supported by some clients
+    $string =~ s/\x04[0-9a-fA-F]{0,6}//ig;
+    
+    return $string;
+}
+
+sub strip_formatting {
+    my $string = shift;
+    $string =~ s/[\x0f\x02\x1f\x16\x1d\x11]//g;
+    return $string;
+}
+
+#------------------------------------------------------------------------------
+# Subroutine ip_get_version
+# Purpose           : Get an IP version
+# Params            : IP address
+# Returns           : 4, 6, 0(don't know)
+sub irc_ip_get_version {
+    my $ip = shift || return;
+
+    # If the address does not contain any ':', maybe it's IPv4
+    return 4 if $ip !~ /:/ && irc_ip_is_ipv4($ip);
+
+    # Is it IPv6 ?
+    return 6 if irc_ip_is_ipv6($ip);
+
+    return;
+}
+
+#------------------------------------------------------------------------------
+# Subroutine ip_is_ipv4
+# Purpose           : Check if an IP address is version 4
+# Params            : IP address
+# Returns           : 1 (yes) or 0 (no)
+sub irc_ip_is_ipv4 {
+    my $ip = shift || return;
+
+    # Check for invalid chars
+    if ($ip !~ /^[\d\.]+$/) {
+        $ERROR = "Invalid chars in IP $ip";
+        $ERRNO = 107;
+        return;
+    }
+
+    if ($ip =~ /^\./) {
+        $ERROR = "Invalid IP $ip - starts with a dot";
+        $ERRNO = 103;
+        return;
+    }
+
+    if ($ip =~ /\.$/) {
+        $ERROR = "Invalid IP $ip - ends with a dot";
+        $ERRNO = 104;
+        return;
+    }
+
+    # Single Numbers are considered to be IPv4
+    return 1 if $ip =~ /^(\d+)$/ && $1 < 256;
+
+    # Count quads
+    my $n = ($ip =~ tr/\./\./);
+
+    # IPv4 must have from 1 to 4 quads
+    if ($n <= 0 || $n > 4) {
+        $ERROR = "Invalid IP address $ip";
+        $ERRNO = 105;
+        return;
+    }
+
+    # Check for empty quads
+    if ($ip =~ /\.\./) {
+        $ERROR = "Empty quad in IP address $ip";
+        $ERRNO = 106;
+        return;
+    }
+
+    for my $quad (split /\./, $ip) {
+        # Check for invalid quads
+        if ($quad < 0 || $quad >= 256) {
+            $ERROR = "Invalid quad in IP address $ip - $_";
+            $ERRNO = 107;
+            return;
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Subroutine ip_is_ipv6
+# Purpose           : Check if an IP address is version 6
+# Params            : IP address
+# Returns           : 1 (yes) or 0 (no)
+sub irc_ip_is_ipv6 {
+    my $ip = shift || return;
+
+    # Count octets
+    my $n = ($ip =~ tr/:/:/);
+    return if ($n <= 0 || $n >= 8);
+
+    # $k is a counter
+    my $k;
+
+    for my $octet (split /:/, $ip) {
+        $k++;
+
+        # Empty octet ?
+        next if $octet eq '';
+
+        # Normal v6 octet ?
+        next if $octet =~ /^[a-f\d]{1,4}$/i;
+
+        # Last octet - is it IPv4 ?
+        if ($k == $n + 1) {
+            next if (ip_is_ipv4($octet));
+        }
+
+        $ERROR = "Invalid IP address $ip";
+        $ERRNO = 108;
+        return;
+    }
+
+    # Does the IP address start with : ?
+    if ($ip =~ m/^:[^:]/) {
+        $ERROR = "Invalid address $ip (starts with :)";
+        $ERRNO = 109;
+        return;
+    }
+
+    # Does the IP address finish with : ?
+    if ($ip =~ m/[^:]:$/) {
+        $ERROR = "Invalid address $ip (ends with :)";
+        $ERRNO = 110;
+        return;
+    }
+
+    # Does the IP address have more than one '::' pattern ?
+    if ($ip =~ s/:(?=:)//g > 1) {
+        $ERROR = "Invalid address $ip (More than one :: pattern)";
+        $ERRNO = 111;
+        return;
+    }
+
+    return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+src::PoCiCommon - provides a set of common functions for the
+infobot suite. Code originally from POE::Component::IRC::Common.
+
+=head1 SYNOPSIS
+
+ use strict;
+ use warnings;
+
+ use src::PoCiCommon qw( :ALL );
+
+ my $nickname = '^Lame|BOT[moo]';
+ my $uppercase_nick = u_irc( $nickname );
+ my $lowercase_nick = l_irc( $nickname );
+
+ my $mode_line = 'ov+b-i Bob sue stalin*!*@*';
+ my $hashref = parse_mode_line( $mode_line );
+
+ my $banmask = 'stalin*';
+ my $full_banmask = parse_ban_mask( $banmask );
+
+ if ( matches_mask( $full_banmask, 'stalin!joe@kremlin.ru' ) ) {
+     print "EEK!";
+ }
+  
+ if ( has_color($message) ) {
+    print 'COLOR CODE ALERT!";
+ }
+
+ my $results_hashref = matches_mask_array( \@masks, \@items_to_match_against );
+
+ my $nick = parse_user( 'stalin!joe@kremlin.ru' );
+ my ($nick, $user, $host) = parse_user( 'stalin!joe@kremlin.ru' );
+
+=head1 DESCRIPTION
+
+src::PoCiCommon provides a set of common functions for the infobot suite.
+Original code from POE::Component::IRC::Common. There are included functions
+for uppercase and lowercase nicknames/channelnames and for parsing mode lines
+and ban masks.
+
+=head1 CONSTANTS
+
+Use the following constants to add formatting and mIRC color codes to IRC
+messages.
+
+Normal text:
+
+ NORMAL
+
+Formatting:
+
+ BOLD
+ UNDERLINE
+ REVERSE
+ ITALIC
+ FIXED
+
+Colors:
+
+ WHITE
+ BLACK
+ DARK_BLUE
+ DARK_GREEN
+ RED
+ BROWN
+ PURPLE
+ ORANGE
+ YELLOW
+ LIGHT_GREEN
+ TEAL
+ CYAN
+ LIGHT_BLUE
+ MAGENTA
+ DARK_GREY
+ LIGHT_GREY
+
+Individual formatting codes can be cancelled with their corresponding constant,
+but you can also cancel all of them at once with C<NORMAL>. To cancel the effect
+of previous color codes, you must use C<NORMAL>. which of course has the side
+effect of cancelling the effect of all previous formatting codes as well.
+
+ $irc->yield('This word is ' . YELLOW . 'yellow' . NORMAL
+     . ' while this word is ' . BOLD . 'bold' . BOLD);
+
+ $irc->yield(UNDERLINE . BOLD . 'This sentence is both underlined and bold.'
+     . NORMAL);
+
+
+
+=head1 FUNCTIONS
+
+=head2 C<u_irc>
+
+Takes one mandatory parameter, a string to convert to IRC uppercase, and one
+optional parameter, the casemapping of the ircd ( which can be 'rfc1459',
+'strict-rfc1459' or 'ascii'. Default is 'rfc1459' ). Returns the IRC uppercase
+equivalent of the passed string.
+
+=head2 C<l_irc>
+
+Takes one mandatory parameter, a string to convert to IRC lowercase, and one
+optional parameter, the casemapping of the ircd ( which can be 'rfc1459',
+'strict-rfc1459' or 'ascii'. Default is 'rfc1459' ). Returns the IRC lowercase
+equivalent of the passed string.
+
+=head2 C<parse_mode_line>
+
+Takes a list representing an IRC mode line. Returns a hashref. If the modeline
+couldn't be parsed the hashref will be empty. On success the following keys
+will be available in the hashref:
+
+ 'modes', an arrayref of normalised modes;
+ 'args', an arrayref of applicable arguments to the modes;
+
+Example:
+
+ my $hashref = parse_mode_line( 'ov+b-i', 'Bob', 'sue', 'stalin*!*@*' );
+
+ # $hashref will be:
+ {
+    modes => [ '+o', '+v', '+b', '-i' ],
+    args  => [ 'Bob', 'sue', 'stalin*!*@*' ],
+ }
+
+=head2 C<parse_ban_mask>
+
+Takes one parameter, a string representing an IRC ban mask. Returns a
+normalised full banmask.
+
+Example:
+
+ $fullbanmask = parse_ban_mask( 'stalin*' );
+
+ # $fullbanmask will be: 'stalin*!*@*';
+
+=head2 C<matches_mask>
+
+Takes two parameters, a string representing an IRC mask ( it'll be processed
+with parse_ban_mask() to ensure that it is normalised ) and something to match
+against the IRC mask, such as a nick!user@hostname string. Returns a true
+value if they match, a false value otherwise. Optionally, one may pass the
+casemapping (see L<C<u_irc()>|/"u_irc">), as this function uses C<u_irc()>
+internally.
+
+=head2 C<matches_mask_array>
+
+Takes two array references, the first being a list of strings representing
+IRC masks, the second a list of somethings to test against the masks. Returns
+an empty hashref if there are no matches. Otherwise, the keys will be the
+masks matched, each value being an arrayref of the strings that matched it.
+Optionally, one may pass the casemapping (see L<C<u_irc()>|/"u_irc">), as
+this function uses C<u_irc()> internally.
+
+=head2 C<parse_user>
+
+Takes one parameter, a string representing a user in the form
+nick!user@hostname. In a scalar context it returns just the nickname.
+In a list context it returns a list consisting of the nick, user and hostname,
+respectively.
+
+=head2 C<has_color>
+
+Takes one parameter, a string of IRC text. Returns 1 if it contains any IRC
+color codes, 0 otherwise. Useful if you want your bot to kick users for
+(ab)using colors. :)
+
+=head2 C<has_formatting>
+
+Takes one parameter, a string of IRC text. Returns 1 if it contains any IRC
+formatting codes, 0 otherwise.
+
+=head2 C<strip_color>
+
+Takes one paramter, a string of IRC text. Returns the string stripped of all
+IRC color codes. Due to the fact that both color and formatting codes can
+be cancelled with the same character, this might strip more than you hoped for
+if the string contains both color and formatting codes. Stripping both will
+always do what you expect it to.
+
+=head2 C<strip_formatting>
+
+Takes one paramter, a string of IRC text. Returns the string stripped of all
+IRC formatting codes. Due to the fact that both color and formatting codes can
+be cancelled with the same character, this might strip more than you hoped for
+if the string contains both color and formatting codes. Stripping both will
+always do what you expect it to.
+
+=head2 C<irc_ip_get_version>
+
+Try to guess the IP version of an IP address.
+
+Params: IP address
+Returns: 4, 6, 0(unable to determine)
+
+C<$version = ip_get_version ($ip)>
+
+=head2 C<irc_ip_is_ipv4>
+
+Check if an IP address is of type 4.
+
+Params: IP address
+Returns: 1 (yes) or 0 (no)
+
+C<ip_is_ipv4($ip) and print "$ip is IPv4";>
+
+=head2 C<irc_ip_is_ipv6>
+
+Check if an IP address is of type 6.
+
+Params: IP address
+Returns: 1 (yes) or 0 (no)
+
+ ip_is_ipv6($ip) && print "$ip is IPv6";
+
+=head1 AUTHOR
+
+Dan 'troubled' McGrath
+
+This whole module is shamelessly 'borrowed' from L<POE::Component::IRC::Common>
+by Chris 'BinGOs' Williams
+
+=head1 SEE ALSO
+
+L<POE::Component::IRC::Common>
+
+L<POE::Component::IRC|POE::Component::IRC>
+
+L<Net::IP|Net::IP>
+
+=cut
index 91b3f9d4aad286176a121fe31148420e9eb3f764..21c2a69b88def607c7873a32766c52ed7d5a9de8 100644 (file)
@@ -26,8 +26,9 @@ sub process {
     $learnok = 1 if ($addressed);
     if ( $param{'learn'} =~ /^HUNGRY$/i ) {
         $learnok  = 1;
-        $addrchar = 1;
-        $talkok   = 1;
+        #FIXME: why can we talk if we just want to learn?
+        #$addrchar = 1;
+        #$talkok   = 1;
     }
 
     &shmFlush();                     # hack.
@@ -274,7 +275,7 @@ sub process {
     }
 
     # greetings.
-    if ( $message =~ /how (the hell )?are (ya|you)( doin\'?g?)?\?*$/ ) {
+    if ( $message =~ /how (the hell )?are (ya|you)( doin\'?g?)?\?*$/ && $talkok ) {
 
         &performReply( &getRandom( keys %{ $lang{'howareyou'} } ) );
         return;
@@ -349,7 +350,8 @@ sub process {
 
         # allow administration of bot via messages (default is DCC CHAT only)
         if ( &IsFlag('A') ) {
-            &loadMyModule('UserDCC');
+            # UserDCC.pl should autoload now from IRC/. Remove if desired
+            #&loadMyModule('UserDCC');
             $er = &userDCC();
             if ( !defined $er ) {
                 return 'SOMETHING 2';
index f100fd6a309dea6b1d9f9f468ad00e401b5d8d80..37de33491e1e850abc4de4b2522feb18fcc5684b 100644 (file)
@@ -7,6 +7,7 @@
 #
 
 use strict;
+use utf8;
 
 use vars qw($statcount $bot_pid $forkedtime $statcountfix $addressed);
 use vars qw($logDate $logold $logcount $logtime $logrepeat $running);
@@ -91,6 +92,7 @@ sub cl {
 
 # logging support.
 sub openLog {
+    binmode( STDOUT, ':encoding(UTF-8)' );
     return unless ( &IsParam('logfile') );
     $file{log} = $param{'logfile'};