* @copyright 2008-2010 Phergie Development Team (http://phergie.org) * @license http://phergie.org/license New BSD License * @link http://pear.phergie.org/package/Phergie_Plugin_UserInfo */ /** * Provides an API for querying information on users. * * @category Phergie * @package Phergie_Plugin_UserInfo * @author Phergie Development Team * @license http://phergie.org/license New BSD License * @link http://pear.phergie.org/package/Phergie_Plugin_UserInfo */ class Phergie_Plugin_UserInfo extends Phergie_Plugin_Abstract { const REGULAR = 1; const VOICE = 2; const HALFOP = 4; const OP = 8; const ADMIN = 16; const OWNER = 32; /** * An array containing all the user information for a given channel * * @var array */ protected $store = array(); /** * Tracks mode changes * * @return void */ public function onMode() { $args = $this->event->getArguments(); if (count($args) != 3) { return; } list($chan, $modes, $nicks) = $args; if (!preg_match('/(?:\+|-)[hovaq+-]+/i', $modes)) { return; } $chan = trim(strtolower($chan)); $modes = str_split(trim(strtolower($modes)), 1); $nicks = explode(' ', trim(strtolower($nicks))); $operation = array_shift($modes); // + or - while ($char = array_shift($modes)) { $nick = array_shift($nicks); $mode = null; switch ($char) { case 'q': $mode = self::OWNER; break; case 'a': $mode = self::ADMIN; break; case 'o': $mode = self::OP; break; case 'h': $mode = self::HALFOP; break; case 'v': $mode = self::VOICE; break; } if (!empty($mode)) { if ($operation == '+') { $this->store[$chan][$nick] |= $mode; } else if ($operation == '-') { $this->store[$chan][$nick] ^= $mode; } } } } /** * Tracks users joining a channel * * @return void */ public function onJoin() { $chan = trim(strtolower($this->event->getArgument(0))); $nick = trim(strtolower($this->event->getNick())); $this->store[$chan][$nick] = self::REGULAR; } /** * Tracks users leaving a channel * * @return void */ public function onPart() { $chan = trim(strtolower($this->event->getArgument(0))); $nick = trim(strtolower($this->event->getNick())); if (isset($this->store[$chan][$nick])) { unset($this->store[$chan][$nick]); } } /** * Tracks users quitting a server * * @return void */ public function onQuit() { $nick = trim(strtolower($this->event->getNick())); foreach ($this->store as $chan => $store) { if (isset($store[$nick])) { unset($this->store[$chan][$nick]); } } } /** * Tracks users changing nicks * * @return void */ public function onNick() { $nick = trim(strtolower($this->event->getNick())); $newNick = trim(strtolower($this->event->getArgument(0))); foreach ($this->store as $chan => $store) { if (isset($store[$nick])) { $this->store[$chan][$newNick] = $store[$nick]; unset($this->store[$chan][$nick]); } } } /** * Populates the internal user listing for a channel when the bot joins it. * * @return void */ public function onResponse() { if ($this->event->getCode() != Phergie_Event_Response::RPL_NAMREPLY) { return; } $desc = preg_split('/[@*=]\s*/', $this->event->getDescription(), 2); list($chan, $users) = array_pad(explode(' :', trim($desc[1])), 2, null); $users = explode(' ', trim($users)); $chan = trim(strtolower($chan)); foreach ($users as $user) { if (empty($user)) { continue; } $user = trim(strtolower($user)); $flag = self::REGULAR; if ($user[0] == '~') { $flag |= self::OWNER; } else if ($user[0] == '&') { $flag |= self::ADMIN; } else if ($user[0] == '@') { $flag |= self::OP; } else if ($user[0] == '%') { $flag |= self::HALFOP; } else if ($user[0] == '+') { $flag |= self::VOICE; } if ($flag != self::REGULAR) { $user = substr($user, 1); } $this->store[$chan][$user] = $flag; } } /** * Debugging function * * @return void */ public function onPrivmsg() { if ($this->getConfig('debug', false) == false) { return; } list($target, $msg) = array_pad($this->event->getArguments(), 2, null); if (preg_match('#^ishere (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isIn($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^isowner (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isOwner($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^isadmin (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isAdmin($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^isop (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isOp($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^ishop (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isHalfop($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^isvoice (\S+)$#', $msg, $m)) { $this->doPrivmsg($target, $this->isVoice($m[1], $target) ? 'true' : 'false'); } elseif (preg_match('#^channels (\S+)$#', $msg, $m)) { $channels = $this->getChannels($m[1]); $this->doPrivmsg($target, $channels ? join(', ', $channels) : 'unable to find nick'); } elseif (preg_match('#^users (\S+)$#', $msg, $m)) { $nicks = $this->getUsers($m[1]); $this->doPrivmsg($target, $nicks ? join(', ', $nicks) : 'unable to find channel'); } elseif (preg_match('#^random (\S+)$#', $msg, $m)) { $nick = $this->getrandomuser($m[1]); $this->doPrivmsg($target, $nick ? $nick : 'unable to find channel'); } } /** * Checks whether or not a given user has a mode * * @param int $mode A numeric mode (identified by the class constants) * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function is($mode, $nick, $chan) { $chan = trim(strtolower($chan)); $nick = trim(strtolower($nick)); if (!isset($this->store[$chan][$nick])) { return false; } return ($this->store[$chan][$nick] & $mode) != 0; } /** * Checks whether or not a given user has owner (~) status * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isOwner($nick, $chan) { return $this->is(self::OWNER, $nick, $chan); } /** * Checks whether or not a given user has admin (&) status * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isAdmin($nick, $chan) { return $this->is(self::ADMIN, $nick, $chan); } /** * Checks whether or not a given user has operator (@) status * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isOp($nick, $chan) { return $this->is(self::OP, $nick, $chan); } /** * Checks whether or not a given user has halfop (%) status * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isHalfop($nick, $chan) { return $this->is(self::HALFOP, $nick, $chan); } /** * Checks whether or not a given user has voice (+) status * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isVoice($nick, $chan) { return $this->is(self::VOICE, $nick, $chan); } /** * Checks whether or not a given user is in a channel * * @param string $nick The nick to check * @param string $chan The channel to check in * * @return bool */ public function isIn($nick, $chan) { return $this->is(self::REGULAR, $nick, $chan); } /** * Returns the entire user list for a channel or false if the bot is not * in the channel. * * @param string $chan The channel name * * @return array|bool */ public function getUsers($chan) { $chan = trim(strtolower($chan)); if (isset($this->store[$chan])) { return array_keys($this->store[$chan]); } return false; } /** * Returns the nick of a random user present in a given channel or false * if the bot is not present in the channel. * * @param string $chan The channel name * * @return array|bool */ public function getRandomUser($chan) { $chan = trim(strtolower($chan)); if (isset($this->store[$chan])) { $ignore = array('chanserv', 'q', 'l', 's'); do { $nick = array_rand($this->store[$chan], 1); } while (in_array($nick, $ignore)); return $nick; } return false; } /** * Returns a list of channels in which a given user is present. * * @param string $nick Nick of the user (optional, defaults to the bot's * nick) * * @return array|bool */ public function getChannels($nick = null) { if (empty($nick)) { $nick = $this->connection->getNick(); } $nick = trim(strtolower($nick)); $channels = array(); foreach ($this->store as $chan => $store) { if (isset($store[$nick])) { $channels[] = $chan; } } return $channels; } }