--- bot.v2.2.1.txt	Wed Jul 16 07:22:02 2003
+++ bot.v2.2.2.schmolli.txt	Wed Jul 16 09:16:27 2003
@@ -95,34 +95,56 @@
 use strict;
 use IO::Socket;
 use Data::Dumper;
-
-my $TEST_MODE = 0; # connect to another test server; see lower block
+use Getopt::Long;
 
 my $version = "2.2.1";
+(my $prog = $0) =~ s/^.*\///;
 
-my $server = "area51.slashnet.org:6667";
-my $botnick = "b0t"; # nick
-my $botuser = "bot"; # username
-my $botrlnm = "http://www.slashnet.org/~b0t/"; # real name
-my $botchan = "#g7"; # game channel
-my $botidentify = "identify g7_rocks!!"; # identify to services
-my $botopcmd = "PRIVMSG #g7 :opz plzkthx!!1 :)))"; # op self in channel
-my $helpurl = "http://jotun.ultrazone.org/g7/"; # URL sent for help
-my $admincommurl = "http://jotun.ultrazone.org/g7/admincomms.txt";
-   # ^-- URL for admin help
-my @access = ('yawnwraith','jotun','drdink');
-   # ^-- usernames with admin access
-
+my %opts = (
+	'server' => 'area51.slashnet.org:6667',
+	'botnick' => 'b0t',
+	'botuser' => 'bot',
+	'botrlnm' => 'http://www.slashnet.org/~b0t/',
+	'botchan' => '#g7',
+	'botpw' => 'g7_rocks!!',
+	'botopcmd' => 'opz plzkthx!!1 :)))',
+	'helpurl' => 'http://jotun.ultrazone.org/g7/',
+	'admincommurl' => 'http://jotun.ultrazone.org/g7/admincomms.txt',
+	'access' => [ 'yawnwraith', 'jotun', 'drdink' ],
+	'rpstep' => 1.16,
+	'rpbase' => 600,
+	'rppenstep' => 1.14,
+	'dbfile' => 'irpg.db',
+);
+
+GetOptions(\%opts,
+	"help|h",
+	"verbose|v",
+	"debug",
+	"server|s=s",
+	"botnick|n=s",
+	"botuser|u=s",
+	"botrlnm|r=s",
+	"botchan|c=s",
+	"botpw|p=s",
+	"botopcmd|o=s",
+	"helpurl=s",
+	"admincommurl=s",
+	"access=s@",
+	"rpstep=f",
+	"rpbase=i",
+	"rppenstep=f",
+	"dbfile|irpgdb|db|d=s",
+) or die "error parsing command line\n";
+
+$opts{'help'} and do { help(); exit 0; };
+my $debug = $opts{'debug'} || 0;
+my $v = $opts{'verbose'} || $debug;
 
 my $outbytes = 0; # sent bytes
 my $inbytes = 0; # received bytes
-my $debug = 0; # print debug yes/no
 my %onchan; # users on game channel
 my %rps; # role-players
-my $irpgdb = "irpg.db"; # database
-my $rpstep = 1.16; # NEXT_LEVEL=(RPBASE*(RPSTEP**CURRENT_LEVEL))
-my $rpbase = 600; # NEXT_LEVEL=(RPBASE*(RPSTEP**CURRENT_LEVEL))
-my $rppenstep = 1.14; # PENALTY_SECS=(PENALTY*(RPPENSTEP**CURRENT_LEVEL))
 my $rpreport = 0; # constant for reporting top players
 my $alrmint = 5; # secs between database rewrites
 my @prev_online; # user@hosts online on restart, die
@@ -130,28 +152,23 @@
 
 $SIG{'HUP'} = 0; # ignore sighup
 
-if ($TEST_MODE) {
-  $server = "user-33qt3ul.dialup.mindspring.com:7800";
-  $rpstep = 0; # 0-second levels
-  $rpbase = 0; # 0-second levels
-}
-
 CONNECT: # cheese.
 loaddb();
-my $sock = IO::Socket::INET->new(PeerAddr=>$server,PeerPort=>6667);
+my $sock = IO::Socket::INET->new(PeerAddr=>$opts{'server'},PeerPort=>6667);
 die "Could not build socket; $!" unless $sock;
 
