#!/usr/local/bin/php
<?php
  
/* CBX's IRC Dice Bot
     - This is an IRC bot that handles dice rolls, and on systems that support it, will daemonize into the background
     created by Naram Qashat (CyberBotX)
     last modified: 11-24-2009 */
  
chdir(dirname(__FILE__)); // Change to the bot's directory if we weren't run from that directory.
  
require_once 'dice.php'// actual dice parser
  
require_once 'randomness.php'// randomness functions
  
require_once 'settings.php'// bot settings
  
ignore_user_abort(true); // Don't allow aborting (which is probably useless in a command-line script)
  
set_time_limit(0); // Never time out, run indefinitely
  
function output_help()
  {
    
// This function output's the syntax of the command
    
global $argv
?>
This script is an IRC Dice Rolling Bot.  It will attempt to fork itself
into the background if your Operating System allows it.

 Usage: <?php echo $argv[0?> [<options>] <IRC Server> <Initial IRC Channels>
     or <?php echo $argv[0?> -p|--password <Quit Password> [<NickServ Password>]

IRC Server must contain a dot if not localhost.
IRC Channels must start with #'s.  If you need the bot in more than one IRC
channel, seperate the channel names with commas only (no spaces).

Options:
 --help, -h, or -?  to get this help.
 --nofork or -f     don't let the bot fork into the background.
 --log or -l        log IRC traffic to irc.log
 --password or -p   change the passwords, does not run the bot, if NickServ
                    password left out, Quit password will be used for NickServ
<?php
  
}
  function 
rewrite_settings_file()
  {
    
// This funtion will rewrite the settings file, in case it changed
    
global $mainIRCnick$altIRCnick$allowed_remcmd$quit_password$ns_key$ns_password;
    if (
$fp = @fopen('settings.php''wb'))
    {
      
fwrite($fp"<?php\n");
      
fwrite($fp"  /* CBX's IRC Dice Bot\n");
      
fwrite($fp"     - This is the settings file for the bot\n");
      
fwrite($fp"     created by Naram Qashat (CyberBotX)\n");
      
fwrite($fp'     last modified: ' date('n-j-Y') . " */\n");
      
fwrite($fp"  \$mainIRCnick = '$mainIRCnick'; // The bot's main nick to use\n");
      
fwrite($fp"  \$altIRCnick = '$altIRCnick'; // An alternate nick for the bot if the main nick is in use\n");
      
fwrite($fp"  \$allowed_remcmd = array(\n");
      for (
$x 0$x count($allowed_remcmd); ++$x)
      {
        
fwrite($fp"    '{$allowed_remcmd[$x]}'");
        if (
$x != count($allowed_remcmd) - 1)
          
fwrite($fp',');
        
fwrite($fp"\n");
      }
      
fwrite($fp"  ); // Array of hostmasks allowed to use remote commands\n");
      
fwrite($fp"  \$quit_password = '$quit_password'; // MD5 of quit password\n");
      
fwrite($fp"  \$ns_key = '$ns_key'; // MD5 of NickServ password\n");
      
fwrite($fp"  \$ns_password = '$ns_password'; // mcrypt'd NickServ password\n");
      
fwrite($fp"?>");
      
fclose($fp);
    }
  }
  function 
allow_remcmd($who)
  {
    
// This function checks if the given user is on the list of hostmasks allowed to use remote commands on the bot
    
global $allowed_remcmd;
    foreach (
$allowed_remcmd as $remcmd// For every user in the $allowed_remcmd array...
    
{
      
$regex str_replace(array('.''*''!'), array('\.''.*''\!'), $remcmd); // Replaces . with \., * with .*, and ! with \!, for the regex
      
if (preg_match("/$regex/i"$who))
        return 
true;
    }
    return 
false;
  }
  function 
sendIRCcommand($cmd$who ''$msg ''$colonmsg true)
  {
    
// This function will send an IRC command to the IRC server, and log if enabled
    
global $ircsocket$log$logging;
    
$line $cmd;
    if (
$who)
      
$line .= $who";
    if (
$msg)
    {
      
$line .= ' ';
      if (
$colonmsg)
        
$line .= ':';
      
$line .= $msg;
    }
    
fwrite($ircsocket"$line\n");
    if (
$logging)
      
fwrite($log'[' date('D m/d/Y H:i:s O') . "] -> $line\n");
  }
  function 
convertTime($weeks)
  {
    
// This function converts a *nix timestamp of how many seconds between two times into a human readable time
    
$seconds $weeks 60;
    
$weeks -= $seconds;
    
$weeks /= 60;
    
$minutes $weeks 60;
    
$weeks -= $minutes;
    
$weeks /= 60;
    
$hours $weeks 24;
    
$weeks -= $hours;
    
$weeks /= 24;
    
$days $weeks 7;
    
$weeks -= $days;
    
$weeks /= 7;
    
$times = array();
    if (
$weeks)
      
$times[] = "$weeks week" . ($weeks 's' '');
    if (
$days)
      
$times[] = "$days day" . ($days 's' '');
    if (
$hours)
      
$times[] = "$hours hour" . ($hours 's' '');
    if (
$minutes)
      
$times[] = "$minutes minute" . ($minutes 's' '');
    if (
$seconds)
      
$times[] = "$seconds second" . ($seconds 's' '');
    return 
implode(' '$times);
  }
  function 
inChan($chan$status 'joined')
  {
    
// This function will check if the bot is within a channel, or if $status is given as 'pendingjoin' or 'pendingpart', if either of those is true
    
global $ircChans;
    if (
count($ircChans))
      foreach (
$ircChans as $key => $ircChan)
        if (
strtolower($ircChan['name']) == strtolower($chan) && $ircChan['status'] == $status)
          return 
$key;
    return 
false;
  }
  function 
find_mode($mode_to_find)
  {
    
// This function will find the given prefix mode in the $prefixes array and return the key within that array
    
global $prefixes;
    
$mkey false;
    foreach (
$prefixes as $key => $prefix)
      if (
$prefix['mode'] == $mode_to_find)
        
$mkey $key// Try to find the key using the mode symbol
    
if ($mkey === false)
      foreach (
$prefixes as $key => $prefix)
        if (
$prefix['char'] == $mode_to_find)
          
$mkey $key// Try to find the key using the character only if the symbol was not found
    
return $mkey// Return the symbol (or false if not found)
  
}
  function 
sort_modes(&$modes)
  {
    
// This function will sort a user's prefix modes to be in the order the 005 server reply had them in and returns the new order
    
$new_modes = array();
    for (
$x 0$x count($modes); ++$x)
    {
      
$key find_mode($modes[$x]);
      for (
$y 0$y count($new_modes); ++$y)
      {
        
$nkey find_mode($new_modes[$y]);
        if (
$key $nkey// Check if the current mode in $modes is a higher mode than the one within the $new_modes array, and add it before that if it is
        
{
          
array_splice($new_modes$y $y 00$modes[$x]);
          break;
        }
      }
      if (!
in_array($modes[$x], $new_modes))
        
$new_modes[] = $modes[$x]; // If we never added the mode, it's the smallest, add to the end
    
}
    
$modes $new_modes;
  }
  function 
find_chanmode($mode_to_find)
  {
    
// This function finds a channel mode and returns it's type
    
global $chanmodes;
    if (
count($chanmodes['param']))
      foreach (
$chanmodes['param'] as $mode)
        if (
$mode == $mode_to_find)
          return 
'param'// Check if the mode was a param mode
    
if (count($chanmodes['paramaddonly']))
      foreach (
$chanmodes['paramaddonly'] as $mode)
        if (
$mode == $mode_to_find)
          return 
'paramaddonly'// Check if the mode was a param mode that only uses a param on adding, not removing
    
if (count($chanmodes['paramless']))
      foreach (
$chanmodes['paramless'] as $mode)
        if (
$mode == $mode_to_find)
          return 
'paramless'// Check if the mode was a paramless mode
    
if (find_mode($mode_to_find) !== false)
      return 
'prefix'// Check if the mode was a prefix mode
    
return 'none'// The mode was not found
  
}
  function 
find_user($user_to_find)
  {
    
// This function will find a user within the $users array and return the key within that array
    
global $users;
    foreach (
$users as $key => $user)
      if (
strtolower($user['user']) == strtolower($user_to_find))
        return 
$key;
    return 
false;
  }
  
// The next function sorts a channels array based on a case-insensitive sort of everything after the #
  
function sort_chans($a$b) { return strcasecmp(substr($a['name'], 1), substr($b['name'], 1)); }
  function 
find_chan($chans$chan)
  {
    
// This function will find a channel within the given $chans array and return the key within that array
    
foreach ($chans as $key => $channel)
      if (
strtolower($channel['name']) == strtolower($chan))
        return 
$key;
    return 
false;
  }
  function 
add_chan(&$user$chan$status null)
  {
    
// This function will see if a user is within the given channel, if not, adds them, if they are, will just update their status if given
    
$key find_chan($user['chans'], $chan);
    if (
$key === false)
    {
      
$user['chans'][] = array('name' => $chan'status' => $status $status : array()); // Add the channel
      
usort($user['chans'], 'sort_chans'); // Sort the channels
    
}
    else
      
$user['chans'][$key]['status'] = $status $status : array(); // Update the status for an existing channel
  
}
  function 
add_user($user$chan$status null)
  {
    
// This function will add a user to the $users array if they aren't there already and add them to the given channel regardless
    
global $users;
    
$key find_user($user); // Try to find the user
    
if ($key === false)
      
$users[] = array('user' => $user'chans' => array()); // User wasn't found, add a whole new entry
    
add_chan($users[$key === false count($users) - $key], $chan$status); // Add the channel to the user, new or old
    
usort($users'sort_users');
  }
  function 
remove_user_from_chan($user$chan)
  {
    
// This function will remove a user from the given channel, and delete their user entry if they are not in any channels the bot is in anymore
    
global $users;
    
$key find_user($user); // Try to find the user
    
if ($key !== false// If the user was found (should be, but better safe than sorry)
    
{
      
$ckey find_chan($users[$key]['chans'], $chan); // Find the key of the channel (also should be found)
      
if ($ckey !== false)
        
array_splice($users[$key]['chans'], $ckey1); // Remove the channel from the array
      
if (!count($users[$key]['chans']))
        
array_splice($users$key1); // If the user is not in any of the bot's channel, remove the user
    
}
  }
  function 
remove_user($user)
  {
    
// This will completely remove the user from the $users array
    
global $users;
    
$key find_user($user); // Try to find the user (again, should be found)
    
if ($key !== false)
      
array_splice($users$key1); // Remove the user's entry entirely
  
}
  
// The next function does a case-insensitive sort of a user's nick
  
function sort_users($a$b) { return strcasecmp($a['user'], $b['user']); }
  function 
sort_users_in_chan($a$b)
  {
    
// This function sorts users, but does so by status, only used in the case of the meroll() function within randomness.php currently
    
$akey count($a['status']) ? find_mode($a['status'][0]) : false// Get the first user's status in the channel
    
$bkey count($b['status']) ? find_mode($b['status'][0]) : false// Get the second user's status in the channel
    
if ($akey !== false && $bkey === false)
      return -
1// If the first user has any status while the second has none, -1 means the first stays first
    
if ($akey === false && $bkey !== false)
      return 
1// If the second user has any status while the first has none, 1 means the second is made first
    
return $akey $bkey ? -: ($akey $bkey strcasecmp($a['user'], $b['user'])); // -1 if the first's status is greater, 1 if the second's status is greater, otherwise just sort by nick
  
}
  function 
chan_sort_users($chan)
  {
    
// This function creates an array that only contains users in the given channel and their status, and sorts them within that channel, currently only used within meroll()
    
global $users;
    
$chan_users = array();
    
// The following will make a special array for the channel that consists of only users and their status
    
foreach ($users as $user)
      foreach (
$user['chans'] as $channel)
        if (
strtolower($channel['name']) == strtolower($chan))
          
$chan_users[] = array('user' => $user['user'], 'status' => $channel['status']);
    
usort($chan_users'sort_users_in_chan'); // Sort the special array
    
return $chan_users// Return the new array
  
}
  
// The next function adds a new set of users (usually from a NAMES reply) into the $users array
  
function merge_users($newusers$chan) { foreach ($newusers as $useradd_user($user['user'], $chan$user['status']); }
  function 
remove_all_users_from_chan($chan)
  {
    
// This function will remove all users from the given channel
    
global $users;
    for (
$x 0$x count($users); ++$x)
    {
      
$oldusername $users[$x]['user']; // This stores the old username
      
remove_user_from_chan($users[$x]['user'], $chan); // Remove the user from the channel
      
if ($x count($users) && $users[$x]['user'] != $oldusername)
        --
$x// Lower $x by one if the user's entry was removed, needed so we don't miss users
    
}
  }
  function 
IRCtoks($str)
  {
    
// This function takes the received string from an IRC server and splits it apart at the \r\n gaps, returning an array with each of the "tokens"
    
$toks = array('');
    for (
$x 0$y 0$x strlen($str); ++$x)
    {
      if (
$str[$x] != "\n" && $str[$x] != "\r")
        
$toks[$y] .= $str[$x];
      if (
$str[$x] == "\n")
      {
        ++
$y;
        if (
$x != strlen($str) - 1)
          
$toks[$y] = '';
      }
    }
    return 
$toks;
  }
  function 
IRCmsg($str)
  {
    
// This function will take a single IRC command received from the server and split it apart at the spaces, handling the special case of a : marking the beginning of a full string
    
$msgparts = array(''); // Array for the message parts, starts with a single blank string
    
for ($x 0$y 0$x strlen($str); ++$x)
    {
      if (
$str[$x] == ':' && $x && !$msgparts[$y]) // The current character is a : that is not at the beginning, everything after it will be a single message
      
{
        
$msgparts[$y] = substr($str$x 1);
        break;
      }
      if (
$str[$x] == ' '// If we are at a space
      
{
        ++
$y;
        if (
$x != strlen($str))
          
$msgparts[$y] = ''// Add another blank string to the message parts if we are not at the end yet
        
continue; // Skip the space
      
}
      
$msgparts[$y] .= $str[$x]; // Add the character to the current message part
    
}
    return 
$msgparts// Return the message parts
  
}
  function 
IRCwho($who)
  {
    
// This function will return the username only of a full hostmask, removing the : from the beginning
    
$markpos strpos($who'!');
    return 
substr($who1$markpos === false strlen($who) : $markpos 1);
  }
  function 
StripControlCodes($text)
  {
    
// This function was taken from UnrealIRCd and modified for PHP by me, it removes all IRC control codes from a string and returns the stripped string
    
$i $save_len $nc $col $rgb 0;
    
$len strlen($text);
    
$save_text = -1;
    
$new_str '';
    for (; 
$len; --$len)
    {
      if (
$col && ((ctype_digit($text[$i]) && $nc 2) || ($text[$i] == ',' && $nc 3)))
      {
        ++
$nc;
        if (
$text[$i] == ',')
          
$nc 0;
      }
      
/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
       * If < 6 hex digits are specified, the code is displayed
       * as text */
      
elseif ($rgb && ((ctype_xdigit($text[$i]) && $nc 6) || ($text[$i] == ',' && $nc 7)))
      {
        ++
$nc;
        if (
$text[$i] == ',')
          
$nc 0;
      }
      else
      {
        if (
$col)
          
$col 0;
        if (
$rgb)
        {
          if (
$nc != 6)
          {
            
$i $save_text 1;
            
$len $save_len 1;
            
$rgb 0;
            continue;
          }
          
$rgb 0;
        }
        switch (
ord($text[$i]))
        {
          case 
3// color
            
$col 1;
            
$nc 0;
            break;
          case 
4// RGB
            
$save_text $i;
            
$save_len $len;
            
$rgb 1;
            
$nc 0;
            break;
          case 
2// bold
          
case 31// underline
          
case 22// reverse
          
case 15// plain
            
break;
          default:
            
$new_str .= $text[$i];
        }
      }
      ++
$i;
    }
    return 
$new_str;
  }
  
$nofork $logging $resetPasswords false;
  
// The following is an argument parser I wrote to handle arguments with -'s
  
$args = array();
  for (
$i 1$i $argc; ++$i)
  {
    switch (
$argv[$i])
    {
      case 
'-p':
      case 
'--password':
        
$resetPasswords true;
        break;
      case 
'-f':
      case 
'--nofork':
        
$nofork true;
        break;
      case 
'-l':
      case 
'--log':
        
$logging true;
        break;
      case 
'-?':
      case 
'-h':
      case 
'--help':
        
output_help();
        exit(
1);
      default:
        if (
substr($argv[$i], 01) == '-'// If it's an argument that started with - but wasn't handled already, it's an error
        
{
          
output_help();
          exit(
1);
        }
        
$args[] = $argv[$i]; // Add the argument to the args array as non-dash arguments
    
}
  }
  if (
$resetPasswords// If we are resetting the passwords...
  
{
    if (!
count($args)) // Needs at least 1 non-dash argument
    
{
      
output_help();
      exit(
1);
    }
    
$quit_password md5($args[0]); // md5 of the password
    
$ns_raw $args[count($args) == 0]; // If we had a 2nd argument, use that, otherwise, use the first one
    
$ns_key md5($ns_raw); // md5 of the password to use as a key for mcrypt
    
$ns_password bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128$ns_key$ns_rawMCRYPT_MODE_ECBstr_pad("\0"16"\0"))); // mcrypt of the password
    
rewrite_settings_file(); // rewrite the settings file with the new data
    
exit(0); // Exit afterwards, don't run the bot
  
}
  if (
count($args) < 2// Needs at least 2 non-dash arguments
  
{
    
output_help();
    exit(
1);
  }
  if (
$args[0] != 'localhost' && strpos($args[0], '.') === false// Argument 1 wasn't localhost and didn't have a dot (meaning not a server name or IP)
  
{
    echo 
"Server name (if not localhost) must have a dot in it!\n";
    exit(
1);
  }
  
$ircChans = array(); // This array will store all the channels the bot is in or going to be in
  
$initChans explode(','$args[1]);
  foreach (
$initChans as $initChan// Go through each of the given channels that were in argument 1
  
{
    if (
$initChan[0] != '#'// If the first character isn't a #, it's not a channel name
    
{
      echo 
"Initial IRC Channels must begin with #'s!\n";
      exit(
1);
    }
    
$ircChans[] = array('name' => $initChan'status' => 'pendingjoin'); // Add the channel to the array as pending join
  
}
  if (
$logging// If -l or --log was given, attempt to open the log file for writing
  
{
    
$log fopen('irc.log''w');
    if (!
$log// Couldn't open the file
    
{
      echo 
"Unable to open irc.log for logging purposes: $php_errormsg\n";
      exit(
1);
    }
  }
  if (!
$nofork && function_exists('pcntl_fork')) // If we didn't turn off forking and we are on a system that supports forking, daemonize the bot
  
{
    
$pid pcntl_fork();
    if (
$pid == -1// A -1 result means the daemonizing failed
    
{
      echo 
"Unable to daemonize...\n";
      exit(
1);
    }
    elseif (
$pid)
      exit(
0); // If the result wasn't 0, then this is the parent, it'll quit, leaving only the child
  
}
  
$botStartupTime time(); // Store the *nix timestamp of when the bot was started, used when ~uptime is asked for
  
$ircServer $args[0]; // Argument 1 was the IRC server to connect to
  
$myircNick $mainIRCnick// Stores the current nick of the bot (could change)
  // needchangeIRCnick = true if nick change to main nick is needed, false is already on main nick
  // istheremore = true only if the end of the received data from the IRC server isn't a \n (meaning more data is coming in)
  // throwFlooded = true when the !throw command was used 5 times in a row
  
$needchangeIRCnick $istheremore $throwFlooded false;
  
$ircsocket fsockopen($ircServer6667$errno$errstr); // Open a socket connection to the IRC server
  // throwEvents will be how many times the !throw command has been used since the last reset
  // endOfMOTD will store the *nix timestamp of when the MOTD command ended
  // seeIfInChannels will store a *nix timestamp when the bot has been blocked from any given channels
  
$throwEvents $endOfMOTD $seeIfInChannels 0;
  
$users $prefixes = array(); // Arrays for users and prefixes
  
$chanmodes = array('param' => array(), 'paramaddonly' => array(), 'paramless' => array()); // Array for valid channel modes
  
if ($ircsocket// If we were able to connect to the IRC server
  
{
    
stream_set_timeout($ircsocket10); // Set the timeout on the IRC socket to 10 seconds.
    
sendIRCcommand('NICK'$mainIRCnick);
    
sendIRCcommand('USER'"cbxdotcom cyberbotx.com $ircServer"'CBX The Dice Bot');
    while (
$ircsocket// As long as the IRC socket is still active
    
{
      if (
$needchangeIRCnick && isset($timeoflastnick) && time() - $timeoflastnick 30// If we need to change the bot's nick and it's been more than 30 seconds since the last change
      
{
        
sendIRCcommand('NICK'$mainIRCnick);
        
$timeoflastnick time();
      }
      if (
$throwEvents && time() - $timeOfLastThrowEvent 60)
        
$throwEvents 0// Reset !throw events after a minute if there were any events
      
if ($throwFlooded && time() - $timeOfThrowFlood 300)
        
$throwFlooded false// Reset if !throw was flooded after 5 minutes if it was flooded
      
if ($endOfMOTD && time() - $endOfMOTD 30// Join any pending channels 30 seconds after the MOTD ends and the bot hasn't joined any yet
      
{
        foreach (
$ircChans as $key => $ircChan)
          if (
$ircChan['status'] == 'pendingjoin')
            
sendIRCcommand('JOIN'$ircChan['name']);
        
$endOfMOTD 0;
      }
      if (
$seeIfInChannels && time() - $seeIfInChannels 15// Quit only if the bot is in no IRC channels after being blocked from any
      
{
        if (!
count($ircChans))
          
sendIRCcommand('QUIT''''Quitting because I couldn\'t join any channels...');
        
$seeIfInChannels 0;
      }
      
$r = array($ircsocket);
      
$w $e null;
      if (
stream_select($r$w$e1) === false// Try to read from the socket, close the socket if nothing could be read
      
{
        
fclose($ircsocket);
        
$ircsocket null;
        continue;
      }
      if (
in_array($ircsocket$r) || $istheremore// If there is something to read from the socket, or we know more data was supposed to come in...
      
{
        
$data fread($ircsocket1024); // Read the data
        
if (!strlen($data)) // If there was no data, assume that the link was closed and close the socket
        
{
          
fclose($ircsocket);
          
$ircsocket null;
          continue;
        }
        
// tmpstr will store the full string, including the previous one if there was more data incoming
        
$tmpstr '';
        if (
$istheremore)
          
$tmpstr .= $prevstr;
        
$tmpstr .= $data;
        
$tokens IRCtoks($tmpstr); // Split the message into seperate "tokens"
        
$istheremore substr($data, -1) == "\n" false true// istheremore will only be true if the last character is NOT a \n character
        
$morecoming false// morecoming will be false until the last line if the last line doesn't end in \n
        
if (count($tokens))
          foreach (
$tokens as $key => $token// For every token, check it
          
{
            if (!
$morecoming)
            {
              if (
$logging)
                
fwrite($log'[' date('D m/d/Y H:i:s O') . "] <- $token\n"); // Log the line as incoming if we are logging
              
$msgparts IRCmsg($token); // Split the line into seperate parts
              
if ($msgparts[0] == 'PING'// PING request
              
{
                
sendIRCcommand('PONG'''$msgparts[1]); // Reply with PONG
                
continue;
              }
              
$who IRCwho($msgparts[0]); // Get the nick (or servername) without the : or the ident/host
              
if ($msgparts[1] == '005'// ISUPPORT numeric for PREFIX, CHANMODES, and NAMESX
              
{
                for (
$x 3$x count($msgparts) - 1; ++$x// Go from the 4th part of the message on (1st is from, 2nd is 005, 3rd is to)
                
{
                  if (
substr($msgparts[$x], 07) == 'PREFIX='// PREFIX 005, format of PREFIX=(qaohv)~&@%+ for example
                  
{
                    
$openparen strpos($msgparts[$x], '(');
                    
$closeparen strpos($msgparts[$x], ')');
                    
$y $openparen 1;
                    
$z $closeparen 1;
                    for (; 
$y $closeparen; ++$y, ++$z)
                      
$prefixes[] = array('char' => $msgparts[$x][$y], 'mode' => $msgparts[$x][$z]);
                  }
                  if (
substr($msgparts[$x], 010) == 'CHANMODES='// CHANMODES 005, format of CHANMODES=listmodes,parammodes,paramaddonlymodes,paramlessmodes
                  
{
                    
$modes substr($msgparts[$x], 10);
                    list(
$a$b$c$d) = explode(','$modes);
                    
$parammodes "$a{$b}";
                    for (
$y 0$y strlen($parammodes); ++$y)
                      
$chanmodes['param'][] = $parammodes[$y];
                    for (
$y 0$y strlen($c); ++$y)
                      
$chanmodes['paramaddonly'][] = $c[$y];
                    for (
$y 0$y strlen($d); ++$y)
                      
$chanmodes['paramless'][] = $d[$y];
                  }
                  if (
$msgparts[$x] == 'NAMESX')
                    
sendIRCcommand('PROTOCTL NAMESX'); // Send a PROTOCTL NAMESX if the IRC server supports it
                
}
              }
              if (
$msgparts[1] == '433' && strtolower($msgparts[2]) == strtolower($myircNick)) // Failed NICK reply for bot
              
{
                if (
strtolower($msgparts[3]) == strtolower($mainIRCnick) && !$needchangeIRCnick// If the failed NICK was for the main nick and we haven't yet needed to change
                
{
                  
sendIRCcommand('NICK'$altIRCnick);
                  
$myircNick $altIRCnick;
                  
$needchangeIRCnick true;
                  
$timeoflastnick time();
                }
              }
              
// If any of the following comes up, remove the channel from the list and set up to kill the bot if it could not enter any channels
              // 471 = full channel, 473 = invite only, 474 = banned, 475 = key'd channel, 477 = channel needs registered nicks
              
if ($msgparts[1] == '471' || $msgparts[1] == '473' || $msgparts[1] == '474' || $msgparts[1] == '475' || $msgparts[1] == '477')
              {
                
$key find_chan($ircChans$msgparts[3]);
                if (
$key !== false)
                  
array_splice($ircChans$key1);
                
$seeIfInChannels time();
              }
              if (
$msgparts[1] == 'NICK'// NICK for anyone
              
{
                if (
strtolower($who) == strtolower($myircNick)) // NICK for the bot
                
{
                  
$needchangeIRCnick strtolower($msgparts[2]) == strtolower($mainIRCnick) ? false true// If the NICK is the bot's main nick, we don't need to change it
                  
$myircNick $msgparts[2];
                }
                else 
// NICK for everyone else
                
{
                  
$key find_user($who);
                  if (
$key !== false)
                    
$users[$key]['user'] = $msgparts[2];
                }
              }
              if (
$msgparts[1] == 'PRIVMSG'// PRIVMSG from anyone (including CTCPs and actions)
              
{
                if (
substr($msgparts[3], 01) == chr(1)) // CTCP
                
{
                  
$space strpos($msgparts[3], ' ');
                  
$ctcpdata $space === false '' substr($msgparts[3], $space 1);
                  
$msgpart1 $space === false $msgparts[3] : substr($msgparts[3], 0$space);
                  if (
substr($ctcpdata, -1) == chr(1))
                    
$ctcpdata substr($ctcpdata0, -1);
                  
$ctcp strtoupper(substr($msgpart11));
                  if (
substr($ctcp, -1) == chr(1))
                    
$ctcp substr($ctcp0, -1);
                  if (
$ctcp != 'ACTION'// If the CTCP was not an ACTION event
                  
{
                    
$ctcpreply null;
                    if (
$ctcp == 'VERSION')
                      
$ctcpreply chr(1) . 'VERSION Dice Rolling Bot by Naram Qashat a.k.a. CyberBotX' chr(1);
                    if (
$ctcp == 'PING')
                      
$ctcpreply $msgparts[3];
                    if (
$ctcp == 'TIME')
                      
$ctcpreply chr(1) . 'TIME ' date('D m/d/Y H:i:s O'). chr(1);
                    if (
$ctcpreply)
                      
sendIRCcommand('NOTICE'$who$ctcpreply);
                    if (
$ctcp == 'REMCMD' && allow_remcmd(substr($msgparts[0], 1))) // This handles remote commands via CTCP command
                    
{
                      if (
$ctcpdata)
                      {
                        
$space strpos($ctcpdata' ');
                        
$remcmd StripControlCodes(substr($ctcpdata0$space === false strlen($ctcpdata) : $space));
                        if (
strtolower($remcmd) != 'dump')
                        {
                          if (
$space !== false)
                          {
                            
$space2 strpos($ctcpdata' '$space 1);
                            
$chan StripControlCodes(substr($ctcpdata$space 1$space2 === false strlen($ctcpdata) : $space2 $space 1));
                          }
                          else
                            continue;
                        }
                        switch (
strtolower($remcmd))
                        {
                          case 
'msg'// Remote command to make the bot say something in the channel
                            
if ($space2 === false || inChan($chan) === false)
                              break;
                            
$msg substr($ctcpdata$space2 1);
                            
sendIRCcommand('PRIVMSG'$chan$msg);
                            break;
                          case 
'action'// Remote command to make the bot do an action in the channel
                            
if ($space2 === false || inChan($chan) === false)
                              break;
                            
$action substr($ctcpdata$space2 1);
                            
sendIRCcommand('PRIVMSG'$chanchr(1) . "ACTION $actionchr(1));
                            break;
                          case 
'throw'// Remote command to make the bot throw things in the channel
                            
if (inChan($chan) === false)
                              break;
                            
$randobj randobj($chan$space2 === false '' substr($ctcpdata$space2 1));
                            
sendIRCcommand('PRIVMSG'$chanchr(1) . "ACTION $randobjchr(1));
                            break;
                          case 
'roll':
                          case 
'exroll'// Remote commands to make the bot do dice rolls in the channel
                            
if ($space2 === false || inChan($chan) === false)
                              break;
                            
$dicecmd substr($ctcpdata$space2 1);
                            
$space strpos($dicecmd' ');
                            
$dice substr($dicecmd0$space === false strlen($dicecmd) : $space);
                            
$comment $space === false '' substr($dicecmd$space 1);
                            
$results dice_roll($dicestrtolower($remcmd) == 'roll' 2$comment);
                            if (
$results['result'])
                              
sendIRCcommand('PRIVMSG'$chan'<' . (strtolower($remcmd) == 'roll' 'R' 'Exr') . "oll [$dice]: " . (strtolower($remcmd) == 'exroll' && $results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                            break;
                          case 
'dnd3echar'// Remote command to make the bot do a D&D3e Character roll in the channel
                            
if (inChan($chan) === false)
                              break;
                            
$dice '';
                            
$comment $space2 === false '' substr($ctcpdata$space2 1);
                            
$results dice_roll($dice3$comment);
                            if (
$results['result'])
                              
sendIRCcommand('PRIVMSG'$chan"<DnD3e Character roll [$dice]: " . ($results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                            break;
                          case 
'join'// Remote command to make the bot join a channel
                            
if ($chan[0] != '#')
                              break;
                            
$key inChan($chan);
                            if (
$key !== false)
                              break;
                            
$ircChans[] = array('name' => $chan'status' => 'pendingjoin');
                            
sendIRCcommand('JOIN'$chan);
                            break;
                          case 
'part'// Remote command to make the bot part a channel
                            
if ($chan[0] != '#')
                              break;
                            
$key inChan($chan);
                            if (
$key === false)
                              break;
                            if (
count($ircChans) == 1// Don't allow the bot to be parted from it's last channel
                            
{
                              
sendIRCcommand('NOTICE'$who'Ignoring part request becasue you would be parting me from the last channel I\'m in.');
                              break;
                            }
                            
$ircChans[$key]['status'] = 'pendingpart';
                            
sendIRCcommand('PART'$chan);
                            break;
                          case 
'dump'// THIS IS MAINLY FOR DEBUGGING
                            
if ($fp fopen('dump.txt''wb'))
                            {
                              
fwrite($fp"botStartupTime: $botStartupTime\n");
                              
fwrite($fp"ircServer: $ircServer\n");
                              
fwrite($fp"myircNick: $myircNick\n");
                              
fwrite($fp'needchangeIRCnick: ' . ($needchangeIRCnick 'true' 'false') . "\n");
                              
fwrite($fp'istheremore: ' . ($istheremore 'true' 'false') . "\n");
                              
fwrite($fp'throwFlooded: ' . ($throwFlooded 'true' 'false') . "\n");
                              
fwrite($fp"throwEvents: $throwEvents\n");
                              
fwrite($fp"endOfMOTD: $endOfMOTD\n");
                              
$output print_r($userstrue);
                              
fwrite($fp"users: $output");
                              
$output print_r($prefixestrue);
                              
fwrite($fp"prefixes: $output");
                              
$output print_r($chanmodestrue);
                              
fwrite($fp"chanmodes: $output");
                              
$output print_r($ircChanstrue);
                              
fwrite($fp"ircChan: $output");
                              
fclose($fp);
                              
sendIRCcommand('NOTICE'$who'Data dumped to dump.txt successfully.');
                            }
                            else
                              
sendIRCcommand('NOTICE'$who'Unable to open dump.txt for writing.');
                        }
                      }
                    }
                  }
                }
                else 
// normal PRIVMSG
                
{
                  
$stripped_msg StripControlCodes($msgparts[3]);
                  if (
inChan($msgparts[2]) !== false// As long as the bot is in the channel in question
                  
{
                    if (
strtolower($stripped_msg) == '~help' || strtolower($stripped_msg) == '!help')
                    {
                      
// Help, obviously, sent as a NOTICE to the person who asked for it
                      
sendIRCcommand('NOTICE'$who'Help for Dice Roller Bot');
                      
sendIRCcommand('NOTICE'$who'========================');
                      
sendIRCcommand('NOTICE'$who'~roll <dice> [<comment>]: Does a dice roll, comment optional.');
                      
sendIRCcommand('NOTICE'$who'~exroll <dice> [<comment>]: Same as above, except each dice roll shown.');
                      
sendIRCcommand('NOTICE'$who'~dnd3echar [<comment>]: Rolls 6 sets of 4d6 and removes the lowest of each, used for D&D 3e/3.5e character rolls');
                      
sendIRCcommand('NOTICE'$who'!throw [<item num>] [<target>]: Throws random object, item num and target optional, target can be given without item num.');
                      
sendIRCcommand('NOTICE'$who'!uptime or ~uptime: Shows the bot\'s uptime and the uptime of the operating system the bot is running on');
                      
sendIRCcommand('NOTICE'$who'All roll commands can be done in PM as well as in channel.');
                    }
                    elseif (
strtolower(substr($stripped_msg05)) == '~roll' || strtolower(substr($stripped_msg07)) == '~exroll')
                    {
                      
// Dice rolling
                      
$offset strtolower(substr($stripped_msg05)) == '~roll' 8;
                      
$space strlen($stripped_msg) <= $offset false strpos($stripped_msg' '$offset);
                      
$dice strlen($stripped_msg) <= $offset '' substr($stripped_msg$offset$space === false strlen($stripped_msg) : $space $offset);
                      
$comment $space === false '' substr($stripped_msg$space 1);
                      
$results dice_roll($dice$offset == 2$comment);
                      if (
count($results['output']))
                        foreach (
$results['output'] as $output)
                          
sendIRCcommand('NOTICE'$who$output);
                      if (
$results['result'])
                        
sendIRCcommand('PRIVMSG'$msgparts[2], '<' . ($offset == 'R' 'Exr') . "oll [$dice]: " . ($offset == && $results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                    }
                    elseif (
strtolower(substr($stripped_msg010)) == '~dnd3echar')
                    {
                      
// D&D3e Character rolling
                      
$dice '';
                      
$comment strlen($stripped_msg) < 11 '' substr($stripped_msg11);
                      
$results dice_roll($dice3$comment);
                      if (
count($results['output']))
                        foreach (
$results['output'] as $output)
                          
sendIRCcommand('NOTICE'$who$output);
                      if (
$results['result'])
                        
sendIRCcommand('PRIVMSG'$msgparts[2], "<DnD3e Character roll [$dice]: " . ($results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                    }
                    elseif (
strtolower($stripped_msg) == '~uptime' || strtolower($stripped_msg) == '!uptime')
                    {
                      
// Bot's uptime, uses *nix's uptime command line option to get the system's uptime if it's available
                      
$botuptime convertTime(time() - $botStartupTime); // Convert the bot's uptime (current time minus the start time) to human readable format
                      
$uptime = @explode(',', `uptime`); // Try to get the system's uptime using the *nix "uptime" utility, if it exists
                      // uptime is in the following format:
                      //  11:51PM  up 5 days, 44 mins, 0 users, load averages: 1.40, 1.29, 1.41
                      
if (count($uptime)) // If we got a result, parse it
                      
{
                        
$first 0// First subscript that contains the uptime
                        
$last count($uptime) - 1// Last subscript that contains the uptime
                        
for ($x 0$x count($uptime); ++$x)
                        {
                          
$uptime[$x] = trim($uptime[$x]); // Trim all preceeding and trailing whitespace from the current part of uptime result
                          
if (($up strpos($uptime[$x], 'up ')) !== false// If we find the word up was found, the beginning of the uptime is in this string
                          
{
                            
$uptime[$x] = substr($uptime[$x], $up 3); // Remove everything before the time itself
                            
$first $x// Set this subscript as the first one
                          
}
                          if (
strpos($uptime[$x], 'user') !== false)
                            
$last $x 1// If user was found in this subscript, the previous one was the last
                        
}
                        
$uptime implode(', 'array_slice($uptime$first$last $first 1)); // Recombine only the needed uptime parts
                      
}
                      else
                        
$uptime ''// No uptime command, make a blank string
                      
sendIRCcommand('PRIVMSG'$msgparts[2], "Bot uptime: $botuptime");
                      if (
$uptime)
                        
sendIRCcommand('PRIVMSG'$msgparts[2], "System uptime: $uptime");
                    }
                    elseif (
strtolower(substr($stripped_msg06)) == '!throw')
                    {
                      
// Random object throwing, with flood protection, taken from CBX's mIRC script of the same functionality
                      
$space strpos($stripped_msg' ');
                      
$line $space === false '' substr($stripped_msg$space 1);
                      
// The following checks for words/phrases that should be ignored for any throwing commands.
                      
if ($line && (stripos($line'CBX') !== false || stripos($line'Naram') !== false || stripos($line$myircNick) !== false || stripos($line'self') !== false || stripos($line'general direction') !== false || stripos($line'hurler') !== false || stripos($line'projectilist') !== false || stripos($line'throw') !== false || stripos($line'his') !== false || (stripos($line'Heavenly') !== false && stripos($line'tech admin') !== false) || stripos($line'SeeBeeEcks') !== false || strtolower($line) == strtolower($myircNick) || stripos($line'IKR') !== false || stripos($line'selves') !== false || stripos($line'Script God') !== false || stripos($line'Isono\'s') !== false || preg_match('/.*C.*y.*b.*e.*r.*B.*o.*t.*X.*/i'$line) || preg_match('/.*C.*B.*X.*/i'$line) || stripos($line'moi') !== false || stripos($line'SeeBeeEx') !== false || stripos($line'bot') !== false || stripos($line'Pink warrior of the stars') !== false || stripos($line'him') !== false || stripos($line'bag') !== false || preg_match('/.*see.*bee.*ex.*/i'$line) || stripos($line'robutt') !== false || stripos($line'thyne') !== false || stripos($line'myzelf') !== false) || stripos($line'Kirby') !== false || stripos($line'Casper') !== false)
                        continue;
                      if (!
$throwFlooded// If the bot has not yet been flooded, increase the number of events and the time of the last event
                      
{
                        ++
$throwEvents;
                        
$timeOfLastThrowEvent time();
                      }
                      if (
$throwEvents == 5// If the bot has received 5 throw commands in the time limit, activate flood protection for 5 minutes
                      
{
                        
sendIRCcommand('PRIVMSG'$msgparts[2], 'Flood protection activated.  All !throw commands will be ignored for the next 5 minutes.');
                        
$throwFlooded true;
                        
$timeOfThrowFlood time();
                        
$throwEvents 0;
                        continue;
                      }
                      if (!
$throwFlooded// If the bot is not throw flooded, do the throw
                      
{
                        
$space strpos($msgparts[3], ' ');
                        
$randobj randobj($msgparts[2], $space === false '' substr($msgparts[3], $space 1));
                        
sendIRCcommand('PRIVMSG'$msgparts[2], chr(1) . "ACTION $randobjchr(1));
                      }
                    }
                  }
                  elseif (
strtolower($msgparts[2]) == strtolower($myircNick)) // If it wasn't for a channel, it might've been for the bot
                  
{
                    if (
strtolower(substr($stripped_msg05)) == '~roll' || strtolower(substr($stripped_msg07)) == '~exroll')
                    {
                      
// Dice rolling, but in PM instead
                      
$offset strtolower(substr($stripped_msg05)) == '~roll' 8;
                      
$space strlen($stripped_msg) <= $offset false strpos($stripped_msg' '$offset);
                      
$dice strlen($stripped_msg) <= $offset '' substr($stripped_msg$offset$space === false strlen($stripped_msg) : $space $offset);
                      
$comment $space === false '' substr($stripped_msg$space 1);
                      
$results dice_roll($dice$offset == 2$comment);
                      if (
count($results['output']))
                        foreach (
$results['output'] as $output)
                          
sendIRCcommand('NOTICE'$who$output);
                      if (
$results['result'])
                        
sendIRCcommand('PRIVMSG'$who'<' . ($offset == 'R' 'Exr') . "oll [$dice]: " . ($offset == && $results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                    }
                    elseif (
strtolower(substr($stripped_msg010)) == '~dnd3echar')
                    {
                      
// D&D3e Characeter rolling, but in PM instead
                      
$dice '';
                      
$comment strlen($stripped_msg) < 11 '' substr($stripped_msg11);
                      
$results dice_roll($dice3$comment);
                      if (
count($results['output']))
                        foreach (
$results['output'] as $output)
                          
sendIRCcommand('NOTICE'$who$output);
                      if (
$results['result'])
                        
sendIRCcommand('PRIVMSG'$who"<DnD3e Character roll [$dice]: " . ($results['exbuf'] ? $results['exbuf'] : '') . "{$results['buf']}>" . ($comment $comment''));
                    }
                    else
                      
sendIRCcommand('PRIVMSG'$who'Sorry, I am just a bot.'); // Tell the person this is a bot
                  
}
                }
              }
              if (
$msgparts[1] == 'NOTICE'// NOTICE from anyone
              
{
                
$stripped_msg StripControlCodes($msgparts[3]);
                if (
strtolower($msgparts[2]) == strtolower($myircNick) && strpos($who'.') === false// If NOTICE is for the bot and not from a server
                
{
                  if (
strtolower($who) == 'nickserv'// If it's from NickServ
                  
{
                    
// When the message is about changing nickname, send the NickServ password
                    // When the message is that the password was accepted, try to join the channels
                    
if ($stripped_msg == 'If you do not change your nickname within one minute, you will be disconnected.' || $stripped_msg == 'If you do not change within one minute, I will change your nick.')
                    {
                      
$password rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128$ns_keypack('H*'$ns_password), MCRYPT_MODE_ECBstr_pad("\0"16"\0")), "\0");
                      
sendIRCcommand('PRIVMSG''NickServ'"identify $password");
                    }
                    elseif (
$stripped_msg == 'Password accepted -- you are now recognized.')
                      foreach (
$ircChans as $key => $ircChan)
                        if (
$ircChan['status'] == 'pendingjoin')
                          
sendIRCcommand('JOIN'$ircChan['name']);
                  }
                  else 
// For all other NOTICES
                  
{
                    
// If the NOTICE was ~quit followed by the correct password, make the bot quit from IRC
                    
if (strtolower(substr($stripped_msg05)) == '~quit' && md5(substr($stripped_msg6)) == $quit_password)
                      
sendIRCcommand('QUIT'''"CBX Dice Bot quitting on request of $who");
                  }
                }
              }
              if (
$msgparts[1] == 'JOIN'// JOIN for anyone
              
{
                if (
strtolower($who) == strtolower($myircNick)) // If the JOIN is for the bot
                
{
                  if ((
$key inChan($msgparts[2], 'pendingjoin')) === false)
                    
sendIRCcommand('PART'$msgparts[2]); // PART if the bot isn't pending a join to the channel
                  
else
                    
$ircChans[$key]['status'] = 'joined'// Otherwise make it joined
                
}
                else 
// JOIN for anyone else
                
{
                  if (
inChan($msgparts[2]) !== false)
                    
add_user($who$msgparts[2]); // Add that user to the channel
                
}
              }
              if (
$msgparts[1] == 'PART'// PART for anyone
              
{
                if (
strtolower($who) != strtolower($myircNick))
                  
remove_user_from_chan($who$msgparts[2]); // PART for everyone but the bot, remove them from the channel
                
elseif (($key inChan($msgparts[2], 'pendingpart')) !== false// PART for the bot and it's pending a part
                
{
                  
remove_all_users_from_chan($msgparts[2]); // Remove all user entries from that channel
                  
array_splice($ircChans$key1); // Remove the channel from the bot's list of channels
                
}
                elseif ((
$key inChan($msgparts[2])) !== false// PART for the bot but it isn't pending a part from it
                
{
                  
remove_all_users_from_chan($msgparts[2]); // Remove the user entries for the channel to regenerate them
                  
$ircChans[$key]['status'] = 'pendingjoin'// Make the channel pending a join
                  
sendIRCcommand('JOIN'$msgparts[2]); // Re-JOIN the channel
                
}
              }
              if (
$msgparts[1] == '353' && inChan($msgparts[4]) !== false// NAMES reply for a joined channel
              
{
                
// This will set up an array of the users in the channel and merge them into the list for the channel
                
$users_tmp explode(' '$msgparts[5]);
                
$users_this = array();
                foreach (
$users_tmp as $user)
                {
                  if (!
$user)
                    continue;
                  
$status = array();
                  if (
find_mode($user[0]) !== false)
                  {
                    for (
$x 1$x strlen($user); ++$x)
                      if (
find_mode($user[$x]) === false)
                        break;
                    for (
$y 0$y $x; ++$y)
                      
$status[] = $user[$y];
                    
sort_modes($status);
                    
$user substr($user$x);
                  }
                  
$users_this[] = array('status' => $status'user' => $user);
                }
                
merge_users($users_this$msgparts[4]);
              }
              if (
$msgparts[1] == 'KICK' && ($key inChan($msgparts[2])) !== false// KICK for a joined channel
              
{
                
remove_user_from_chan($who$msgparts[2]); // Remove the user from the channel regardless of if it's the bot or not
                
if (strtolower($msgparts[3]) == strtolower($myircNick)) // If the KICK was for the bot
                
{
                  
remove_all_users_from_chan($msgparts[2]); // Remove the user entries for the channel to regenerate them
                  
$ircChans[$key]['status'] = 'pendingjoin'// Make the channel pending a join
                  
sendIRCcommand('JOIN'$msgparts[2]); // Re-JOIN the channel
                
}
              }
              if (
$msgparts[1] == 'QUIT' && strtolower($who) != strtolower($myircNick))
                
remove_user($who); // QUIT for anyone except the bot, remove their user entry
              
if ($msgparts[1] == 'MODE' && inChan($msgparts[2]) !== false// MODE for a joined channel
              
{
                
// The following will get the modes and their params (if any), then update users for any prefix modes
                
$modes $msgparts[3];
                
$params = array();
                for (
$x 4$x count($msgparts); ++$x)
                  
$params[] = $msgparts[$x]; // Get all the params
                
$add 1;
                for (
$x 0$y 0$x strlen($modes); ++$x)
                {
                  
$modechar $modes[$x];
                  switch (
$modechar)
                  {
                    case 
'+'$add 1; break; // + = Mode being added
                    
case '-'$add 0; break; // - = Mode being removed
                    
default:
                      
$modetype find_chanmode($modechar);
                      
$param $modetype == 'param' || ($modetype == 'paramaddonly' && $add) || $modetype == 'prefix' $params[$y++] : '';
                      if (
$modetype == 'prefix'// If the mode character is a prefix mode
                      
{
                        
$ukey find_user($param); // Find the user
                        
$ckey find_chan($users[$ukey]['chans'], $msgparts[2]); // Find the channel entry in their user entry
                        
$mkey find_mode($modechar); // Find the key of the mode
                        
$prefix $prefixes[$mkey]['mode']; // Get the actual prefix symbol
                        
$skey false;
                        
// The following finds the key of the prefix symbol if it exists
                        
if (count($users[$ukey]['chans'][$ckey]['status']))
                          foreach (
$users[$ukey]['chans'][$ckey]['status'] as $key => $mode)
                            if (
$mode == $prefix)
                              
$skey $key;
                        if (
$add && $skey === false// If we are adding a mode and the prefix symbol does not exist, add it and sort the modes in correct order
                        
{
                          
$users[$ukey]['chans'][$ckey]['status'][] = $prefix;
                          
sort_modes($users[$ukey]['status']);
                        }
                        else 
// We aren't adding, and the next will only happen if the prefix symbol was found
                        
{
                          if (
$skey !== false)
                            
array_splice($users[$ukey]['chans'][$ckey]['status'], $skey1); // Remove the prefix symbol
                        
}
                      }
                  }
                }
              }
              if (
$msgparts[1] == '376' || $msgparts[1] == '422')
                
$endOfMOTD time(); // 376 = End of MOTD, 422 = No MOTD
            
}
            
$morecoming $key == count($tokens) - && $istheremore true false// morecoming will be true if we are on the second to last token and there is more coming
            
$laststr $token// This token will be the last one
          
}
        if (
$istheremore)
          
$prevstr $laststr// If there is more coming, set the previous string to the last one
      
}
    }
  }
  if (
$logging)
    
fclose($log); // Close the log file, if we are logging
?>