/** * This function will process all messages received directly from the * socket, in here we will handle various callbacks and functions. * * @param string $sMessage The raw message being received. * @return boolean */ public function onReceive($sMessage) { // TODO Refactor this mess. $this->In->Chunks = explode(' ', $sMessage); $this->In->Raw = $sMessage; $sNetworkName = $this->m_aBotInfo['Network']['Name']; $this->In->User = new User($sNetworkName, substr($this->In->Chunks[0], 1)); $this->In->PostColon = ''; /** Extract message, if there is any. **/ $nColonPosition = strpos($sMessage, ' :', 2); if ($nColonPosition !== false) { $this->In->PostColon = substr($sMessage, $nColonPosition + 2); } // Exception for "PING", which is not the second piece; if ($this->In->Chunks[0] == 'PING') { return $this->m_pSocket->send('PONG :' . $this->In->PostColon); } ErrorExceptionHandler::$Context = $this; $pModules = ModuleManager::getInstance(); $bIsSlave = $this->m_aBotInfo['Slave']; switch (strtolower($this->In->Chunks[1])) { case '001': $this->m_aBotInfo['Connected'] = true; $this->m_aBotInfo['ConnectTry'] = 0; $this->m_aBotInfo['SentPings'] = 0; $this->m_sNickname = $this->In->Chunks[2]; $this->m_pSocket->send('MODE ' . $this->m_sNickname . ' +B'); $pModules->onConnect($this); break; case '005': // Information about what the server supports; $pNetworkManager = NetworkManager::getInstance(); $pNetworkManager->parseSupported($sNetworkName, array_slice($this->In->Chunks, 3)); if ($pNetworkManager->getSupportRule($sNetworkName, 'NAMESX') !== false) { $this->m_pSocket->send('PROTOCTL NAMESX'); } break; case '332': // Topic command if (!$bIsSlave) { $pModules->onChannelTopic($this, $this->In->Chunks[3], $this->In->PostColon); } break; case '353': // Names command if (!$bIsSlave) { $pModules->onChannelNames($this, $this->In->Chunks[4], $this->In->PostColon); } break; case 'invite': // Inviting someone to a channel if (!$bIsSlave) { $pModules->onInvite($this, $this->In->User->Nickname, $this->In->Chunks[2], substr($this->In->Chunks[3], 1)); } break; case 'join': // Joining a certain channel $sChannel = str_replace(':', '', $this->In->Chunks[2]); if ($this->In->User->Nickname == $this->m_sNickname) { $this->m_aBotInfo['Channels'][strtolower($sChannel)] = true; } if (!$bIsSlave) { $pModules->onChannelJoin($this, $sChannel, $this->In->User->Nickname); } break; case 'kick': // When someone gets kicked if ($this->In->Chunks[3] == $this->m_sNickname) { unset($this->m_aBotInfo['Channels'][strtolower($this->In->Chunks[2])]); } if (!$bIsSlave) { $pModules->onChannelKick($this, $this->In->Chunks[2], $this->In->Chunks[3], $this->In->User->Nickname, $this->In->PostColon); } break; case 'mode': // Change a mode on a channel if (!$bIsSlave) { $pModules->onChannelMode($this, $this->In->Chunks[2], implode(' ', array_slice($this->In->Chunks, 3))); } break; case 'nick': // Nickchanges $sNewNick = str_replace(':', '', $this->In->Chunks[2]); if ($this->In->User->Nickname == $this->m_sNickname) { $this->m_sNickname = $sNewNick; } if (!$bIsSlave) { $pModules->onChangeNick($this, $this->In->User->Nickname, $sNewNick); } break; case 'notice': // Notice received from someone/something if ($this->In->PostColon == 'Nuwani201') { /** Notice to see if we're still connected. **/ $this->m_aBotInfo['SentPings']--; break; } if (!$bIsSlave) { $pModules->onNotice($this, $this->In->Chunks[2], $this->In->User->Nickname, $this->In->PostColon); } break; case 'part': // Leaving a channel $sChannel = str_replace(':', '', $this->In->Chunks[2]); if ($this->In->User->Nickname == $this->m_sNickname) { unset($this->m_aBotInfo['Channels'][strtolower($sChannel)]); } if (!$bIsSlave) { $pModules->onChannelPart($this, $sChannel, $this->In->User->Nickname, $this->In->PostColon); } break; case 'privmsg': // A normal message of somekind if ($bIsSlave) { break; } /** slaves don't handle messages **/ $sMessageSource = ltrim($this->In->Chunks[2], '+%@&~:'); if ($this->In->PostColon[0] != chr(1)) { if (substr($sMessageSource, 0, 1) == '#') { // If the bot was addressed, remove the prefix and handle the message like normal. // The original message is still available in $pBot -> In -> PostColon. $sMessage = $this->In->PostColon; $sPrefix = $this->m_sNickname . ': '; if (substr($sMessage, 0, strlen($sPrefix)) == $sPrefix) { $sMessage = substr($sMessage, strlen($sPrefix)); } $pModules->onChannelPrivmsg($this, $sMessageSource, $this->In->User->Nickname, $sMessage); } else { $pModules->onPrivmsg($this, $this->In->User->Nickname, $this->In->PostColon); } } else { $sType = strtoupper(substr(str_replace("", '', $this->In->Chunks[3]), 1)); $sMessage = trim(substr($this->In->PostColon, strlen($sType) + 2, -1)); $pModules->onCTCP($this, $sMessageSource, $this->In->User->Nickname, $sType, $sMessage); } break; case 'topic': // A topic has been changed if (!$bIsSlave) { $pModules->onChangeTopic($this, $this->In->Chunks[2], $this->In->User->Nickname, $this->In->PostColon); } break; case 'quit': // Leaving IRC alltogether if (!$bIsSlave) { $pModules->onQuit($this, $this->In->User->Nickname, $this->In->PostColon); } break; default: $pModules->onUnhandledCommand($this); break; } if (!$bIsSlave) { // Slaves are dumb... no really, they are. $pModules->onRaw($this, $this->In->Raw); } ErrorExceptionHandler::$Context = null; return true; }
/** * The invoking function which allows us to use fancy syntax for commands. It allows the user * and bot-system to directly invoke the object variable. * * @param Bot $pBot The bot that got the command. * @param string $sDestination The place we should send the output to. * @param string $sChannel The channel the command was received in. * @param string $sNickname The nickname of the user who executed the command. * @param array $aParams The parameters given to the command. * @param string $sMessage The complete message after the command. * @throws Exception If the command was not callable. * @return boolean Did the command execute? */ public function __invoke(Bot $pBot, $sDestination, $sChannel, $sNickname, $aParams, $sMessage) { if (!is_callable($this->m_rCachedCommand)) { /** Quite a serious problem this is. Throw an Exception. **/ throw new Exception('Command ' . $this->getCommand() . ' is not callable.'); } if ($this->getPermission() !== null && !$pBot->getSecurityManager()->hasPermission($pBot->In->User, $this->getPermission())) { return false; } if ($this->checkNetwork($pBot['Network']) && $this->checkChannel($sChannel)) { $cFunction = $this->m_rCachedCommand; $this->m_aStatistics['Executed']++; $this->m_aStatistics['LastTime'] = time(); /** Let the exception handler know where we are executing code. **/ ErrorExceptionHandler::$Source = $sDestination; /** Catch output. **/ ob_start(); $fStart = microtime(true); /** Execute the command. **/ $nReturnCode = call_user_func($cFunction, $pBot, $sDestination, $sChannel, $sNickname, $aParams, $sMessage); $this->m_aStatistics['TotalTime'] += microtime(true) - $fStart; if ($nReturnCode == null || !is_int($nReturnCode)) { $nReturnCode = self::OUTPUT_NORMAL; } switch ($nReturnCode) { default: case self::OUTPUT_NORMAL: $sCallback = 'privmsg'; break; case self::OUTPUT_ERROR: $sCallback = 'error'; break; case self::OUTPUT_INFO: $sCallback = 'info'; break; case self::OUTPUT_NOTICE: $sCallback = 'notice'; break; case self::OUTPUT_SUCCESS: $sCallback = 'success'; break; case self::OUTPUT_USAGE: $sCallback = 'usage'; break; } /** Send output to IRC. **/ $aOutput = explode("\n", trim(ob_get_clean())); if (isset($pBot) && $pBot instanceof Bot) { foreach ($aOutput as $sLine) { call_user_func(array('Command', $sCallback), $pBot, $sDestination, $sLine); } } return true; } return false; }
/** * This function will immediatly execute the evaluation that we're doing * right now. Evaluation delays are already handled by the parser. * * @param Bot $pBot Bot that should handle the evaluation. * @param string $sDestination Where should the output be redirected? * @param array $aOptions Array with the parsed options for this evaluation. */ public function doEvaluation(Bot $pBot, $sDestination, $aOptions) { ob_start(); ErrorExceptionHandler::$Source = $sDestination; switch ($aOptions['Type']) { case 'PHP': eval($aOptions['Operation']); break; case 'EXEC': $process = proc_open($aOptions['Operation'], array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); if (is_resource($process)) { fclose($pipes[0]); $output = trim(stream_get_contents($pipes[1])); fclose($pipes[1]); $error = trim(stream_get_contents($pipes[2])); fclose($pipes[2]); $returnValue = proc_close($process); if ($output != '') { echo $output . ' '; } $haveNewLine = false; if ($error != '') { echo PHP_EOL . ModuleBase::COLOUR_RED . '* Error' . ModuleBase::CLEAR . ': ' . $error . ' '; $haveNewLine = true; } if ($returnValue !== 0) { if (!$haveNewLine) { echo PHP_EOL; } else { echo '| '; } echo ModuleBase::COLOUR_TEAL; if (!$haveNewLine) { echo '* '; } echo 'Return value' . ModuleBase::CLEAR . ': ' . $returnValue; $haveNewLine = true; } } break; case 'SQL': $pDatabase = Database::getInstance(); if ($pDatabase == null || $pDatabase->connect_error) { echo '4* Database Error: Could not connect to database'; if ($pDatabase != null) { echo ': "' . $pDatabase->connect_error . '"'; } echo '.'; break; } $pResult = $pDatabase->query($aOptions['Operation']); if ($pResult === false) { echo '4* Database Error: ' . $pDatabase->error; } else { if ($pResult->num_rows == 0) { echo '10* No rows.'; } else { $aFields = $aFieldLen = array(); foreach ($pResult->fetch_fields() as $pField) { $aFields[$pField->name] = array($pField->name); $aFieldLen[$pField->name] = strlen($pField->name); } while (($aRow = $pResult->fetch_assoc()) != null) { foreach ($aRow as $sField => $mValue) { if ($mValue == null) { $mValue = 'NULL'; } $aFields[$sField][] = $mValue; $aFieldLen[$sField] = max($aFieldLen[$sField], strlen((string) $mValue)); } } // Output is imminent. for ($i = 0; $i <= $pResult->num_rows; $i++) { // 1 extra loop for the header. foreach (array_keys($aFields) as $sField) { echo sprintf('| ' . ($i == 0 ? '' : '') . '%\'' . chr(160) . '-' . $aFieldLen[$sField] . 's' . ($i == 0 ? '' : '') . ' ', $aFields[$sField][$i]); } echo '|' . PHP_EOL; } } } break; } $aOutput = explode("\n", trim(ob_get_clean())); if (count($aOutput) > $aOptions['MaxLines'] + 1 && $aOptions['Buffering'] == true) { $nSize = count($aOutput); $aOutput = array_slice($aOutput, 0, $aOptions['MaxLines']); $aOutput[] = '10* Displayed ' . $aOptions['MaxLines'] . ' out of ' . $nSize . ' lines.'; } if (isset($pBot) && $pBot instanceof Bot) { foreach ($aOutput as $sLine) { $pBot->send('PRIVMSG ' . $sDestination . ' :' . trim($sLine), false); } } }