-sts("NICK $botnick");
-sts("USER $botuser 0 0 :$botrlnm");
+sts("NICK $opts{botnick}");
+sts("USER $opts{botuser} 0 0 :$opts{botrlnm}");
 
 while (<$sock>) {
   $inbytes += length;
   s/[\r\n]//g;
+  print "<- $_\n" if $debug;
   my @arg = split/ /;
   if (lc $arg[0] eq 'ping') { sts("PONG $arg[1]"); }
   if ($arg[1] eq '433') {
-    $botnick .= 0;
-    sts("NICK $botnick");
+    $opts{'botnick'} .= 0;
+    sts("NICK $opts{botnick}");
   }
   if (lc $arg[1] eq 'join') {
     my $usernick = (split(/!/,$arg[0]))[0];
@@ -165,7 +182,7 @@
       if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick
       && $rps{$k}{online}) {
         $rps{$k}{online}=0;
-        $rps{$k}{next}+=int(20 * ($rppenstep**$rps{$k}{level}));
+        $rps{$k}{next}+=int(20 * ($opts{'rppenstep'}**$rps{$k}{level}));
       }
     }
     delete $onchan{$usernick};
@@ -177,9 +194,9 @@
       if ($rps{$k}{nick} eq $usernick && $rps{$k}{online}) {
         $rps{$k}{nick}=substr($arg[2],1);
         $rps{$k}{userhost}=substr($arg[0],1);
-        $rps{$k}{next} += int(30 * ($rppenstep**$rps{$k}{level}));
+        $rps{$k}{next} += int(30 * ($opts{'rppenstep'}**$rps{$k}{level}));
         sts("NOTICE $rps{$k}{nick} :Penalty of ".
-        duration(int(30 * ($rppenstep**$rps{$k}{level}))).
+        duration(int(30 * ($opts{'rppenstep'}**$rps{$k}{level}))).
         " added to your timer for changing nicks.");
       }
     }
@@ -192,9 +209,9 @@
       if (exists $rps{$k}{nick} &&
       $rps{$k}{nick} eq $usernick && $rps{$k}{online}) {
         $rps{$k}{online}=0;
-        $rps{$k}{next}+=int(200 * ($rppenstep**$rps{$k}{level}));
+        $rps{$k}{next}+=int(200 * ($opts{'rppenstep'}**$rps{$k}{level}));
         sts("NOTICE $rps{$k}{nick} :Penalty of ".
-        duration(int(200 * ($rppenstep**$rps{$k}{level}))).
+        duration(int(200 * ($opts{'rppenstep'}**$rps{$k}{level}))).
         " added to your timer for parting.");
       }
     }
@@ -206,9 +223,9 @@
       if (exists $rps{$k}{nick} &&
       $rps{$k}{nick} eq $usernick && $rps{$k}{online}) {
         $rps{$k}{online}=0;
-        $rps{$k}{next}+=int(250 * ($rppenstep**$rps{$k}{level}));
+        $rps{$k}{next}+=int(250 * ($opts{'rppenstep'}**$rps{$k}{level}));
         sts("NOTICE $rps{$k}{nick} :Penalty of ".
-        duration(int(250 * ($rppenstep**$rps{$k}{level}))).
+        duration(int(250 * ($opts{'rppenstep'}**$rps{$k}{level}))).
         " added to your timer for getting kicked.");
       }
     }
@@ -216,11 +233,12 @@
   }
   if (lc $arg[1] eq '315') {
     if (@auto_login) {
-      sts("PRIVMSG $botchan :".scalar @auto_login." users matching ".
+      chanmsg(scalar @auto_login . " users matching " .
           scalar @prev_online." hosts automatically logged in; accounts: ".
           join(", ",@auto_login));
+    } else {
+      chanmsg("0 users qualified for auto login.");
     }
-    else { sts("PRIVMSG $botchan :0 users qualified for auto login."); }
     undef @prev_online;
     undef @auto_login;
   }
@@ -240,11 +258,11 @@
     }
   }
   if ($arg[1] eq '001') {
-    sts($botidentify);
-    sts("JOIN $botchan");
-    sts("MODE $botchan");
-    sts($botopcmd);
-    sts("WHO $botchan");
+    sts("identify $opts{botpw}");
+    sts("JOIN $opts{botchan}");
+    sts("MODE $opts{botchan}");
+    chanmsg($opts{'botopcmd'});
+    sts("WHO $opts{botchan}");
     $SIG{ALRM} = \&rpcheck;
     alarm(5);
   }
@@ -255,10 +273,10 @@
       if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick
       && $rps{$k}{online}) {
         $rps{$k}{next} += int((length("@arg[3..$#arg]")-1) *
-        ($rppenstep**$rps{$k}{level}));
+        ($opts{'rppenstep'}**$rps{$k}{level}));
         sts("NOTICE $rps{$k}{nick} :Penalty of ".
         duration(int((length("@arg[3..$#arg]")-1) *
-        ($rppenstep**$rps{$k}{level}))).
+        ($opts{'rppenstep'}**$rps{$k}{level}))).
         " added to your timer for notice.");
       }
     }
@@ -266,125 +284,126 @@
   if (lc $arg[1] eq 'privmsg') {
     $arg[0] = substr($arg[0],1);
     my $usernick = (split/!/,$arg[0])[0];
-    if (lc $arg[2] eq lc $botnick) {
+    if (lc $arg[2] eq lc $opts{'botnick'}) {
       if (lc $arg[3] eq ":\1version\1") {
         sts("NOTICE $usernick :\1VERSION IRPG bot v$version by jotun; ".
-            "$helpurl\1");
+            "$opts{helpurl}\1");
       }
       if (lc $arg[3] eq ":calc" && ha($usernick)) {
         my $eq = "@arg[4..$#arg]";
         $eq =~ s/[^\d\.\+\-\(\)\*\&\^\%\~\!\/]//g;
-        sts("PRIVMSG $botchan :$_") for eval $eq;
+        chanmsg($_) for eval $eq;
         if ($@) {
-          sts("PRIVMSG $usernick :EVAL ERROR   : $@");
-          sts("PRIVMSG $usernick :In expression: $eq");
+          privmsg("EVAL ERROR   : $@", $usernick);
+          privmsg("In expression: $eq", $usernick);
         }
         next;
       }
       if (lc $arg[3] eq ":peval") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to PEVAL.");
+          privmsg("You don't have access to PEVAL.", $usernick);
         }
         else {
-          sts("PRIVMSG $usernick :$_") for eval "@arg[4..$#arg]";
-          sts("PRIVMSG $usernick :EVAL ERROR: $@") if $@;
+          privmsg($_, $usernick) for eval "@arg[4..$#arg]";
+          privmsg("EVAL ERROR: $@", $usernick) if $@;
           next;
         }
       }
       if (lc $arg[3] eq ":register") {
         if ($#arg < 6 || $arg[6] eq "") {
-          sts("PRIVMSG $usernick :Try: REGISTER <char name> <password> <class>");
-          sts("PRIVMSG $usernick :IE : REGISTER Poseidon MyPassword ".
-          "God of the Sea");
+          privmsg("Try: REGISTER <char name> <password> <class>", $usernick);
+          privmsg("IE : REGISTER Poseidon MyPassword ".
+          "God of the Sea", $usernick);
         }
         elsif (exists $rps{$arg[4]}) {
-          sts("PRIVMSG $usernick :Sorry, that charname is already in use.");
+          privmsg("Sorry, that charname is already in use.", $usernick);
         }
         elsif (!exists $onchan{$usernick}) {
-          sts("PRIVMSG $usernick :Sorry, you're not in $botchan.");
+          privmsg("Sorry, you're not in $opts{botchan}.", $usernick);
         }
         elsif (length $arg[4] > 16) {
-          sts("PRIVMSG $usernick :Sorry, charnames must be < 17 chars long.");
+          privmsg("Sorry, charnames must be < 17 chars long.", $usernick);
         }
         elsif (length "@arg[6..$#arg]" > 30) {
-          sts("PRIVMSG $usernick :Sorry, char classes must be < 31 chars long.");
+          privmsg("Sorry, char classes must be < 31 chars long.", $usernick);
         }
         else {
-          $rps{$arg[4]}{next} = $rpbase;
+          $rps{$arg[4]}{next} = $opts{'rpbase'};
           $rps{$arg[4]}{class} = "@arg[6..$#arg]";
           $rps{$arg[4]}{level} = 0;
           $rps{$arg[4]}{online} = 1;
           $rps{$arg[4]}{nick} = $usernick;
           $rps{$arg[4]}{userhost} = $arg[0];
           $rps{$arg[4]}{lastlogin} = localtime(time());
-          $rps{$arg[4]}{pass} = crypt($arg[5],"rp");
-          sts("PRIVMSG $botchan :Welcome $usernick"."'s new player $arg[4], ".
-          "the @arg[6..$#arg]! Next level in ".duration($rpbase).".");
-          sts("PRIVMSG $usernick :Success! Account $arg[4] created. ".
-          "You have $rpbase seconds idleness until you reach level 1. ");
-          sts("PRIVMSG $usernick :NOTE: The point of the game is to see who ".
-          "can idle the longest. As such, talking (to channel or the ".
-          "bot), parting, quitting, and changing nicks penalize you.");
+          $rps{$arg[4]}{pass} = crypt($arg[5],mksalt());
+          chanmsg("Welcome $usernick"."'s new player $arg[4], the " .
+            "@arg[6..$#arg]! Next level in ".duration($opts{'rpbase'}).".");
+          privmsg("Success! Account $arg[4] created. You have $opts{rpbase} " .
+            "seconds idleness until you reach level 1. ", $usernick);
+          privmsg("NOTE: The point of the game is to see who can idle the " .
+            "longest. As such, talking (to channel or the bot), parting, ".
+            "quitting, and changing nicks penalize you.", $usernick);
           next;
         }
       }
       if (lc $arg[3] eq ":del") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to del.");
+          privmsg("You don't have access to del.", $usernick);
         }
         else {
           if (!defined $arg[4]) {
-            sts("PRIVMSG $usernick :Try: DEL <char name>");
+            privmsg("Try: DEL <char name>", $usernick);
           }
           else {
             if (exists $rps{$arg[4]}) {
               delete $rps{$arg[4]};
-              sts("PRIVMSG $botchan :Account $arg[4] removed by $arg[0].");
+              chanmsg("Account $arg[4] removed by $arg[0].");
+            } else {
+              privmsg("No such account $arg[4].", $usernick);
             }
-            else { sts("PRIVMSG $usernick :No such account $arg[4]."); }
           }
           next;
         }
       }
       if (lc $arg[3] eq ":alert") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to ALERT.");
+          privmsg("You don't have access to ALERT.", $usernick);
         }
         else {
           if (!defined $arg[4]) {
-            sts("PRIVMSG $usernick :Try: ALERT <message>");
+            privmsg("Try: ALERT <message>", $usernick);
           }
           else {
-            sts("PRIVMSG $botchan :ALERT from $usernick: @arg[4..$#arg]");
+            chanmsg("ALERT from $usernick: @arg[4..$#arg]");
           }
           next;
         }
       }
       if (lc $arg[3] eq ":hog") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to HOG.");
+          privmsg("You don't have access to HOG.", $usernick);
         }
         else {
-          sts("PRIVMSG $botchan :$usernick has summoned the Hand of God.");
+          chanmsg("$usernick has summoned the Hand of God.");
           hog();
           next;
         }
       }
       if (lc $arg[3] eq ":chpass") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to CHPASS.");
+          privmsg("You don't have access to CHPASS.", $usernick);
         }
         else {
           if (!defined $arg[5]) {
-            sts("PRIVMSG $usernick :Try: CHPASS <char name> <new pass>");
+            privmsg("Try: CHPASS <char name> <new pass>", $usernick);
           }
           else {
             if (exists $rps{$arg[4]}) {
-              $rps{$arg[4]}{pass} = crypt($arg[5],"rp");
-              sts("PRIVMSG $usernick :Password for $arg[4] changed.");
+              $rps{$arg[4]}{pass} = crypt($arg[5],mksalt());
+              privmsg("Password for $arg[4] changed.", $usernick);
             }
             else {
-              sts("PRIVMSG $usernick :No such username $arg[4].");
+              privmsg("No such username $arg[4].", $usernick);
             }
           }
           next;
@@ -392,63 +411,65 @@
       }
       if (lc $arg[3] eq ":chuser") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to CHUSER.");
+          privmsg("You don't have access to CHUSER.", $usernick);
         }
         else {
           if (!defined $arg[5]) {
-            sts("PRIVMSG $usernick :Try: CHUSER <char name> <new char name>");
+            privmsg("Try: CHUSER <char name> <new char name>", $usernick);
           }
           elsif (!exists $rps{$arg[4]}) {
-            sts("PRIVMSG $usernick :No such username $arg[4].");
+            privmsg("No such username $arg[4].", $usernick);
           }
           elsif (exists $rps{$arg[5]}) {
-            sts("PRIVMSG $usernick :Username $arg[5] is already taken.");
+            privmsg("Username $arg[5] is already taken.", $usernick);
           }
           else {
             $rps{$arg[5]} = delete $rps{$arg[4]};
-            sts("PRIVMSG $usernick :Username for $arg[4] changed to $arg[5].");
+            privmsg("Username for $arg[4] changed to $arg[5].", $usernick);
           }
           next;
         }
       }
       if (lc $arg[3] eq ":chclass") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to CHCLASS.");
+          privmsg("You don't have access to CHCLASS.", $usernick);
         }
         else {
           if (!defined $arg[5]) {
-            sts("PRIVMSG $usernick :Try: CHCLASS <char name> <new char class>");
+            privmsg("Try: CHCLASS <char name> <new char class>", $usernick);
           }
           else {
             if (exists $rps{$arg[4]}) {
               $rps{$arg[4]}{class} = "@arg[5..$#arg]";
-              sts("PRIVMSG $usernick :Class for $arg[4] changed to @arg[5..$#arg].");
+              privmsg("Class for $arg[4] changed to @arg[5..$#arg].", $usernick);
+            } else {
+              privmsg("No such username $arg[4].", $usernick);
             }
-            else { sts("PRIVMSG $usernick :No such username $arg[4]."); }
           }
           next;
         }
       }
       if (lc $arg[3] eq ":push") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You don't have access to PUSH.");
+          privmsg("You don't have access to PUSH.", $usernick);
         }
         else {
           if (!defined $arg[5]) {
-            sts("PRIVMSG $usernick :Try: PUSH <char name> <seconds>");
+            privmsg("Try: PUSH <char name> <seconds>", $usernick);
           }
           else {
             if (exists $rps{$arg[4]}) {
               $rps{$arg[4]}{next} -= $arg[5];
-              sts("PRIVMSG $usernick :Time to level for $arg[4] pushed ahead ".
+              privmsg("Time to level for $arg[4] pushed ahead ".
                   "$arg[5] seconds. $arg[4] reaches next level in ".
-                  duration($rps{$arg[4]}{next}).".");
-              sts("PRIVMSG $botchan :$usernick has pushed $arg[4] $arg[5] ".
+                  duration($rps{$arg[4]}{next}).".", $usernick);
+              chanmsg("$usernick has pushed $arg[4] $arg[5] ".
                   "seconds toward level ".($rps{$arg[4]}{level}+1).
                   ". $arg[4] reaches next level in ".
                   duration($rps{$arg[4]}{next}).".");
+            } else {
+              privmsg("No such username $arg[4].", $usernick);
             }
-            else { sts("PRIVMSG $usernick :No such username $arg[4]."); }
           }
           next;
         }
@@ -458,32 +479,32 @@
         for (keys %rps) {
           if (exists $rps{$_}{nick} && $rps{$_}{nick} eq $usernick
               && $rps{$_}{online}) {
-            sts("PRIVMSG $usernick :You are logged in as $_.");
+            privmsg("You are logged in as $_.", $usernick);
             $f=1;
           }
         }
-        sts("PRIVMSG $usernick :You are not logged in.") if !$f;
+        privmsg("You are not logged in.", $usernick) if !$f;
         next;
       }
       if (lc $arg[3] eq ":help") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :To register a new account: ".
-          "/msg $botnick REGISTER");
-          sts("PRIVMSG $usernick :To login to an account: ".
-          "/msg $botnick LOGIN");
-          sts("PRIVMSG $usernick :If you forget your password, ask for help ".
-          "in the channel.");
-          sts("PRIVMSG $usernick :For more info, see $helpurl");
+          privmsg("To register a new account: ".
+            "/msg $opts{botnick} REGISTER", $usernick);
+          privmsg("To login to an account: ".
+            "/msg $opts{botnick} LOGIN", $usernick);
+          privmsg("If you forget your password, ask for help ".
+            "in the channel.", $usernick);
+          privmsg("For more info, see $opts{helpurl}", $usernick);
         }
         else {
-          sts("PRIVMSG $usernick :Help URL is $helpurl");
-          sts("PRIVMSG $usernick :Admin commands URL is $admincommurl");
+          privmsg("Help URL is $opts{helpurl}", $usernick);
+          privmsg("Admin commands URL is $opts{admincommurl}", $usernick);
         }
         next;
       }
       if (lc $arg[3] eq ":die") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You do not have access to DIE.");
+          privmsg("You do not have access to DIE.", $usernick);
         }
         else {
           sts("QUIT :DIE from $arg[0]");
@@ -492,14 +513,14 @@
       }
       if (lc $arg[3] eq ":jump") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You do not have access to JUMP.");
+          privmsg("You do not have access to JUMP.", $usernick);
         }
         elsif (!defined $arg[4]) {
-          sts("PRIVMSG $usernick :Try JUMP <server[:port]>");
+          privmsg("Try JUMP <server[:port]>", $usernick);
         }
         else {
           sts("QUIT :JUMP to $arg[4] from $arg[0]");
-          $server = $arg[4];
+          $opts{'server'} = $arg[4];
           close $sock;
           sleep 3;
           goto CONNECT;
@@ -507,7 +528,7 @@
       }
       if (lc $arg[3] eq ":restart") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You do not have access to RESTART.");
+          privmsg("You do not have access to RESTART.", $usernick);
         }
         else {
           sts("QUIT :RESTART from $arg[0]");
@@ -516,41 +537,41 @@
       }
       if (lc $arg[3] eq ":info") {
         if (!ha($usernick)) {
-          sts("PRIVMSG $usernick :You do not have access to INFO.");
+          privmsg("You do not have access to INFO.", $usernick);
         }
         else {
           my $info = sprintf("%.2fkb sent, %.2fkb received in %s. ".
           "%d IRPG users online.",
           $outbytes/1024,$inbytes/1024,duration(time-$^T),
           scalar(grep { $rps{$_}{online} } keys %rps));
-          sts("PRIVMSG $usernick :$info");
+          privmsg($info, $usernick);
           next;
         }
       }
       if (lc $arg[3] eq ":login") {
         if ($#arg < 5 || $arg[5] eq "") {
-          sts("PRIVMSG $usernick :Try: LOGIN <username> <password>");
+          privmsg("Try: LOGIN <username> <password>", $usernick);
         }
         elsif (!exists $rps{$arg[4]}) {
-          sts("PRIVMSG $usernick :Sorry, no such account name. Note ".
-          "that account names are case sensitive.");
+          privmsg("Sorry, no such account name. Note ".
+          "that account names are case sensitive.", $usernick);
         }
         elsif (!exists $onchan{$usernick}) {
-          sts("PRIVMSG $usernick :Sorry, you're not in $botchan.");
+          privmsg("Sorry, you're not in $opts{botchan}.", $usernick);
         }
-        elsif ($rps{$arg[4]}{pass} ne crypt($arg[5],"rp")) {
-          sts("PRIVMSG $usernick :Wrong password.");
+        elsif ($rps{$arg[4]}{pass} ne crypt($arg[5],$rps{$arg[4]}{pass})) {
+          privmsg("Wrong password.", $usernick);
         }
         else {
           $rps{$arg[4]}{online} = 1;
           $rps{$arg[4]}{nick} = $usernick;
           $rps{$arg[4]}{userhost} = $arg[0];
           $rps{$arg[4]}{lastlogin} = localtime(time());
-          sts("PRIVMSG $botchan :$arg[4], the level $rps{$arg[4]}{level} ".
-          "$rps{$arg[4]}{class}, is now online from nickname $usernick. ".
-          "Next level in ".duration($rps{$arg[4]}{next}).".");
-          sts("PRIVMSG $usernick :Logon successful. Next level in ".
-          duration($rps{$arg[4]}{next}).".");
+          chanmsg("$arg[4], the level $rps{$arg[4]}{level} ".
+            "$rps{$arg[4]}{class}, is now online from nickname $usernick. ".
+            "Next level in ".duration($rps{$arg[4]}{next}).".");
+          privmsg("Logon successful. Next level in ".
+            duration($rps{$arg[4]}{next}).".", $usernick);
         }
         next;
       }
@@ -560,28 +581,27 @@
       if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick
       && $rps{$k}{online}) {
         $rps{$k}{next} += int((length("@arg[3..$#arg]")-1) *
-        ($rppenstep**$rps{$k}{level}));
+        ($opts{'rppenstep'}**$rps{$k}{level}));
         sts("NOTICE $rps{$k}{nick} :Penalty of ".
         duration(int((length("@arg[3..$#arg]")-1) *
-        ($rppenstep**$rps{$k}{level}))).
+        ($opts{'rppenstep'}**$rps{$k}{level}))).
         " added to your timer for privmsg.");
         $found=1;
       }
     }
     if (!$found && "@arg" =~ /http:/i && (time()-$onchan{$usernick}) < 90 &&
         "@arg" !~ /ultrazone/i) {
-      sts("MODE $botchan +b $arg[0]");
+      sts("MODE $opts{botchan} +b $arg[0]");
     }
   }
-  print "in : $_\n" if $debug;
 }
 
-print "Disconnected.\r\n" if $debug;
+print "Disconnected.\n" if $v || $debug;
 
 sub sts { # send to server
   my $text = shift;
   print $sock "$text\r\n";
-  print "out: $text\n" if $debug;
+  print "-> $text\n" if $debug;
   $outbytes += length($text) + 2;
 }
 
@@ -589,7 +609,7 @@
   my $nick = shift;
   for my $k (keys %rps) {
     if ($rps{$k}{nick} eq $nick && $rps{$k}{online}) {
-      for my $l (@access) { return 1 if $l eq $k; }
+      for my $l (@{$opts{'access'}}) { return 1 if $l eq $k; }
     }
   }
   return 0;
@@ -613,19 +633,18 @@
   my $win = int(rand(5));
   my $time = int(((5 + int(rand(70)))/100) * $rps{$player}{next});
   if ($win) {
-    sts("PRIVMSG $botchan :Verily I say unto thee, the Heavens have burst ".
-    "forth, and the blessed hand of God carried $player ".duration($time).
-    " toward level ".($rps{$player}{level}+1).".");
+    chanmsg("Verily I say unto thee, the Heavens have burst ".
+      "forth, and the blessed hand of God carried $player ".duration($time).
+      " toward level ".($rps{$player}{level}+1).".");
     $rps{$player}{next} -= $time;
   }
   else {
-    sts("PRIVMSG $botchan :Thereupon He stretched out His little finger ".
-    "among them and consumed $player with fire, slowing the heathen ".
-    duration($time)." from level ".($rps{$player}{level}+1).".");
+    chanmsg("Thereupon He stretched out His little finger ".
+      "among them and consumed $player with fire, slowing the heathen ".
+      duration($time)." from level ".($rps{$player}{level}+1).".");
     $rps{$player}{next} += $time;
   }
-  sts("PRIVMSG $botchan :$player reaches next level in ".
-  duration($rps{$player}{next}).".");
+  chanmsg("$player reaches next level in ". duration($rps{$player}{next}).".");
 }
 
 sub rpcheck { # check levels, update database
@@ -634,19 +653,16 @@
   if ($rpreport%4320==0) { # 4320 = six hours, if $alrmint is 5 seconds
     my @u = sort { $rps{$b}{level} <=> $rps{$a}{level} ||
     $rps{$a}{next}  <=> $rps{$b}{next} } keys %rps;
-    sts("PRIVMSG $botchan :#G7 Idle RPG Top Players:");
-    sts("PRIVMSG $botchan :$u[0], the level $rps{$u[0]}{level} ".
-    "$rps{$u[0]}{class}, is #1! Next level in ".
-    (duration($rps{$u[0]}{next})).".");
-    sts("PRIVMSG $botchan :$u[1], the level $rps{$u[1]}{level} ".
-    "$rps{$u[1]}{class}, is #2! Next level in ".
-    (duration($rps{$u[1]}{next})).".");
-    sts("PRIVMSG $botchan :$u[2], the level $rps{$u[2]}{level} ".
-    "$rps{$u[2]}{class}, is #3! Next level in ".
-    (duration($rps{$u[2]}{next})).".");
-    system("cp $irpgdb .dbbackup/$irpgdb".time());
+    chanmsg("#G7 Idle RPG Top Players:");
+	 foreach my $i (0..2) {
+       $#u >= $i and
+        chanmsg("$u[$i], the level $rps{$u[$i]}{level} ".
+          "$rps{$u[$i]}{class}, is #" . ($i + 1) . "! Next level in ".
+          (duration($rps{$u[$i]}{next})).".");
+    }
+    system("cp $opts{dbfile} .dbbackup/$opts{dbfile}".time());
   }
-  open(RPS,">$irpgdb") or die "FAILED WRITING $irpgdb: $!";
+  open(RPS,">$opts{dbfile}") or die "$opts{dbfile}: $!";
 
   print RPS "# username\tpass\tlevel\tclass\tnext\tnick\tuserhost\tonline 0/1".
   "\tlast login time\tamulet\tcharm\thelm\tboots\tgloves\tring\t".
@@ -657,8 +673,8 @@
       $rps{$k}{next}-=$alrmint;
       if ($rps{$k}{next} < 1) {
         $rps{$k}{level}++;
-        $rps{$k}{next} = int($rpbase*($rpstep**$rps{$k}{level}));
-        sts("PRIVMSG $botchan :$k, the $rps{$k}{class}, has attained level ".
+        $rps{$k}{next} = int($opts{'rpbase'}*($opts{'rpstep'}**$rps{$k}{level}));
+        sts("PRIVMSG $opts{botchan} :$k, the $rps{$k}{class}, has attained level ".
         "$rps{$k}{level}! Next level in ".duration($rps{$k}{next}).
         ".");
         find_item($k);
@@ -705,7 +721,7 @@
     my $gain = int($rps{$opp}{level}/4);
     $gain = 7 if $gain < 7;
     $gain = int(($gain/100)*$rps{$u}{next});
-    sts("PRIVMSG $botchan :$u has challenged $opp in combat and won! ".
+    sts("PRIVMSG $opts{botchan} :$u has challenged $opp in combat and won! ".
     duration($gain)." is removed from $u"."'s clock.");
     open(B,">>battles.txt");
     print B "$u has challenged $opp in combat and won! ".duration($gain).
@@ -718,7 +734,7 @@
       print B "$u has dealt $opp a Critical Strike! ".duration($gain).
       " is added to $opp"."'s clock.\n";
       close B;
-      sts("PRIVMSG $botchan :$u has dealt $opp a Critical Strike! ".
+      sts("PRIVMSG $opts{botchan} :$u has dealt $opp a Critical Strike! ".
       duration($gain)." is added to $opp"."'s clock.");
       $rps{$opp}{next} += $gain;
     }
@@ -727,7 +743,7 @@
     my $gain = int($rps{$opp}{level}/7);
     $gain = 7 if $gain < 7;
     $gain = int(($gain/100)*$rps{$u}{next});
-    sts("PRIVMSG $botchan :$u has challenged $opp in combat and lost! ".
+    sts("PRIVMSG $opts{botchan} :$u has challenged $opp in combat and lost! ".
     duration($gain)." is added to $u"."'s clock.");
     open(B,">>battles.txt");
     print B "$u has challenged $opp in combat and lost! ".duration($gain).
@@ -754,7 +770,7 @@
       $gain = $rps{$opp[$p]}{next} if $gain > $rps{$opp[$p]}{next};
     }
     $gain = int($gain*.20);
-    sts("PRIVMSG $botchan :$opp[0], $opp[1], and $opp[2] have team battled ".
+    sts("PRIVMSG $opts{botchan} :$opp[0], $opp[1], and $opp[2] have team battled ".
         "$opp[3], $opp[4], and $opp[5] and won! ".duration($gain).
         " is removed from their clocks.");
     open(B,">>battles.txt");
@@ -767,7 +783,7 @@
     $rps{$opp[2]}{next} -= $gain;
   }
   else {
-    sts("PRIVMSG $botchan :$opp[0], $opp[1], and $opp[2] have team battled ".
+    sts("PRIVMSG $opts{botchan} :$opp[0], $opp[1], and $opp[2] have team battled ".
         "$opp[3], $opp[4], and $opp[5] and lost! No time is awarded.");
     open(B,">>battles.txt");
     print B "$opp[0], $opp[1], and $opp[2] have team battled $opp[3], ".
@@ -828,8 +844,8 @@
   }
   elsif ($level > $rps{$u}{item}{$type}) {
     sts("NOTICE $rps{$u}{nick} :You found a level $level $type! Your ".
-    "current $type is only level ".(0+$rps{$u}{item}{$type})." so ".
-    "it seems Luck is with you.");
+      "current $type is only level ".(0+$rps{$u}{item}{$type})." so ".
+      "it seems Luck is with you.");
     $rps{$u}{item}{$type} = $level;
   }
   else {
@@ -843,12 +859,13 @@
 sub loaddb { # load the players database
   undef %rps;
   %rps = ();
-  (open(RPS,"<$irpgdb") || ! -e $irpgdb) or die "loaddb() failed; $!";
-  while (chomp(my $l=<RPS>)) {
+  (open(RPS,"<$opts{dbfile}") || ! -e $opts{dbfile}) or die "loaddb() failed; $!";
+  while (my $l=<RPS>) {
+    chomp $l;
     next if $l =~ /^#/; # skip comments
     my @i = split("\t",$l);
     print Dumper @i if @i != 19;
-    die "Anomaly in loaddb(); line $. of $irpgdb has wrong fields (".
+    die "Anomaly in loaddb(); line $. of $opts{dbfile} has wrong fields (".
       scalar @i.")" if @i != 19;
     if ($i[7]) { push(@prev_online,$i[6]); } # log back in
     $rps{$i[0]}{pass} = $i[1];
@@ -871,4 +888,43 @@
     $rps{$i[0]}{item}{weapon} = $i[18];
   }
   close RPS;
+}
+
+sub mksalt { # generate a random salt for passwds
+	join '',('a'..'z','A'..'Z','0'..'9','/','.')[rand 64, rand 64];
+}
+
+sub chanmsg { # send a message to the channel
+	my $msg = shift or return undef;
+	privmsg($msg, $opts{'botchan'});
+}
+
+sub privmsg { # send a message to an arbitrary entity
+	my $msg = shift or return undef;
+	my $target = shift or return undef;
+	sts("PRIVMSG $target :$msg");
+}
+
+sub help { # print help message 
+	print "
+usage: $prog [OPTIONS]
+  --help, -h           Print this message
+  --verbose, -v        Print verbose messages
+  --server, -s         Specify IRC server:port to connect to
+  --botnick, -n        bot's IRC nick
+  --botuser, -u        bot's local username
+  --botrlnm, -r        bot's real name
+  --botchan, -c        IRC channel to join
+  --botpw, -p          Specify passwd to identify with
+  --botopcmd, -o       Specify message to spit into botchan on init
+  --helpurl            URL to refer newbies to
+  --admincommurl       URL to refer admins to
+  --access             usernames allowed to issue admin commands
+
+  Timing parameters:
+  --rpbase             base time to level up
+  --rpstep             time to next level = rpbase * (rpstep ** CURRENT_LEVEL)
+  --rppenstep          PENALTY_SECS=(PENALTY*(RPPENSTEP**CURRENT_LEVEL))
+
+";
 }

