diff options
Diffstat (limited to 'plugins/Irc/extlib/phergie/Phergie')
98 files changed, 16472 insertions, 0 deletions
diff --git a/plugins/Irc/extlib/phergie/Phergie/Autoload.php b/plugins/Irc/extlib/phergie/Phergie/Autoload.php new file mode 100755 index 000000000..0004f44e2 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Autoload.php @@ -0,0 +1,81 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Autoloader for Phergie classes. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Autoload +{ + /** + * Constructor to add the base Phergie path to the include_path. + * + * @return void + */ + public function __construct() + { + $path = realpath(dirname(__FILE__) . '/..'); + $includePath = get_include_path(); + $includePathList = explode(PATH_SEPARATOR, $includePath); + if (!in_array($path, $includePathList)) { + self::addPath($path); + } + } + + /** + * Autoload callback for loading class files. + * + * @param string $class Class to load + * + * @return void + */ + public function load($class) + { + include str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; + } + + /** + * Registers an instance of this class as an autoloader. + * + * @return void + */ + public static function registerAutoloader() + { + spl_autoload_register(array(new self, 'load')); + } + + /** + * Add a path to the include path. + * + * @param string $path Path to add + * + * @return void + */ + public static function addPath($path) + { + set_include_path($path . PATH_SEPARATOR . get_include_path()); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Bot.php b/plugins/Irc/extlib/phergie/Phergie/Bot.php new file mode 100755 index 000000000..85e8a00fc --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Bot.php @@ -0,0 +1,390 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Composite class for other components to represent the bot. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Bot +{ + /** + * Current version of Phergie + */ + const VERSION = '2.0.1'; + + /** + * Current driver instance + * + * @var Phergie_Driver_Abstract + */ + protected $driver; + + /** + * Current configuration instance + * + * @var Phergie_Config + */ + protected $config; + + /** + * Current connection handler instance + * + * @var Phergie_Connection_Handler + */ + protected $connections; + + /** + * Current plugin handler instance + * + * @var Phergie_Plugin_Handler + */ + protected $plugins; + + /** + * Current event handler instance + * + * @var Phergie_Event_Handler + */ + protected $events; + + /** + * Current end-user interface instance + * + * @var Phergie_Ui_Abstract + */ + protected $ui; + + /** + * Current processor instance + * + * @var Phergie_Process_Abstract + */ + protected $processor; + + /** + * Returns a driver instance, creating one of the default class if + * none has been set. + * + * @return Phergie_Driver_Abstract + */ + public function getDriver() + { + if (empty($this->driver)) { + // Check if a driver has been defined in the configuration to use + // as the default + $config = $this->getConfig(); + if (isset($config['driver'])) { + $class = 'Phergie_Driver_' . ucfirst($config['driver']); + } else { + // Otherwise default to the Streams driver. + $class = 'Phergie_Driver_Streams'; + } + + $this->driver = new $class; + } + return $this->driver; + } + + /** + * Sets the driver instance to use. + * + * @param Phergie_Driver_Abstract $driver Driver instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setDriver(Phergie_Driver_Abstract $driver) + { + $this->driver = $driver; + return $this; + } + + /** + * Sets the configuration to use. + * + * @param Phergie_Config $config Configuration instance + * + * @return Phergie_Runner_Abstract Provides a fluent interface + */ + public function setConfig(Phergie_Config $config) + { + $this->config = $config; + return $this; + } + + /** + * Returns the entire configuration in use or the value of a specific + * configuration setting. + * + * @param string $index Optional index of a specific configuration + * setting for which the corresponding value should be returned + * @param mixed $default Value to return if no match is found for $index + * + * @return mixed Value corresponding to $index or the entire + * configuration if $index is not specified + */ + public function getConfig($index = null, $default = null) + { + if (empty($this->config)) { + $this->config = new Phergie_Config; + $this->config->read('Settings.php'); + } + if ($index !== null) { + if (isset($this->config[$index])) { + return $this->config[$index]; + } else { + return $default; + } + } + return $this->config; + } + + /** + * Returns a plugin handler instance, creating it if it does not already + * exist and using a default class if none has been set. + * + * @return Phergie_Plugin_Handler + */ + public function getPluginHandler() + { + if (empty($this->plugins)) { + $this->plugins = new Phergie_Plugin_Handler( + $this->getConfig(), + $this->getEventHandler() + ); + } + return $this->plugins; + } + + /** + * Sets the plugin handler instance to use. + * + * @param Phergie_Plugin_Handler $handler Plugin handler instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setPluginHandler(Phergie_Plugin_Handler $handler) + { + $this->plugins = $handler; + return $this; + } + + /** + * Returns an event handler instance, creating it if it does not already + * exist and using a default class if none has been set. + * + * @return Phergie_Event_Handler + */ + public function getEventHandler() + { + if (empty($this->events)) { + $this->events = new Phergie_Event_Handler; + } + return $this->events; + } + + /** + * Sets the event handler instance to use. + * + * @param Phergie_Event_Handler $handler Event handler instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setEventHandler(Phergie_Event_Handler $handler) + { + $this->events = $handler; + return $this; + } + + /** + * Returns a connection handler instance, creating it if it does not + * already exist and using a default class if none has been set. + * + * @return Phergie_Connection_Handler + */ + public function getConnectionHandler() + { + if (empty($this->connections)) { + $this->connections = new Phergie_Connection_Handler; + } + return $this->connections; + } + + /** + * Sets the connection handler instance to use. + * + * @param Phergie_Connection_Handler $handler Connection handler instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setConnectionHandler(Phergie_Connection_Handler $handler) + { + $this->connections = $handler; + return $this; + } + + /** + * Returns an end-user interface instance, creating it if it does not + * already exist and using a default class if none has been set. + * + * @return Phergie_Ui_Abstract + */ + public function getUi() + { + if (empty($this->ui)) { + $this->ui = new Phergie_Ui_Console; + } + return $this->ui; + } + + /** + * Sets the end-user interface instance to use. + * + * @param Phergie_Ui_Abstract $ui End-user interface instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setUi(Phergie_Ui_Abstract $ui) + { + $this->ui = $ui; + return $this; + } + + /** + * Returns a processer instance, creating one if none exists. + * + * @return Phergie_Process_Abstract + */ + public function getProcessor() + { + if (empty($this->processor)) { + $class = 'Phergie_Process_Standard'; + + $type = $this->getConfig('processor'); + if (!empty($type)) { + $class = 'Phergie_Process_' . ucfirst($type); + } + + $this->processor = new $class( + $this, + $this->getConfig('processor.options', array()) + ); + } + return $this->processor; + } + + /** + * Sets the processer instance to use. + * + * @param Phergie_Process_Abstract $processor Processer instance + * + * @return Phergie_Bot Provides a fluent interface + */ + public function setProcessor(Phergie_Process_Abstract $processor) + { + $this->processor = $processor; + return $this; + } + + /** + * Loads plugins into the plugin handler. + * + * @return void + */ + protected function loadPlugins() + { + $config = $this->getConfig(); + $plugins = $this->getPluginHandler(); + $ui = $this->getUi(); + + $plugins->setAutoload($config['plugins.autoload']); + foreach ($config['plugins'] as $name) { + try { + $plugin = $plugins->addPlugin($name); + $ui->onPluginLoad($name); + } catch (Phergie_Plugin_Exception $e) { + $ui->onPluginFailure($name, $e->getMessage()); + if (!empty($plugin)) { + $plugins->removePlugin($plugin); + } + } + } + } + + /** + * Configures and establishes connections to IRC servers. + * + * @return void + */ + protected function loadConnections() + { + $config = $this->getConfig(); + $driver = $this->getDriver(); + $connections = $this->getConnectionHandler(); + $plugins = $this->getPluginHandler(); + $ui = $this->getUi(); + + foreach ($config['connections'] as $data) { + $connection = new Phergie_Connection($data); + $connections->addConnection($connection); + + $ui->onConnect($data['host']); + $driver->setConnection($connection)->doConnect(); + $plugins->setConnection($connection); + $plugins->onConnect(); + } + } + + /** + * Establishes server connections and initiates an execution loop to + * continuously receive and process events. + * + * @return Phergie_Bot Provides a fluent interface + */ + public function run() + { + set_time_limit(0); + + $timezone = $this->getConfig('timezone', 'UTC'); + date_default_timezone_set($timezone); + + $ui = $this->getUi(); + $ui->setEnabled($this->getConfig('ui.enabled')); + + $this->loadPlugins(); + $this->loadConnections(); + + $processor = $this->getProcessor(); + + $connections = $this->getConnectionHandler(); + while (count($connections)) { + $processor->handleEvents(); + } + + $ui->onShutdown(); + + return $this; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Config.php b/plugins/Irc/extlib/phergie/Phergie/Config.php new file mode 100755 index 000000000..c182f2ac1 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Config.php @@ -0,0 +1,186 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Reads from and writes to PHP configuration files and provides access to + * the settings they contain. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Config implements ArrayAccess +{ + /** + * Mapping of configuration file paths to an array of names of settings + * they contain + * + * @var array + */ + protected $files = array(); + + /** + * Mapping of setting names to their current corresponding values + * + * @var array + */ + protected $settings = array(); + + /** + * Includes a specified PHP configuration file and incorporates its + * return value (which should be an associative array) into the current + * configuration settings. + * + * @param string $file Path to the file to read + * + * @return Phergie_Config Provides a fluent interface + * @throws Phergie_Config_Exception + */ + public function read($file) + { + if (!(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' + && file_exists($file)) + && !is_executable($file) + ) { + throw new Phergie_Config_Exception( + 'Path "' . $file . '" does not reference an executable file', + Phergie_Config_Exception::ERR_FILE_NOT_EXECUTABLE + ); + } + + $settings = include $file; + if (!is_array($settings)) { + throw new Phergie_Config_Exception( + 'File "' . $file . '" does not return an array', + Phergie_Config_Exception::ERR_ARRAY_NOT_RETURNED + ); + } + + $this->files[$file] = array_keys($settings); + $this->settings += $settings; + + return $this; + } + + /** + * Merges an associative array of configuration setting values into the + * current configuration settings. + * + * @param array $settings Associative array of configuration setting + * values keyed by setting name + * + * @return Phergie_Config Provides a fluent interface + */ + public function readArray(array $settings) + { + $this->settings += $settings; + + return $this; + } + + /** + * Writes the values of the current configuration settings back to their + * originating files. + * + * @return Phergie_Config Provides a fluent interface + */ + public function write() + { + foreach ($this->files as $file => &$settings) { + $values = array(); + foreach ($settings as $setting) { + $values[$setting] = $this->settings[$setting]; + } + $source = '<?php' . PHP_EOL . PHP_EOL . + 'return ' . var_export($value, true) . ';'; + file_put_contents($file, $source); + } + } + + /** + * Checks to see if a configuration setting is assigned a value. + * + * @param string $offset Configuration setting name + * + * @return bool TRUE if the setting has a value, FALSE otherwise + * @see ArrayAccess::offsetExists() + */ + public function offsetExists($offset) + { + return isset($this->settings[$offset]); + } + + /** + * Returns the value of a configuration setting. + * + * @param string $offset Configuration setting name + * + * @return mixed Configuration setting value or NULL if it is not + * assigned a value + * @see ArrayAccess::offsetGet() + */ + public function offsetGet($offset) + { + if (isset($this->settings[$offset])) { + $value = &$this->settings[$offset]; + } else { + $value = null; + } + + return $value; + } + + /** + * Sets the value of a configuration setting. + * + * @param string $offset Configuration setting name + * @param mixed $value New setting value + * + * @return void + * @see ArrayAccess::offsetSet() + */ + public function offsetSet($offset, $value) + { + $this->settings[$offset] = $value; + } + + /** + * Removes the value set for a configuration setting. + * + * @param string $offset Configuration setting name + * + * @return void + * @see ArrayAccess::offsetUnset() + */ + public function offsetUnset($offset) + { + unset($this->settings[$offset]); + + foreach ($this->files as $file => $settings) { + $key = array_search($offset, $settings); + if ($key !== false) { + unset($this->files[$file][$key]); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Config/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Config/Exception.php new file mode 100644 index 000000000..fb646c10c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Config/Exception.php @@ -0,0 +1,44 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to configuration. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Config_Exception extends Phergie_Exception +{ + /** + * Error indicating that an attempt was made to read a configuration + * file that could not be executed + */ + const ERR_FILE_NOT_EXECUTABLE = 1; + + /** + * Error indicating that a read configuration file does not return an + * array + */ + const ERR_ARRAY_NOT_RETURNED = 2; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Connection.php b/plugins/Irc/extlib/phergie/Phergie/Connection.php new file mode 100755 index 000000000..746dec05f --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Connection.php @@ -0,0 +1,401 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Data structure for connection metadata. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Connection +{ + /** + * Host to which the client will connect + * + * @var string + */ + protected $host; + + /** + * Port on which the client will connect, defaults to the standard IRC + * port + * + * @var int + */ + protected $port; + + /** + * Transport for the connection, defaults to tcp but can be set to ssl + * or variations thereof to connect over SSL + * + * @var string + */ + protected $transport; + + /** + * Encoding method for the connection, defaults to ISO-8859-1 but can + * be set to UTF8 if necessary + * + * @var strng + */ + protected $encoding; + + /** + * Nick that the client will use + * + * @var string + */ + protected $nick; + + /** + * Username that the client will use + * + * @var string + */ + protected $username; + + /** + * Realname that the client will use + * + * @var string + */ + protected $realname; + + /** + * Password that the client will use + * + * @var string + */ + protected $password; + + /** + * Hostmask for the connection + * + * @var Phergie_Hostmask + */ + protected $hostmask; + + /** + * Constructor to initialize instance properties. + * + * @param array $options Optional associative array of property values + * to initialize + * + * @return void + */ + public function __construct(array $options = array()) + { + $this->transport = 'tcp'; + $this->encoding = 'ISO-8859-1'; + // @note this may need changed to something different, for broader support. + // @note also may need to make use of http://us.php.net/manual/en/function.stream-encoding.php + + $this->setOptions($options); + } + + /** + * Emits an error related to a required connection setting does not have + * value set for it. + * + * @param string $setting Name of the setting + * + * @return void + */ + protected function checkSetting($setting) + { + if (empty($this->$setting)) { + throw new Phergie_Connection_Exception( + 'Required connection setting "' . $setting . '" missing', + Phergie_Connection_Exception::ERR_REQUIRED_SETTING_MISSING + ); + } + } + + /** + * Returns a hostmask that uniquely identifies the connection. + * + * @return string + */ + public function getHostmask() + { + if (empty($this->hostmask)) { + $this->hostmask = new Phergie_Hostmask( + $this->getNick(), + $this->getUsername(), + $this->getHost() + ); + } + + return $this->hostmask; + } + + /** + * Sets the host to which the client will connect. + * + * @param string $host Hostname + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setHost($host) + { + if (empty($this->host)) { + $this->host = (string) $host; + } + + return $this; + } + + /** + * Returns the host to which the client will connect if it is set or + * emits an error if it is not set. + * + * @return string + */ + public function getHost() + { + $this->checkSetting('host'); + + return $this->host; + } + + /** + * Sets the port on which the client will connect. + * + * @param int $port Port + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setPort($port) + { + if (empty($this->port)) { + $this->port = (int) $port; + } + + return $this; + } + + /** + * Returns the port on which the client will connect. + * + * @return int + */ + public function getPort() + { + if (empty($this->port)) { + $this->port = 6667; + } + + return $this->port; + } + + /** + * Sets the transport for the connection to use. + * + * @param string $transport Transport (ex: tcp, ssl, etc.) + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setTransport($transport) + { + $this->transport = (string) $transport; + + if (!in_array($this->transport, stream_get_transports())) { + throw new Phergie_Connection_Exception( + 'Transport ' . $this->transport . ' is not supported', + Phergie_Connection_Exception::ERR_TRANSPORT_NOT_SUPPORTED + ); + } + + return $this; + } + + /** + * Returns the transport in use by the connection. + * + * @return string Transport (ex: tcp, ssl, etc.) + */ + public function getTransport() + { + return $this->transport; + } + + /** + * Sets the encoding for the connection to use. + * + * @param string $encoding Encoding to use (ex: ASCII, ISO-8859-1, UTF8, etc.) + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setEncoding($encoding) + { + $this->encoding = (string) $encoding; + + if (!in_array($this->encoding, mb_list_encodings())) { + throw new Phergie_Connection_Exception( + 'Encoding ' . $this->encoding . ' is not supported', + Phergie_Connection_Exception::ERR_ENCODING_NOT_SUPPORTED + ); + } + + return $this; + } + + /** + * Returns the encoding in use by the connection. + * + * @return string Encoding (ex: ASCII, ISO-8859-1, UTF8, etc.) + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Sets the nick that the client will use. + * + * @param string $nick Nickname + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setNick($nick) + { + if (empty($this->nick)) { + $this->nick = (string) $nick; + } + + return $this; + } + + /** + * Returns the nick that the client will use. + * + * @return string + */ + public function getNick() + { + $this->checkSetting('nick'); + + return $this->nick; + } + + /** + * Sets the username that the client will use. + * + * @param string $username Username + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setUsername($username) + { + if (empty($this->username)) { + $this->username = (string) $username; + } + + return $this; + } + + /** + * Returns the username that the client will use. + * + * @return string + */ + public function getUsername() + { + $this->checkSetting('username'); + + return $this->username; + } + + /** + * Sets the realname that the client will use. + * + * @param string $realname Real name + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setRealname($realname) + { + if (empty($this->realname)) { + $this->realname = (string) $realname; + } + + return $this; + } + + /** + * Returns the realname that the client will use. + * + * @return string + */ + public function getRealname() + { + $this->checkSetting('realname'); + + return $this->realname; + } + + /** + * Sets the password that the client will use. + * + * @param string $password Password + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setPassword($password) + { + if (empty($this->password)) { + $this->password = (string) $password; + } + + return $this; + } + + /** + * Returns the password that the client will use. + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Sets multiple connection settings using an array. + * + * @param array $options Associative array of setting names mapped to + * corresponding values + * + * @return Phergie_Connection Provides a fluent interface + */ + public function setOptions(array $options) + { + foreach ($options as $option => $value) { + $method = 'set' . ucfirst($option); + if (method_exists($this, $method)) { + $this->$method($value); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Connection/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Connection/Exception.php new file mode 100644 index 000000000..aec1cd8e0 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Connection/Exception.php @@ -0,0 +1,50 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to a connection to an IRC server. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Connection_Exception extends Phergie_Exception +{ + /** + * Error indicating that an operation was attempted requiring a value + * for a specific configuration setting, but none was set + */ + const ERR_REQUIRED_SETTING_MISSING = 1; + + /** + * Error indicating that a connection is configured to use a transport, + * but that transport is not supported by the current PHP installation + */ + const ERR_TRANSPORT_NOT_SUPPORTED = 2; + + /** + * Error indicating that a connection is configured to use an encoding, + * but that encoding is not supported by the current PHP installation + */ + const ERR_ENCODING_NOT_SUPPORTED = 3; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Connection/Handler.php b/plugins/Irc/extlib/phergie/Phergie/Connection/Handler.php new file mode 100644 index 000000000..e9aeddcd3 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Connection/Handler.php @@ -0,0 +1,130 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Handles connections initiated by the bot. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Connection_Handler implements Countable, IteratorAggregate +{ + /** + * Map of connections indexed by hostmask + * + * @var array + */ + protected $connections; + + /** + * Constructor to initialize storage for connections. + * + * @return void + */ + public function __construct() + { + $this->connections = array(); + } + + /** + * Adds a connection to the connection list. + * + * @param Phergie_Connection $connection Connection to add + * + * @return Phergie_Connection_Handler Provides a fluent interface + */ + public function addConnection(Phergie_Connection $connection) + { + $this->connections[(string) $connection->getHostmask()] = $connection; + return $this; + } + + /** + * Removes a connection from the connection list. + * + * @param Phergie_Connection|string $connection Instance or hostmask for + * the connection to remove + * + * @return Phergie_Connection_Handler Provides a fluent interface + */ + public function removeConnection($connection) + { + if ($connection instanceof Phergie_Connection) { + $hostmask = (string) $connection->getHostmask(); + } elseif (is_string($connection) + && isset($this->connections[$connection])) { + $hostmask = $connection; + } else { + return $this; + } + unset($this->connections[$hostmask]); + return $this; + } + + /** + * Returns the number of connections in the list. + * + * @return int Number of connections + */ + public function count() + { + return count($this->connections); + } + + /** + * Returns an iterator for the connection list. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->connections); + } + + /** + * Returns a list of specified connection objects. + * + * @param array|string $keys One or more hostmasks identifying the + * connections to return + * + * @return array List of Phergie_Connection objects corresponding to the + * specified hostmask(s) + */ + public function getConnections($keys) + { + $connections = array(); + + if (!is_array($keys)) { + $keys = array($keys); + } + + foreach ($keys as $key) { + if (isset($this->connections[$key])) { + $connections[] = $this->connections[$key]; + } + } + + return $connections; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Driver/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Driver/Abstract.php new file mode 100755 index 000000000..62736620d --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Driver/Abstract.php @@ -0,0 +1,301 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for drivers which handle issuing client commands to the IRC + * server and converting responses into usable data objects. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +abstract class Phergie_Driver_Abstract +{ + /** + * Currently active connection + * + * @var Phergie_Connection + */ + protected $connection; + + /** + * Sets the currently active connection. + * + * @param Phergie_Connection $connection Active connection + * + * @return Phergie_Driver_Abstract Provides a fluent interface + */ + public function setConnection(Phergie_Connection $connection) + { + $this->connection = $connection; + + return $this; + } + + /** + * Returns the currently active connection. + * + * @return Phergie_Connection + * @throws Phergie_Driver_Exception + */ + public function getConnection() + { + if (empty($this->connection)) { + throw new Phergie_Driver_Exception( + 'Operation requires an active connection, but none is set', + Phergie_Driver_Exception::ERR_NO_ACTIVE_CONNECTION + ); + } + + return $this->connection; + } + + /** + * Returns an event if one has been received from the server. + * + * @return Phergie_Event_Interface|null Event instance if an event has + * been received, NULL otherwise + */ + public abstract function getEvent(); + + /** + * Initiates a connection with the server. + * + * @return void + */ + public abstract function doConnect(); + + /** + * Terminates the connection with the server. + * + * @param string $reason Reason for connection termination (optional) + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_1_6 + */ + public abstract function doQuit($reason = null); + + /** + * Joins a channel. + * + * @param string $channels Comma-delimited list of channels to join + * @param string $keys Optional comma-delimited list of channel keys + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_1 + */ + public abstract function doJoin($channels, $keys = null); + + /** + * Leaves a channel. + * + * @param string $channels Comma-delimited list of channels to leave + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_2 + */ + public abstract function doPart($channels); + + /** + * Invites a user to an invite-only channel. + * + * @param string $nick Nick of the user to invite + * @param string $channel Name of the channel + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_7 + */ + public abstract function doInvite($nick, $channel); + + /** + * Obtains a list of nicks of users in specified channels. + * + * @param string $channels Comma-delimited list of one or more channels + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_5 + */ + public abstract function doNames($channels); + + /** + * Obtains a list of channel names and topics. + * + * @param string $channels Comma-delimited list of one or more channels + * to which the response should be restricted + * (optional) + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_6 + */ + public abstract function doList($channels = null); + + /** + * Retrieves or changes a channel topic. + * + * @param string $channel Name of the channel + * @param string $topic New topic to assign (optional) + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_4 + */ + public abstract function doTopic($channel, $topic = null); + + /** + * Retrieves or changes a channel or user mode. + * + * @param string $target Channel name or user nick + * @param string $mode New mode to assign (optional) + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_3 + */ + public abstract function doMode($target, $mode = null); + + /** + * Changes the client nick. + * + * @param string $nick New nick to assign + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_1_2 + */ + public abstract function doNick($nick); + + /** + * Retrieves information about a nick. + * + * @param string $nick Nick + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_5_2 + */ + public abstract function doWhois($nick); + + /** + * Sends a message to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the message to send + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_4_1 + */ + public abstract function doPrivmsg($target, $text); + + /** + * Sends a notice to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the notice to send + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_4_2 + */ + public abstract function doNotice($target, $text); + + /** + * Kicks a user from a channel. + * + * @param string $nick Nick of the user + * @param string $channel Channel name + * @param string $reason Reason for the kick (optional) + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_2_8 + */ + public abstract function doKick($nick, $channel, $reason = null); + + /** + * Responds to a server test of client responsiveness. + * + * @param string $daemon Daemon from which the original request originates + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html#c4_6_3 + */ + public abstract function doPong($daemon); + + /** + * Sends a CTCP ACTION (/me) command to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the action to perform + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.4 + */ + public abstract function doAction($target, $text); + + /** + * Sends a CTCP PING request to a user. + * + * @param string $nick User nick + * @param string $hash Hash to use in the handshake + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.2 + */ + public abstract function doPing($nick, $hash); + + /** + * Sends a CTCP VERSION request or response to a user. + * + * @param string $nick User nick + * @param string $version Version string to send for a response + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.1 + */ + public abstract function doVersion($nick, $version = null); + + /** + * Sends a CTCP TIME request to a user. + * + * @param string $nick User nick + * @param string $time Time string to send for a response + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.6 + */ + public abstract function doTime($nick, $time = null); + + /** + * Sends a CTCP FINGER request to a user. + * + * @param string $nick User nick + * @param string $finger Finger string to send for a response + * + * @return void + * @link http://www.irchelp.org/irchelp/rfc/ctcpspec.html + */ + public abstract function doFinger($nick, $finger = null); + + /** + * Sends a raw command to the server. + * + * @param string $command Command string to send + * + * @return void + */ + public abstract function doRaw($command); +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Driver/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Driver/Exception.php new file mode 100755 index 000000000..5873b2cb9 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Driver/Exception.php @@ -0,0 +1,59 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to driver operations. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Driver_Exception extends Phergie_Exception +{ + /** + * Error indicating that an operation was requested requiring an active + * connection before one had been set + */ + const ERR_NO_ACTIVE_CONNECTION = 1; + + /** + * Error indicating that an operation was requested requiring an active + * connection where one had been set but not initiated + */ + const ERR_NO_INITIATED_CONNECTION = 2; + + /** + * Error indicating that an attempt to initiate a connection failed + */ + const ERR_CONNECTION_ATTEMPT_FAILED = 3; + + /** + * Error indicating that an attempt to send data via a connection failed + */ + const ERR_CONNECTION_WRITE_FAILED = 4; + + /** + * Error indicating that an attempt to read data via a connection failed + */ + const ERR_CONNECTION_READ_FAILED = 5; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php b/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php new file mode 100644 index 000000000..84c85a01c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Driver/Statusnet.php @@ -0,0 +1,66 @@ +<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Extends the Streams driver (Phergie_Driver_Streams) to give external access
+ * to the socket resources and send method
+ *
+ * @category Phergie
+ * @package Phergie_Driver_Statusnet
+ * @author Luke Fitzgerald <lw.fitzgerald@googlemail.com>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class Phergie_Driver_Statusnet extends Phergie_Driver_Streams {
+ /**
+ * Handles construction of command strings and their transmission to the
+ * server.
+ *
+ * @param string $command Command to send
+ * @param string|array $args Optional string or array of sequential
+ * arguments
+ *
+ * @return string Command string that was sent
+ * @throws Phergie_Driver_Exception
+ */
+ public function send($command, $args = '') {
+ return parent::send($command, $args);
+ }
+
+ public function forceQuit() {
+ try {
+ // Send a QUIT command to the server
+ $this->send('QUIT', 'Reconnecting');
+ } catch (Phergie_Driver_Exception $e){}
+
+ // Terminate the socket connection
+ fclose($this->socket);
+
+ // Remove the socket from the internal socket list
+ unset($this->sockets[(string) $this->getConnection()->getHostmask()]);
+ }
+
+ /**
+ * Returns the array of sockets
+ *
+ * @return array Array of socket resources
+ */
+ public function getSockets() {
+ return $this->sockets;
+ }
+}
\ No newline at end of file diff --git a/plugins/Irc/extlib/phergie/Phergie/Driver/Streams.php b/plugins/Irc/extlib/phergie/Phergie/Driver/Streams.php new file mode 100755 index 000000000..73c0230c7 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Driver/Streams.php @@ -0,0 +1,729 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Driver that uses the sockets wrapper of the streams extension for + * communicating with the server and handles formatting and parsing of + * events using PHP. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Driver_Streams extends Phergie_Driver_Abstract +{ + /** + * Socket handlers + * + * @var array + */ + protected $sockets = array(); + + /** + * Reference to the currently active socket handler + * + * @var resource + */ + protected $socket; + + /** + * Amount of time in seconds to wait to receive an event each time the + * socket is polled + * + * @var float + */ + protected $timeout = 0.1; + + /** + * Handles construction of command strings and their transmission to the + * server. + * + * @param string $command Command to send + * @param string|array $args Optional string or array of sequential + * arguments + * + * @return string Command string that was sent + * @throws Phergie_Driver_Exception + */ + protected function send($command, $args = '') + { + $connection = $this->getConnection(); + $encoding = $connection->getEncoding(); + + // Require an open socket connection to continue + if (empty($this->socket)) { + throw new Phergie_Driver_Exception( + 'doConnect() must be called first', + Phergie_Driver_Exception::ERR_NO_INITIATED_CONNECTION + ); + } + + // Add the command + $buffer = strtoupper($command); + + // Add arguments + if (!empty($args)) { + + // Apply formatting if arguments are passed in as an array + if (is_array($args)) { + $end = count($args) - 1; + $args[$end] = ':' . $args[$end]; + $args = implode(' ', $args); + } else { + $args = ':' . $args; + } + + $buffer .= ' ' . $args; + } + + // Transmit the command over the socket connection + $attempts = $written = 0; + $temp = $buffer . "\r\n"; + $is_multibyte = !substr($encoding, 0, 8) === 'ISO-8859' && $encoding !== 'ASCII' && $encoding !== 'CP1252'; + $length = ($is_multibyte) ? mb_strlen($buffer, '8bit') : strlen($buffer); + while (true) { + $written += (int) fwrite($this->socket, $temp); + if ($written < $length) { + $temp = substr($temp, $written); + $attempts++; + if ($attempts == 3) { + throw new Phergie_Driver_Exception( + 'Unable to write to socket', + Phergie_Driver_Exception::ERR_CONNECTION_WRITE_FAILED + ); + } + } else { + break; + } + } + + // Return the command string that was transmitted + return $buffer; + } + + /** + * Overrides the parent class to set the currently active socket handler + * when the active connection is changed. + * + * @param Phergie_Connection $connection Active connection + * + * @return Phergie_Driver_Streams Provides a fluent interface + */ + public function setConnection(Phergie_Connection $connection) + { + // Set the active socket handler + $hostmask = (string) $connection->getHostmask(); + if (!empty($this->sockets[$hostmask])) { + $this->socket = $this->sockets[$hostmask]; + } + + // Set the active connection + return parent::setConnection($connection); + } + + /** + * Returns a list of hostmasks corresponding to sockets with data to read. + * + * @param int $sec Length of time to wait for new data (seconds) + * @param int $usec Length of time to wait for new data (microseconds) + * + * @return array List of hostmasks or an empty array if none were found + * to have data to read + */ + public function getActiveReadSockets($sec = 0, $usec = 200000) + { + $read = $this->sockets; + $write = null; + $error = null; + $active = array(); + + if (count($this->sockets) > 0) { + $number = stream_select($read, $write, $error, $sec, $usec); + if ($number > 0) { + foreach ($read as $item) { + $active[] = array_search($item, $this->sockets); + } + } + } + + return $active; + } + + /** + * Sets the amount of time to wait for a new event each time the socket + * is polled. + * + * @param float $timeout Amount of time in seconds + * + * @return Phergie_Driver_Streams Provides a fluent interface + */ + public function setTimeout($timeout) + { + $timeout = (float) $timeout; + if ($timeout) { + $this->timeout = $timeout; + } + return $this; + } + + /** + * Returns the amount of time to wait for a new event each time the + * socket is polled. + * + * @return float Amount of time in seconds + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Supporting method to parse event argument strings where the last + * argument may contain a colon. + * + * @param string $args Argument string to parse + * @param int $count Optional maximum number of arguments + * + * @return array Array of argument values + */ + protected function parseArguments($args, $count = -1) + { + return preg_split('/ :?/S', $args, $count); + } + + /** + * Listens for an event on the current connection. + * + * @return Phergie_Event_Interface|null Event instance if an event was + * received, NULL otherwise + */ + public function getEvent() + { + // Check the socket is still active + if (feof($this->socket)) { + throw new Phergie_Driver_Exception( + 'EOF detected on socket', + Phergie_Driver_Exception::ERR_CONNECTION_READ_FAILED + ); + } + + // Check for a new event on the current connection + $buffer = fgets($this->socket, 512); + + // If no new event was found, return NULL + if (empty($buffer)) { + return null; + } + + // Strip the trailing newline from the buffer + $buffer = rtrim($buffer); + + // If the event is from the server... + if (substr($buffer, 0, 1) != ':') { + + // Parse the command and arguments + list($cmd, $args) = array_pad(explode(' ', $buffer, 2), 2, null); + $hostmask = new Phergie_Hostmask(null, null, $this->connection->getHost()); + + } else { + // If the event could be from the server or a user... + + // Parse the server hostname or user hostmask, command, and arguments + list($prefix, $cmd, $args) + = array_pad(explode(' ', ltrim($buffer, ':'), 3), 3, null); + if (strpos($prefix, '@') !== false) { + $hostmask = Phergie_Hostmask::fromString($prefix); + } else { + $hostmask = new Phergie_Hostmask(null, null, $prefix); + } + } + + // Parse the event arguments depending on the event type + $cmd = strtolower($cmd); + switch ($cmd) { + case 'names': + case 'nick': + case 'quit': + case 'ping': + case 'join': + case 'error': + $args = array(ltrim($args, ':')); + break; + + case 'privmsg': + case 'notice': + $args = $this->parseArguments($args, 2); + list($source, $ctcp) = $args; + if (substr($ctcp, 0, 1) === "\001" && substr($ctcp, -1) === "\001") { + $ctcp = substr($ctcp, 1, -1); + $reply = ($cmd == 'notice'); + list($cmd, $args) = array_pad(explode(' ', $ctcp, 2), 2, null); + $cmd = strtolower($cmd); + switch ($cmd) { + case 'version': + case 'time': + case 'finger': + if ($reply) { + $args = $ctcp; + } + break; + case 'ping': + if ($reply) { + $cmd .= 'Response'; + } else { + $cmd = 'ctcpPing'; + } + break; + case 'action': + $args = array($source, $args); + break; + + default: + $cmd = 'ctcp'; + if ($reply) { + $cmd .= 'Response'; + } + $args = array($source, $args); + break; + } + } + break; + + case 'oper': + case 'topic': + case 'mode': + $args = $this->parseArguments($args); + break; + + case 'part': + case 'kill': + case 'invite': + $args = $this->parseArguments($args, 2); + break; + + case 'kick': + $args = $this->parseArguments($args, 3); + break; + + // Remove the target from responses + default: + $args = substr($args, strpos($args, ' ') + 1); + break; + } + + // Create, populate, and return an event object + if (ctype_digit($cmd)) { + $event = new Phergie_Event_Response; + $event + ->setCode($cmd) + ->setDescription($args); + } else { + $event = new Phergie_Event_Request; + $event + ->setType($cmd) + ->setArguments($args); + if (isset($hostmask)) { + $event->setHostmask($hostmask); + } + } + $event->setRawData($buffer); + return $event; + } + + /** + * Initiates a connection with the server. + * + * @return void + */ + public function doConnect() + { + // Listen for input indefinitely + set_time_limit(0); + + // Get connection information + $connection = $this->getConnection(); + $hostname = $connection->getHost(); + $port = $connection->getPort(); + $password = $connection->getPassword(); + $username = $connection->getUsername(); + $nick = $connection->getNick(); + $realname = $connection->getRealname(); + $transport = $connection->getTransport(); + + // Establish and configure the socket connection + $remote = $transport . '://' . $hostname . ':' . $port; + $this->socket = @stream_socket_client($remote, $errno, $errstr); + if (!$this->socket) { + throw new Phergie_Driver_Exception( + 'Unable to connect: socket error ' . $errno . ' ' . $errstr, + Phergie_Driver_Exception::ERR_CONNECTION_ATTEMPT_FAILED + ); + } + + $seconds = (int) $this->timeout; + $microseconds = ($this->timeout - $seconds) * 1000000; + stream_set_timeout($this->socket, $seconds, $microseconds); + + // Send the password if one is specified + if (!empty($password)) { + $this->send('PASS', $password); + } + + // Send user information + $this->send( + 'USER', + array( + $username, + $hostname, + $hostname, + $realname + ) + ); + + $this->send('NICK', $nick); + + // Add the socket handler to the internal array for socket handlers + $this->sockets[(string) $connection->getHostmask()] = $this->socket; + } + + /** + * Terminates the connection with the server. + * + * @param string $reason Reason for connection termination (optional) + * + * @return void + */ + public function doQuit($reason = null) + { + // Send a QUIT command to the server + $this->send('QUIT', $reason); + + // Terminate the socket connection + fclose($this->socket); + + // Remove the socket from the internal socket list + unset($this->sockets[(string) $this->getConnection()->getHostmask()]); + } + + /** + * Joins a channel. + * + * @param string $channels Comma-delimited list of channels to join + * @param string $keys Optional comma-delimited list of channel keys + * + * @return void + */ + public function doJoin($channels, $keys = null) + { + $args = array($channels); + + if (!empty($keys)) { + $args[] = $keys; + } + + $this->send('JOIN', $args); + } + + /** + * Leaves a channel. + * + * @param string $channels Comma-delimited list of channels to leave + * + * @return void + */ + public function doPart($channels) + { + $this->send('PART', $channels); + } + + /** + * Invites a user to an invite-only channel. + * + * @param string $nick Nick of the user to invite + * @param string $channel Name of the channel + * + * @return void + */ + public function doInvite($nick, $channel) + { + $this->send('INVITE', array($nick, $channel)); + } + + /** + * Obtains a list of nicks of usrs in currently joined channels. + * + * @param string $channels Comma-delimited list of one or more channels + * + * @return void + */ + public function doNames($channels) + { + $this->send('NAMES', $channels); + } + + /** + * Obtains a list of channel names and topics. + * + * @param string $channels Comma-delimited list of one or more channels + * to which the response should be restricted + * (optional) + * + * @return void + */ + public function doList($channels = null) + { + $this->send('LIST', $channels); + } + + /** + * Retrieves or changes a channel topic. + * + * @param string $channel Name of the channel + * @param string $topic New topic to assign (optional) + * + * @return void + */ + public function doTopic($channel, $topic = null) + { + $args = array($channel); + + if (!empty($topic)) { + $args[] = $topic; + } + + $this->send('TOPIC', $args); + } + + /** + * Retrieves or changes a channel or user mode. + * + * @param string $target Channel name or user nick + * @param string $mode New mode to assign (optional) + * + * @return void + */ + public function doMode($target, $mode = null) + { + $args = array($target); + + if (!empty($mode)) { + $args[] = $mode; + } + + $this->send('MODE', $args); + } + + /** + * Changes the client nick. + * + * @param string $nick New nick to assign + * + * @return void + */ + public function doNick($nick) + { + $this->send('NICK', $nick); + } + + /** + * Retrieves information about a nick. + * + * @param string $nick Nick + * + * @return void + */ + public function doWhois($nick) + { + $this->send('WHOIS', $nick); + } + + /** + * Sends a message to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the message to send + * + * @return void + */ + public function doPrivmsg($target, $text) + { + $this->send('PRIVMSG', array($target, $text)); + } + + /** + * Sends a notice to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the notice to send + * + * @return void + */ + public function doNotice($target, $text) + { + $this->send('NOTICE', array($target, $text)); + } + + /** + * Kicks a user from a channel. + * + * @param string $nick Nick of the user + * @param string $channel Channel name + * @param string $reason Reason for the kick (optional) + * + * @return void + */ + public function doKick($nick, $channel, $reason = null) + { + $args = array($nick, $channel); + + if (!empty($reason)) { + $args[] = $response; + } + + $this->send('KICK', $args); + } + + /** + * Responds to a server test of client responsiveness. + * + * @param string $daemon Daemon from which the original request originates + * + * @return void + */ + public function doPong($daemon) + { + $this->send('PONG', $daemon); + } + + /** + * Sends a CTCP ACTION (/me) command to a nick or channel. + * + * @param string $target Channel name or user nick + * @param string $text Text of the action to perform + * + * @return void + */ + public function doAction($target, $text) + { + $buffer = rtrim('ACTION ' . $text); + + $this->doPrivmsg($target, chr(1) . $buffer . chr(1)); + } + + /** + * Sends a CTCP response to a user. + * + * @param string $nick User nick + * @param string $command Command to send + * @param string|array $args String or array of sequential arguments + * (optional) + * + * @return void + */ + protected function doCtcp($nick, $command, $args = null) + { + if (is_array($args)) { + $args = implode(' ', $args); + } + + $buffer = rtrim(strtoupper($command) . ' ' . $args); + + $this->doNotice($nick, chr(1) . $buffer . chr(1)); + } + + /** + * Sends a CTCP PING request or response (they are identical) to a user. + * + * @param string $nick User nick + * @param string $hash Hash to use in the handshake + * + * @return void + */ + public function doPing($nick, $hash) + { + $this->doCtcp($nick, 'PING', $hash); + } + + /** + * Sends a CTCP VERSION request or response to a user. + * + * @param string $nick User nick + * @param string $version Version string to send for a response + * + * @return void + */ + public function doVersion($nick, $version = null) + { + if ($version) { + $this->doCtcp($nick, 'VERSION', $version); + } else { + $this->doCtcp($nick, 'VERSION'); + } + } + + /** + * Sends a CTCP TIME request to a user. + * + * @param string $nick User nick + * @param string $time Time string to send for a response + * + * @return void + */ + public function doTime($nick, $time = null) + { + if ($time) { + $this->doCtcp($nick, 'TIME', $time); + } else { + $this->doCtcp($nick, 'TIME'); + } + } + + /** + * Sends a CTCP FINGER request to a user. + * + * @param string $nick User nick + * @param string $finger Finger string to send for a response + * + * @return void + */ + public function doFinger($nick, $finger = null) + { + if ($finger) { + $this->doCtcp($nick, 'FINGER', $finger); + } else { + $this->doCtcp($nick, 'FINGER'); + } + } + + /** + * Sends a raw command to the server. + * + * @param string $command Command string to send + * + * @return void + */ + public function doRaw($command) + { + $this->send('RAW', $command); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Event/Abstract.php new file mode 100644 index 000000000..54b035dc0 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Abstract.php @@ -0,0 +1,62 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for events. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +abstract class Phergie_Event_Abstract +{ + /** + * Event type, used for determining the callback to execute in response + * + * @var string + */ + protected $type; + + /** + * Returns the event type. + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Sets the event type. + * + * @param string $type Event type + * + * @return Phergie_Event_Abstract Implements a fluent interface + */ + public function setType($type) + { + $this->type = (string) $type; + return $this; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Command.php b/plugins/Irc/extlib/phergie/Phergie/Event/Command.php new file mode 100644 index 000000000..5940636ba --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Command.php @@ -0,0 +1,62 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Event originating from a plugin for the bot. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Event_Command extends Phergie_Event_Request +{ + /** + * Reference to the plugin instance that created the event + * + * @var Phergie_Plugin_Abstract + */ + protected $plugin; + + /** + * Stores a reference to the plugin instance that created the event. + * + * @param Phergie_Plugin_Abstract $plugin Plugin instance + * + * @return Phergie_Event_Command Provides a fluent interface + */ + public function setPlugin(Phergie_Plugin_Abstract $plugin) + { + $this->plugin = $plugin; + return $this; + } + + /** + * Returns a reference to the plugin instance that created the event. + * + * @return Phergie_Plugin_Abstract + */ + public function getPlugin() + { + return $this->plugin; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Event/Exception.php new file mode 100644 index 000000000..6b094a810 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Exception.php @@ -0,0 +1,38 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to outgoing events. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Event_Exception extends Phergie_Exception +{ + /** + * Error indicating that an attempt was made to create an event of an + * unknown type + */ + const ERR_UNKNOWN_EVENT_TYPE = 1; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Handler.php b/plugins/Irc/extlib/phergie/Phergie/Event/Handler.php new file mode 100644 index 000000000..e308df8a5 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Handler.php @@ -0,0 +1,190 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Handles events initiated by plugins. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Event_Handler implements IteratorAggregate, Countable +{ + /** + * Current queue of events + * + * @var array + */ + protected $events; + + /** + * Constructor to initialize the event queue. + * + * @return void + */ + public function __construct() + { + $this->events = array(); + } + + /** + * Adds an event to the queue. + * + * @param Phergie_Plugin_Abstract $plugin Plugin originating the event + * @param string $type Event type, corresponding to a + * Phergie_Event_Command::TYPE_* constant + * @param array $args Optional event arguments + * + * @return Phergie_Event_Handler Provides a fluent interface + */ + public function addEvent(Phergie_Plugin_Abstract $plugin, $type, + array $args = array() + ) { + if (!defined('Phergie_Event_Command::TYPE_' . strtoupper($type))) { + throw new Phergie_Event_Exception( + 'Unknown event type "' . $type . '"', + Phergie_Event_Exception::ERR_UNKNOWN_EVENT_TYPE + ); + } + + $event = new Phergie_Event_Command; + $event + ->setPlugin($plugin) + ->setType($type) + ->setArguments($args); + + $this->events[] = $event; + + return $this; + } + + /** + * Returns the current event queue. + * + * @return array Enumerated array of Phergie_Event_Command objects + */ + public function getEvents() + { + return $this->events; + } + + /** + * Clears the event queue. + * + * @return Phergie_Event_Handler Provides a fluent interface + */ + public function clearEvents() + { + $this->events = array(); + return $this; + } + + /** + * Replaces the current event queue with a given queue of events. + * + * @param array $events Ordered list of objects of the class + * Phergie_Event_Command + * + * @return Phergie_Event_Handler Provides a fluent interface + */ + public function replaceEvents(array $events) + { + $this->events = $events; + return $this; + } + + /** + * Returns whether an event of the given type exists in the queue. + * + * @param string $type Event type from Phergie_Event_Request::TYPE_* + * constants + * + * @return bool TRUE if an event of the specified type exists in the + * queue, FALSE otherwise + */ + public function hasEventOfType($type) + { + foreach ($this->events as $event) { + if ($event->getType() == $type) { + return true; + } + } + return false; + } + + /** + * Returns a list of events of a specified type. + * + * @param string $type Event type from Phergie_Event_Request::TYPE_* + * constants + * + * @return array Array containing event instances of the specified type + * or an empty array if no such events were found + */ + public function getEventsOfType($type) + { + $events = array(); + foreach ($this->events as $event) { + if ($event->getType() == $type) { + $events[] = $event; + } + } + return $events; + } + + /** + * Removes a single event from the event queue. + * + * @param Phergie_Event_Command $event Event to remove + * + * @return Phergie_Event_Handler Provides a fluent interface + */ + public function removeEvent(Phergie_Event_Command $event) + { + $key = array_search($event, $this->events); + if ($key !== false) { + unset($this->events[$key]); + } + return $this; + } + + /** + * Returns an iterator for the current event queue. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->events); + } + + /** + * Returns the number of events in the event queue + * + * @return int number of queued events + */ + public function count() + { + return count($this->events); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Request.php b/plugins/Irc/extlib/phergie/Phergie/Event/Request.php new file mode 100755 index 000000000..647b5acb8 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Request.php @@ -0,0 +1,468 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Autonomous event originating from a user or the server. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + * @link http://www.irchelp.org/irchelp/rfc/chapter4.html + */ +class Phergie_Event_Request + extends Phergie_Event_Abstract + implements ArrayAccess +{ + /** + * Nick message event type + */ + const TYPE_NICK = 'nick'; + + /** + * Whois message event type + */ + const TYPE_WHOIS = 'whois'; + + /** + * Quit command event type + */ + const TYPE_QUIT = 'quit'; + + /** + * Join message event type + */ + const TYPE_JOIN = 'join'; + + /** + * Kick message event type + */ + const TYPE_KICK = 'kick'; + + /** + * Part message event type + */ + const TYPE_PART = 'part'; + + /** + * Invite message event type + */ + const TYPE_INVITE = 'invite'; + + /** + * Mode message event type + */ + const TYPE_MODE = 'mode'; + + /** + * Topic message event type + */ + const TYPE_TOPIC = 'topic'; + + /** + * Private message command event type + */ + const TYPE_PRIVMSG = 'privmsg'; + + /** + * Notice message event type + */ + const TYPE_NOTICE = 'notice'; + + /** + * Pong message event type + */ + const TYPE_PONG = 'pong'; + + /** + * CTCP ACTION command event type + */ + const TYPE_ACTION = 'action'; + + /** + * CTCP PING command event type + */ + const TYPE_PING = 'ping'; + + /** + * CTCP TIME command event type + */ + const TYPE_TIME = 'time'; + + /** + * CTCP VERSION command event type + */ + const TYPE_VERSION = 'version'; + + /** + * RAW message event type + */ + const TYPE_RAW = 'raw'; + + /** + * Mapping of event types to their named parameters + * + * @var array + */ + protected static $map = array( + + self::TYPE_QUIT => array( + 'message' => 0 + ), + + self::TYPE_JOIN => array( + 'channel' => 0 + ), + + self::TYPE_KICK => array( + 'channel' => 0, + 'user' => 1, + 'comment' => 2 + ), + + self::TYPE_PART => array( + 'channel' => 0, + 'message' => 1 + ), + + self::TYPE_INVITE => array( + 'nickname' => 0, + 'channel' => 1 + ), + + self::TYPE_MODE => array( + 'target' => 0, + 'mode' => 1, + 'limit' => 2, + 'user' => 3, + 'banmask' => 4 + ), + + self::TYPE_TOPIC => array( + 'channel' => 0, + 'topic' => 1 + ), + + self::TYPE_PRIVMSG => array( + 'receiver' => 0, + 'text' => 1 + ), + + self::TYPE_NOTICE => array( + 'nickname' => 0, + 'text' => 1 + ), + + self::TYPE_ACTION => array( + 'target' => 0, + 'action' => 1 + ), + + self::TYPE_RAW => array( + 'message' => 0 + ) + + ); + + /** + * Hostmask representing the originating user, if applicable + * + * @var Phergie_Hostmask + */ + protected $hostmask; + + /** + * Arguments included with the message + * + * @var array + */ + protected $arguments; + + /** + * Raw data sent by the server + * + * @var string + */ + protected $rawData; + + /** + * Sets the hostmask representing the originating user. + * + * @param Phergie_Hostmask $hostmask User hostmask + * + * @return Phergie_Event_Request Provides a fluent interface + */ + public function setHostmask(Phergie_Hostmask $hostmask) + { + $this->hostmask = $hostmask; + return $this; + } + + /** + * Returns the hostmask representing the originating user. + * + * @return Phergie_Event_Request|null Hostmask or NULL if none was set + */ + public function getHostmask() + { + return $this->hostmask; + } + + /** + * Sets the arguments for the request. + * + * @param array $arguments Request arguments + * + * @return Phergie_Event_Request Provides a fluent interface + */ + public function setArguments($arguments) + { + $this->arguments = $arguments; + return $this; + } + + /** + * Sets the value of a single argument for the request. + * + * @param mixed $argument Integer position (starting from 0) or the + * equivalent string name of the argument from self::$map + * @param string $value Value to assign to the argument + * + * @return Phergie_Event_Request Provides a fluent interface + */ + public function setArgument($argument, $value) + { + $argument = $this->resolveArgument($argument); + if ($argument !== null) { + $this->arguments[$argument] = (string) $value; + } + return $this; + } + + /** + * Returns the arguments for the request. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Resolves an argument specification to an integer position. + * + * @param mixed $argument Integer position (starting from 0) or the + * equivalent string name of the argument from self::$map + * + * @return int|null Integer position of the argument or NULL if no + * corresponding argument was found + */ + protected function resolveArgument($argument) + { + if (isset($this->arguments[$argument])) { + return $argument; + } else { + $argument = strtolower($argument); + if (isset(self::$map[$this->type][$argument]) + && isset($this->arguments[self::$map[$this->type][$argument]]) + ) { + return self::$map[$this->type][$argument]; + } + } + return null; + } + + /** + * Returns a single specified argument for the request. + * + * @param mixed $argument Integer position (starting from 0) or the + * equivalent string name of the argument from self::$map + * + * @return string|null Argument value or NULL if none is set + */ + public function getArgument($argument) + { + $argument = $this->resolveArgument($argument); + if ($argument !== null) { + return $this->arguments[$argument]; + } + return null; + } + + /** + * Sets the raw buffer for the event. + * + * @param string $buffer Raw event buffer + * + * @return Phergie_Event_Request Provides a fluent interface + */ + public function setRawData($buffer) + { + $this->rawData = $buffer; + return $this; + } + + /** + * Returns the raw buffer sent from the server for the event. + * + * @return string + */ + public function getRawData() + { + return $this->rawData; + } + + /** + * Returns the nick of the user who originated the event. + * + * @return string + */ + public function getNick() + { + return $this->hostmask->getNick(); + } + + /** + * Returns the channel name if the event occurred in a channel or the + * user nick if the event was a private message directed at the bot by a + * user. + * + * @return string + */ + public function getSource() + { + if (substr($this->arguments[0], 0, 1) == '#') { + return $this->arguments[0]; + } + return $this->hostmask->getNick(); + } + + /** + * Returns whether or not the event occurred within a channel. + * + * @return TRUE if the event is in a channel, FALSE otherwise + */ + public function isInChannel() + { + return (substr($this->getSource(), 0, 1) == '#'); + } + + /** + * Returns whether or not the event originated from a user. + * + * @return TRUE if the event is from a user, FALSE otherwise + */ + public function isFromUser() + { + if (empty($this->hostmask)) { + return false; + } + $username = $this->hostmask->getUsername(); + return !empty($username); + } + + /** + * Returns whether or not the event originated from the server. + * + * @return TRUE if the event is from the server, FALSE otherwise + */ + public function isFromServer() + { + $username = $this->hostmask->getUsername(); + return empty($username); + } + + /** + * Provides access to named parameters via virtual "getter" methods. + * + * @param string $name Name of the method called + * @param array $arguments Arguments passed to the method (should always + * be empty) + * + * @return mixed Method return value + */ + public function __call($name, array $arguments) + { + if (!count($arguments) && substr($name, 0, 3) == 'get') { + return $this->getArgument(substr($name, 3)); + } + } + + /** + * Checks to see if an event argument is assigned a value. + * + * @param string|int $offset Argument name or position beginning from 0 + * + * @return bool TRUE if the argument has a value, FALSE otherwise + * @see ArrayAccess::offsetExists() + */ + public function offsetExists($offset) + { + return ($this->resolveArgument($offset) !== null); + } + + /** + * Returns the value of an event argument. + * + * @param string|int $offset Argument name or position beginning from 0 + * + * @return string|null Argument value or NULL if none is set + * @see ArrayAccess::offsetGet() + */ + public function offsetGet($offset) + { + return $this->getArgument($offset); + } + + /** + * Sets the value of an event argument. + * + * @param string|int $offset Argument name or position beginning from 0 + * @param string $value New argument value + * + * @return void + * @see ArrayAccess::offsetSet() + */ + public function offsetSet($offset, $value) + { + $offset = $this->resolveArgument($offset); + if ($offset !== null) { + $this->arguments[$offset] = $value; + } + } + + /** + * Removes the value set for an event argument. + * + * @param string|int $offset Argument name or position beginning from 0 + * + * @return void + * @see ArrayAccess::offsetUnset() + */ + public function offsetUnset($offset) + { + if ($offset = $this->resolveArgument($offset)) { + unset($this->arguments[$offset]); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Event/Response.php b/plugins/Irc/extlib/phergie/Phergie/Event/Response.php new file mode 100755 index 000000000..097e2535e --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Event/Response.php @@ -0,0 +1,953 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Event originating from the server in response to an event sent by the + * current client. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + * @link http://www.irchelp.org/irchelp/rfc/chapter6.html + */ +class Phergie_Event_Response extends Phergie_Event_Abstract +{ + /** + * <nickname> No such nick/channel + * + * Used to indicate the nickname parameter supplied to a command is currently + * unused. + */ + const ERR_NOSUCHNICK = '401'; + + /** + * <server name> No such server + * + * Used to indicate the server name given currently doesn't exist. + */ + const ERR_NOSUCHSERVER = '402'; + + /** + * <channel name> No such channel + * + * Used to indicate the given channel name is invalid. + */ + const ERR_NOSUCHCHANNEL = '403'; + + /** + * <channel name> Cannot send to channel + * + * Sent to a user who is either (a) not on a channel which is mode +n or (b) not + * a chanop (or mode +v) on a channel which has mode +m set and is trying to send + * a PRIVMSG message to that channel. + */ + const ERR_CANNOTSENDTOCHAN = '404'; + + /** + * <channel name> You have joined too many channels + * + * Sent to a user when they have joined the maximum number of allowed channels + * and they try to join another channel. + */ + const ERR_TOOMANYCHANNELS = '405'; + + /** + * <nickname> There was no such nickname + * + * Returned by WHOWAS to indicate there is no history information for that + * nickname. + */ + const ERR_WASNOSUCHNICK = '406'; + + /** + * <target> Duplicate recipients. No message delivered + * + * Returned to a client which is attempting to send PRIVMSG/NOTICE using the + * user@host destination format and for a user@host which has several + * occurrences. + */ + const ERR_TOOMANYTARGETS = '407'; + + /** + * No origin specified + * + * PING or PONG message missing the originator parameter which is required since + * these commands must work without valid prefixes. + */ + const ERR_NOORIGIN = '409'; + + /** + * No recipient given (<command>) + */ + const ERR_NORECIPIENT = '411'; + + /** + * No text to send + */ + const ERR_NOTEXTTOSEND = '412'; + + /** + * <mask> No toplevel domain specified + */ + const ERR_NOTOPLEVEL = '413'; + + /** + * <mask> Wildcard in toplevel domain + * + * 412 - 414 are returned by PRIVMSG to indicate that the message wasn't + * delivered for some reason. ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that + * are returned when an invalid use of "PRIVMSG $<server>" or "PRIVMSG #<host>" + * is attempted. + */ + const ERR_WILDTOPLEVEL = '414'; + + /** + * <command> Unknown command + * + * Returned to a registered client to indicate that the command sent is unknown + * by the server. + */ + const ERR_UNKNOWNCOMMAND = '421'; + + /** + * MOTD File is missing + * + * Server's MOTD file could not be opened by the server. + */ + const ERR_NOMOTD = '422'; + + /** + * <server> No administrative info available + * + * Returned by a server in response to an ADMIN message when there is an error in + * finding the appropriate information. + */ + const ERR_NOADMININFO = '423'; + + /** + * File error doing <file op> on <file> + * + * Generic error message used to report a failed file operation during the + * processing of a message. + */ + const ERR_FILEERROR = '424'; + + /** + * No nickname given + * + * Returned when a nickname parameter expected for a command and isn't found. + */ + const ERR_NONICKNAMEGIVEN = '431'; + + /** + * <nick> Erroneus nickname + * + * Returned after receiving a NICK message which contains characters which do not + * fall in the defined set. See section x.x.x for details on valid nicknames. + */ + const ERR_ERRONEUSNICKNAME = '432'; + + /** + * <nick> Nickname is already in use + * + * Returned when a NICK message is processed that results in an attempt to change + * to a currently existing nickname. + */ + const ERR_NICKNAMEINUSE = '433'; + + /** + * <nick> Nickname collision KILL + * + * Returned by a server to a client when it detects a nickname collision + * (registered of a NICK that already exists by another server). + */ + const ERR_NICKCOLLISION = '436'; + + /** + * <nick> <channel> They aren't on that channel + * + * Returned by the server to indicate that the target user of the command is not + * on the given channel. + */ + const ERR_USERNOTINCHANNEL = '441'; + + /** + * <channel> You're not on that channel + * + * Returned by the server whenever a client tries to perform a channel effecting + * command for which the client isn't a member. + */ + const ERR_NOTONCHANNEL = '442'; + + /** + * <user> <channel> is already on channel + * + * Returned when a client tries to invite a user to a channel they are already + * on. + */ + const ERR_USERONCHANNEL = '443'; + + /** + * <user> User not logged in + * + * Returned by the summon after a SUMMON command for a user was unable to be + * performed since they were not logged in. + */ + const ERR_NOLOGIN = '444'; + + /** + * SUMMON has been disabled + * + * Returned as a response to the SUMMON command. Must be returned by any server + * which does not implement it. + */ + const ERR_SUMMONDISABLED = '445'; + + /** + * USERS has been disabled + * + * Returned as a response to the USERS command. Must be returned by any server + * which does not implement it. + */ + const ERR_USERSDISABLED = '446'; + + /** + * You have not registered + * + * Returned by the server to indicate that the client must be registered before + * the server will allow it to be parsed in detail. + */ + const ERR_NOTREGISTERED = '451'; + + /** + * <command> Not enough parameters + * + * Returned by the server by numerous commands to indicate to the client that it + * didn't supply enough parameters. + */ + const ERR_NEEDMOREPARAMS = '461'; + + /** + * You may not reregister + * + * Returned by the server to any link which tries to change part of the + * registered details (such as password or user details from second USER + * message). + */ + const ERR_ALREADYREGISTRED = '462'; + + /** + * Your host isn't among the privileged + * + * Returned to a client which attempts to register with a server which does not + * been setup to allow connections from the host the attempted connection is + * tried. + */ + const ERR_NOPERMFORHOST = '463'; + + /** + * Password incorrect + * + * Returned to indicate a failed attempt at registering a connection for which a + * password was required and was either not given or incorrect. + */ + const ERR_PASSWDMISMATCH = '464'; + + /** + * You are banned from this server + * + * Returned after an attempt to connect and register yourself with a server which + * has been setup to explicitly deny connections to you. + */ + const ERR_YOUREBANNEDCREEP = '465'; + + /** + * <channel> Channel key already set + */ + const ERR_KEYSET = '467'; + + /** + * <channel> Cannot join channel (+l) + */ + const ERR_CHANNELISFULL = '471'; + + /** + * <char> is unknown mode char to me + */ + const ERR_UNKNOWNMODE = '472'; + + /** + * <channel> Cannot join channel (+i) + */ + const ERR_INVITEONLYCHAN = '473'; + + /** + * <channel> Cannot join channel (+b) + */ + const ERR_BANNEDFROMCHAN = '474'; + + /** + * <channel> Cannot join channel (+k) + */ + const ERR_BADCHANNELKEY = '475'; + + /** + * Permission Denied- You're not an IRC operator + * + * Any command requiring operator privileges to operate must return this error to + * indicate the attempt was unsuccessful. + */ + const ERR_NOPRIVILEGES = '481'; + + /** + * <channel> You're not channel operator + * + * Any command requiring 'chanop' privileges (such as MODE messages) must return + * this error if the client making the attempt is not a chanop on the specified + * channel. + */ + const ERR_CHANOPRIVSNEEDED = '482'; + + /** + * You cant kill a server! + * + * Any attempts to use the KILL command on a server are to be refused and this + * error returned directly to the client. + */ + const ERR_CANTKILLSERVER = '483'; + + /** + * No O-lines for your host + * + * If a client sends an OPER message and the server has not been configured to + * allow connections from the client's host as an operator, this error must be + * returned. + */ + const ERR_NOOPERHOST = '491'; + + /** + * Unknown MODE flag + * + * Returned by the server to indicate that a MODE message was sent with a + * nickname parameter and that the a mode flag sent was not recognized. + */ + const ERR_UMODEUNKNOWNFLAG = '501'; + + /** + * Cant change mode for other users + * + * Error sent to any user trying to view or change the user mode for a user other + * than themselves. + */ + const ERR_USERSDONTMATCH = '502'; + + /** + * Dummy reply number. Not used. + */ + const RPL_NONE = '300'; + + /** + * [<reply>{<space><reply>}] + * + * Reply format used by USERHOST to list replies to the query list. The reply + * string is composed as follows <reply> = <nick>['*'] '=' <'+'|'-'><hostname> + * The '*' indicates whether the client has registered as an Operator. The '-' or + * '+' characters represent whether the client has set an AWAY message or not + * respectively. + */ + const RPL_USERHOST = '302'; + + /** + * [<nick> {<space><nick>}] + * + * Reply format used by ISON to list replies to the query list. + */ + const RPL_ISON = '303'; + + /** + * <nick> <away message> + */ + const RPL_AWAY = '301'; + + /** + * You are no longer marked as being away + */ + const RPL_UNAWAY = '305'; + + /** + * You have been marked as being away + * + * These replies are used with the AWAY command (if allowed). RPL_AWAY is sent to + * any client sending a PRIVMSG to a client which is away. RPL_AWAY is only sent + * by the server to which the client is connected. Replies RPL_UNAWAY and + * RPL_NOWAWAY are sent when the client removes and sets an AWAY message. + */ + const RPL_NOWAWAY = '306'; + + /** + * <nick> <user> <host> * <real name> + */ + const RPL_WHOISUSER = '311'; + + /** + * <nick> <server> <server info> + */ + const RPL_WHOISSERVER = '312'; + + /** + * <nick> is an IRC operator + */ + const RPL_WHOISOPERATOR = '313'; + + /** + * <nick> <integer> seconds idle + */ + const RPL_WHOISIDLE = '317'; + + /** + * <nick> End of /WHOIS list + */ + const RPL_ENDOFWHOIS = '318'; + + /** + * <nick> {[@|+]<channel><space>} + * + * Replies 311 - 313, 317 - 319 are all replies generated in response to a WHOIS + * message. Given that there are enough parameters present, the answering server + * must either formulate a reply out of the above numerics (if the query nick is + * found) or return an error reply. The '*' in RPL_WHOISUSER is there as the + * literal character and not as a wild card. For each reply set, only + * RPL_WHOISCHANNELS may appear more than once (for long lists of channel names). + * The '@' and '+' characters next to the channel name indicate whether a client + * is a channel operator or has been granted permission to speak on a moderated + * channel. The RPL_ENDOFWHOIS reply is used to mark the end of processing a + * WHOIS message. + */ + const RPL_WHOISCHANNELS = '319'; + + /** + * <nick> <user> <host> * <real name> + */ + const RPL_WHOWASUSER = '314'; + + /** + * <nick> End of WHOWAS + * + * When replying to a WHOWAS message, a server must use the replies + * RPL_WHOWASUSER, RPL_WHOISSERVER or ERR_WASNOSUCHNICK for each nickname in the + * presented list. At the end of all reply batches, there must be RPL_ENDOFWHOWAS + * (even if there was only one reply and it was an error). + */ + const RPL_ENDOFWHOWAS = '369'; + + /** + * Channel Users Name + */ + const RPL_LISTSTART = '321'; + + /** + * <channel> <# visible> <topic> + */ + const RPL_LIST = '322'; + + /** + * End of /LIST + * + * Replies RPL_LISTSTART, RPL_LIST, RPL_LISTEND mark the start, actual replies + * with data and end of the server's response to a LIST command. If there are no + * channels available to return, only the start and end reply must be sent. + */ + const RPL_LISTEND = '323'; + + /** + * <channel> <mode> <mode params> + */ + const RPL_CHANNELMODEIS = '324'; + + /** + * <channel> No topic is set + */ + const RPL_NOTOPIC = '331'; + + /** + * <channel> <topic> + * + * When sending a TOPIC message to determine the channel topic, one of two + * replies is sent. If the topic is set, RPL_TOPIC is sent back else RPL_NOTOPIC. + */ + const RPL_TOPIC = '332'; + + /** + * <channel> <nick> + * + * Returned by the server to indicate that the attempted INVITE message was + * successful and is being passed onto the end client. + */ + const RPL_INVITING = '341'; + + /** + * <user> Summoning user to IRC + * + * Returned by a server answering a SUMMON message to indicate that it is + * summoning that user. + */ + const RPL_SUMMONING = '342'; + + /** + * <version>.<debuglevel> <server> <comments> + * + * Reply by the server showing its version details. The <version> is the version + * of the software being used (including any patchlevel revisions) and the + * <debuglevel> is used to indicate if the server is running in "debug mode". The + * "comments" field may contain any comments about the version or further version + * details. + */ + const RPL_VERSION = '351'; + + /** + * <channel> <user> <host> <server> <nick> <H|G>[*][@|+] <hopcount> <real name> + */ + const RPL_WHOREPLY = '352'; + + /** + * <name> End of /WHO list + * + * The RPL_WHOREPLY and RPL_ENDOFWHO pair are used to answer a WHO message. The + * RPL_WHOREPLY is only sent if there is an appropriate match to the WHO query. + * If there is a list of parameters supplied with a WHO message, a RPL_ENDOFWHO + * must be sent after processing each list item with <name> being the item. + */ + const RPL_ENDOFWHO = '315'; + + /** + * <channel> [[@|+]<nick> [[@|+]<nick> [...]]] + */ + const RPL_NAMREPLY = '353'; + + /** + * <channel> End of /NAMES list + * + * To reply to a NAMES message, a reply pair consisting of RPL_NAMREPLY and + * RPL_ENDOFNAMES is sent by the server back to the client. If there is no + * channel found as in the query, then only RPL_ENDOFNAMES is returned. The + * exception to this is when a NAMES message is sent with no parameters and all + * visible channels and contents are sent back in a series of RPL_NAMEREPLY + * messages with a RPL_ENDOFNAMES to mark the end. + */ + const RPL_ENDOFNAMES = '366'; + + /** + * <mask> <server> <hopcount> <server info> + */ + const RPL_LINKS = '364'; + + /** + * <mask> End of /LINKS list + * + * In replying to the LINKS message, a server must send replies back using the + * RPL_LINKS numeric and mark the end of the list using an RPL_ENDOFLINKS reply.v + */ + const RPL_ENDOFLINKS = '365'; + + /** + * <channel> <banid> + */ + const RPL_BANLIST = '367'; + + /** + * <channel> End of channel ban list + * + * When listing the active 'bans' for a given channel, a server is required to + * send the list back using the RPL_BANLIST and RPL_ENDOFBANLIST messages. A + * separate RPL_BANLIST is sent for each active banid. After the banids have been + * listed (or if none present) a RPL_ENDOFBANLIST must be sent. + */ + const RPL_ENDOFBANLIST = '368'; + + /** + * <string> + */ + const RPL_INFO = '371'; + + /** + * End of /INFO list + * + * A server responding to an INFO message is required to send all its 'info' in a + * series of RPL_INFO messages with a RPL_ENDOFINFO reply to indicate the end of + * the replies. + */ + const RPL_ENDOFINFO = '374'; + + /** + * - <server> Message of the day - + */ + const RPL_MOTDSTART = '375'; + + /** + * - <text> + */ + const RPL_MOTD = '372'; + + /** + * End of /MOTD command + * + * When responding to the MOTD message and the MOTD file is found, the file is + * displayed line by line, with each line no longer than 80 characters, using + * RPL_MOTD format replies. These should be surrounded by a RPL_MOTDSTART (before + * the RPL_MOTDs) and an RPL_ENDOFMOTD (after). + */ + const RPL_ENDOFMOTD = '376'; + + /** + * You are now an IRC operator + * + * RPL_YOUREOPER is sent back to a client which has just successfully issued an + * OPER message and gained operator status. + */ + const RPL_YOUREOPER = '381'; + + /** + * <config file> Rehashing + * + * If the REHASH option is used and an operator sends a REHASH message, an + * RPL_REHASHING is sent back to the operator. + */ + const RPL_REHASHING = '382'; + + /** + * <server> <string showing server's local time> + * + * When replying to the TIME message, a server must send the reply using the + * RPL_TIME format above. The string showing the time need only contain the + * correct day and time there. There is no further requirement for the time + * string. + */ + const RPL_TIME = '391'; + + /** + * UserID Terminal Host + */ + const RPL_USERSSTART = '392'; + + /** + * %-8s %-9s %-8s + */ + const RPL_USERS = '393'; + + /** + * End of users + */ + const RPL_ENDOFUSERS = '394'; + + /** + * Nobody logged in + * + * If the USERS message is handled by a server, the replies RPL_USERSTART, + * RPL_USERS, RPL_ENDOFUSERS and RPL_NOUSERS are used. RPL_USERSSTART must be + * sent first, following by either a sequence of RPL_USERS or a single + * RPL_NOUSER. Following this is RPL_ENDOFUSERS. + */ + const RPL_NOUSERS = '395'; + + /** + * Link <version & debug level> <destination> <next server> + */ + const RPL_TRACELINK = '200'; + + /** + * Try. <class> <server> + */ + const RPL_TRACECONNECTING = '201'; + + /** + * H.S. <class> <server> + */ + const RPL_TRACEHANDSHAKE = '202'; + + /** + * ???? <class> [<client IP address in dot form>] + */ + const RPL_TRACEUNKNOWN = '203'; + + /** + * Oper <class> <nick> + */ + const RPL_TRACEOPERATOR = '204'; + + /** + * User <class> <nick> + */ + const RPL_TRACEUSER = '205'; + + /** + * Serv <class> <int>S <int>C <server> <nick!user|*!*>@<host|server> + */ + const RPL_TRACESERVER = '206'; + + /** + * <newtype> 0 <client name> + */ + const RPL_TRACENEWTYPE = '208'; + + /** + * File <logfile> <debug level> + * + * The RPL_TRACE* are all returned by the server in response to the TRACE + * message. How many are returned is dependent on the the TRACE message and + * whether it was sent by an operator or not. There is no predefined order for + * which occurs first. Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and + * RPL_TRACEHANDSHAKE are all used for connections which have not been fully + * established and are either unknown, still attempting to connect or in the + * process of completing the 'server handshake'. RPL_TRACELINK is sent by any + * server which handles a TRACE message and has to pass it on to another server. + * The list of RPL_TRACELINKs sent in response to a TRACE command traversing the + * IRC network should reflect the actual connectivity of the servers themselves + * along that path. RPL_TRACENEWTYPE is to be used for any connection which does + * not fit in the other categories but is being displayed anyway. + */ + const RPL_TRACELOG = '261'; + + /** + * <linkname> <sendq> <sent messages> <sent bytes> <received messages> <received + * bytes> <time open> + */ + const RPL_STATSLINKINFO = '211'; + + /** + * <command> <count> + */ + const RPL_STATSCOMMANDS = '212'; + + /** + * C <host> * <name> <port> <class> + */ + const RPL_STATSCLINE = '213'; + + /** + * N <host> * <name> <port> <class> + */ + const RPL_STATSNLINE = '214'; + + /** + * I <host> * <host> <port> <class> + */ + const RPL_STATSILINE = '215'; + + /** + * K <host> * <username> <port> <class> + */ + const RPL_STATSKLINE = '216'; + + /** + * Y <class> <ping frequency> <connect frequency> <max sendq> + */ + const RPL_STATSYLINE = '218'; + + /** + * <stats letter> End of /STATS report + */ + const RPL_ENDOFSTATS = '219'; + + /** + * L <hostmask> * <servername> <maxdepth> + */ + const RPL_STATSLLINE = '241'; + + /** + * Server Up %d days %d%02d%02d + */ + const RPL_STATSUPTIME = '242'; + + /** + * O <hostmask> * <name> + */ + const RPL_STATSOLINE = '243'; + + /** + * H <hostmask> * <servername> + */ + const RPL_STATSHLINE = '244'; + + /** + * <user mode string> + * + * To answer a query about a client's own mode, RPL_UMODEIS is sent back. + */ + const RPL_UMODEIS = '221'; + + /** + * There are <integer> users and <integer> invisible on <integer> servers + */ + const RPL_LUSERCLIENT = '251'; + + /** + * <integer> operator(s) online + */ + const RPL_LUSEROP = '252'; + + /** + * <integer> unknown connection(s) + */ + const RPL_LUSERUNKNOWN = '253'; + + /** + * <integer> channels formed + */ + const RPL_LUSERCHANNELS = '254'; + + /** + * I have <integer> clients and <integer> servers + * + * In processing an LUSERS message, the server sends a set of replies from + * RPL_LUSERCLIENT, RPL_LUSEROP, RPL_USERUNKNOWN, RPL_LUSERCHANNELS and + * RPL_LUSERME. When replying, a server must send back RPL_LUSERCLIENT and + * RPL_LUSERME. The other replies are only sent back if a non-zero count is found + * for them. + */ + const RPL_LUSERME = '255'; + + /** + * <server> Administrative info + */ + const RPL_ADMINME = '256'; + + /** + * <admin info> + */ + const RPL_ADMINLOC1 = '257'; + + /** + * <admin info> + */ + const RPL_ADMINLOC2 = '258'; + + /** + * <admin info> + * + * When replying to an ADMIN message, a server is expected to use replies + * RLP_ADMINME through to RPL_ADMINEMAIL and provide a text message with each. + * For RPL_ADMINLOC1 a description of what city, state and country the server is + * in is expected, followed by details of the university and department + * (RPL_ADMINLOC2) and finally the administrative contact for the server (an + * email address here is required) in RPL_ADMINEMAIL. + */ + const RPL_ADMINEMAIL = '259'; + + /** + * Reply code sent by the server, which can be compared to the ERR_* and + * RPL_* constants + * + * @var string + */ + protected $code; + + /** + * Reply code description sent by the server. + * + * @var string + */ + protected $description; + + /** + * Raw data sent by the server + * + * @var string + */ + protected $rawData; + + /** + * Event type + * + * @var string + */ + protected $type = 'response'; + + /** + * Sets the reply code sent by the server. + * + * @param string $code Reply code + * + * @return Phergie_Event_Response Provides a fluent interface + */ + public function setCode($code) + { + $this->code = $code; + return $this; + } + + /** + * Returns the reply code sent by the server. + * + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * Sets the reply code description sent by the server. + * + * @param string $description Reply code description + * + * @return Phergie_Event_Response Provides a fluent interface + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Returns the reply code description sent by the server. + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the raw buffer for the given event + * + * @param string $buffer Raw event buffer + * + * @return Phergie_Event_Response Provides a fluent interface + */ + public function setRawData($buffer) + { + $this->rawData = $buffer; + return $this; + } + + /** + * Returns the raw buffer that was sent from the server for that event + * + * @return string + */ + public function getRawData() + { + return $this->rawData; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Exception.php new file mode 100755 index 000000000..f4d71e531 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Exception.php @@ -0,0 +1,33 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for all Phergie-related exceptions. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Exception extends Exception +{ +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Hostmask.php b/plugins/Irc/extlib/phergie/Phergie/Hostmask.php new file mode 100755 index 000000000..b13842f53 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Hostmask.php @@ -0,0 +1,217 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Data structure for a hostmask. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Hostmask +{ + /** + * Host + * + * @var string + */ + protected $host; + + /** + * Nick + * + * @var string + */ + protected $nick; + + /** + * Username + * + * @var string + */ + protected $username; + + /** + * Regular expression used to parse a hostmask + * + * @var string + */ + protected static $regex = '/^([^!@]+)!(?:[ni]=)?([^@]+)@([^ ]+)/'; + + /** + * Constructor to initialize components of the hostmask. + * + * @param string $nick Nick component + * @param string $username Username component + * @param string $host Host component + * + * @return void + */ + public function __construct($nick, $username, $host) + { + $this->nick = $nick; + $this->username = $username; + $this->host = $host; + } + + /** + * Returns whether a given string appears to be a valid hostmask. + * + * @param string $string Alleged hostmask string + * + * @return bool TRUE if the string appears to be a valid hostmask, FALSE + * otherwise + */ + public static function isValid($string) + { + return (preg_match(self::$regex, $string) > 0); + } + + /** + * Parses a string containing the entire hostmask into a new instance of + * this class. + * + * @param string $hostmask Entire hostmask including the nick, username, + * and host components + * + * @return Phergie_Hostmask New instance populated with data parsed from + * the provided hostmask string + * @throws Phergie_Hostmask_Exception + */ + public static function fromString($hostmask) + { + if (preg_match(self::$regex, $hostmask, $match)) { + list(, $nick, $username, $host) = $match; + return new self($nick, $username, $host); + } + + throw new Phergie_Hostmask_Exception( + 'Invalid hostmask specified: "' . $hostmask . '"', + Phergie_Hostmask_Exception::ERR_INVALID_HOSTMASK + ); + } + + /** + * Sets the hostname. + * + * @param string $host Hostname + * + * @return Phergie_Hostmask Provides a fluent interface + */ + public function setHost($host) + { + $this->host = $host; + + return $this; + } + + /** + * Returns the hostname. + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the username of the user. + * + * @param string $username Username + * + * @return Phergie_Hostmask Provides a fluent interface + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Returns the username of the user. + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Sets the nick of the user. + * + * @param string $nick User nick + * + * @return Phergie_Hostmask Provides a fluent interface + */ + public function setNick($nick) + { + $this->nick = $nick; + + return $this; + } + + /** + * Returns the nick of the user. + * + * @return string + */ + public function getNick() + { + return $this->nick; + } + + /** + * Returns the hostmask for the originating server or user. + * + * @return string + */ + public function __toString() + { + return $this->nick . '!' . $this->username . '@' . $this->host; + } + + /** + * Returns whether a given hostmask matches a given pattern. + * + * @param string $pattern Pattern using conventions of a ban mask where + * represents a wildcard + * @param string $hostmask Optional hostmask to match against, if not + * the current hostmask instance + * + * @return bool TRUE if the hostmask matches the pattern, FALSE otherwise + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_3 Examples + */ + public function matches($pattern, $hostmask = null) + { + if (!$hostmask) { + $hostmask = (string) $this; + } + + $pattern = str_replace('*', '.*', $pattern); + + return (preg_match('#^' . $pattern . '$#', $hostmask) > 0); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Hostmask/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Hostmask/Exception.php new file mode 100644 index 000000000..590f02004 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Hostmask/Exception.php @@ -0,0 +1,37 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to hostmask handling. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Hostmask_Exception extends Phergie_Exception +{ + /** + * Error indicating that an invalid hostmask string was specified + */ + const ERR_INVALID_HOSTMASK = 1; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Abstract.php new file mode 100755 index 000000000..b7105ecdb --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Abstract.php @@ -0,0 +1,605 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for plugins to provide event handler stubs and commonly needed + * functionality. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +abstract class Phergie_Plugin_Abstract +{ + /** + * Current configuration handler + * + * @var Phergie_Config + */ + protected $config; + + /** + * Plugin handler used to provide access to other plugins + * + * @var Phergie_Plugin_Handler + */ + protected $plugins; + + /** + * Current event handler instance for outgoing events + * + * @var Phergie_Event_Handler + */ + protected $events; + + /** + * Current connection instance + * + * @var Phergie_Connection + */ + protected $connection; + + /** + * Current incoming event being handled + * + * @var Phergie_Event_Request|Phergie_Event_Response + */ + protected $event; + + /** + * Plugin short name + * + * @var string + */ + protected $name; + + /** + * Returns the short name for the plugin based on its class name. + * + * @return string + */ + public function getName() + { + if (empty($this->name)) { + $this->name = substr(strrchr(get_class($this), '_'), 1); + } + return $this->name; + } + + /** + * Sets the short name for the plugin. + * + * @param string $name Plugin short name + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * Indicates that the plugin failed to load due to an unsatisfied + * runtime requirement, such as a missing dependency. + * + * @param string $message Error message to provide more information + * about the reason for the failure + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + * @throws Phergie_Plugin_Exception Always + */ + protected function fail($message) + { + throw new Phergie_Plugin_Exception( + $message, + Phergie_Plugin_Exception::ERR_REQUIREMENT_UNSATISFIED + ); + } + + /** + * Sets the current configuration handler. + * + * @param Phergie_Config $config Configuration handler + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setConfig(Phergie_Config $config) + { + $this->config = $config; + return $this; + } + + /** + * Returns the current configuration handler or the value of a single + * setting from it. + * + * @param string $name Optional name of a setting for which the value + * should be returned instead of the entire configuration handler + * @param mixed $default Optional default value to return if no value + * is set for the setting indicated by $name + * + * @return Phergie_Config|mixed Configuration handler or value of the + * setting specified by $name + * @throws Phergie_Plugin_Exception No configuration handler has been set + */ + public function getConfig($name = null, $default = null) + { + if (empty($this->config)) { + throw new Phergie_Plugin_Exception( + 'Configuration handler cannot be accessed before one is set', + Phergie_Plugin_Exception::ERR_NO_CONFIG_HANDLER + ); + } + if (!is_null($name)) { + if (!isset($this->config[$name])) { + return $default; + } + return $this->config[$name]; + } + return $this->config; + } + + /** + * Sets the current plugin handler. + * + * @param Phergie_Plugin_Handler $handler Plugin handler + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setPluginHandler(Phergie_Plugin_Handler $handler) + { + $this->plugins = $handler; + return $this; + } + + /** + * Returns the current plugin handler. + * + * @return Phergie_Plugin_Handler + * @throws Phergie_Plugin_Exception No plugin handler has been set + */ + public function getPluginHandler() + { + if (empty($this->plugins)) { + throw new Phergie_Plugin_Exception( + 'Plugin handler cannot be accessed before one is set', + Phergie_Plugin_Exception::ERR_NO_PLUGIN_HANDLER + ); + } + return $this->plugins; + } + + /** + * Sets the current event handler. + * + * @param Phergie_Event_Handler $handler Event handler + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setEventHandler(Phergie_Event_Handler $handler) + { + $this->events = $handler; + return $this; + } + + /** + * Returns the current event handler. + * + * @return Phergie_Event_Handler + * @throws Phergie_Plugin_Exception No event handler has been set + */ + public function getEventHandler() + { + if (empty($this->events)) { + throw new Phergie_Plugin_Exception( + 'Event handler cannot be accessed before one is set', + Phergie_Plugin_Exception::ERR_NO_EVENT_HANDLER + ); + } + return $this->events; + } + + /** + * Sets the current connection. + * + * @param Phergie_Connection $connection Connection + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setConnection(Phergie_Connection $connection) + { + $this->connection = $connection; + return $this; + } + + /** + * Returns the current event connection. + * + * @return Phergie_Connection + * @throws Phergie_Plugin_Exception No connection has been set + */ + public function getConnection() + { + if (empty($this->connection)) { + throw new Phergie_Plugin_Exception( + 'Connection cannot be accessed before one is set', + Phergie_Plugin_Exception::ERR_NO_CONNECTION + ); + } + return $this->connection; + } + + /** + * Sets the current incoming event to be handled. + * + * @param Phergie_Event_Request|Phergie_Event_Response $event Event + * + * @return Phergie_Plugin_Abstract Provides a fluent interface + */ + public function setEvent($event) + { + $this->event = $event; + return $this; + } + + /** + * Returns the current incoming event to be handled. + * + * @return Phergie_Event_Request|Phergie_Event_Response + */ + public function getEvent() + { + if (empty($this->event)) { + throw new Phergie_Plugin_Exception( + 'Event cannot be accessed before one is set', + Phergie_Plugin_Exception::ERR_NO_EVENT + ); + } + return $this->event; + } + + /** + * Provides do* methods with signatures identical to those of + * Phergie_Driver_Abstract but that queue up events to be dispatched + * later. + * + * @param string $name Name of the method called + * @param array $args Arguments passed in the call + * + * @return mixed + */ + public function __call($name, array $args) + { + $subcmd = substr($name, 0, 2); + if ($subcmd == 'do') { + $type = strtolower(substr($name, 2)); + $this->getEventHandler()->addEvent($this, $type, $args); + } else if ($subcmd != 'on') { + throw new Phergie_Plugin_Exception( + 'Called invalid method ' . $name . ' in ' . get_class($this), + Phergie_Plugin_Exception::ERR_INVALID_CALL + ); + } + } + + /** + * Handler for when the plugin is initially loaded - useful for checking + * runtime dependencies or performing any setup necessary for the plugin + * to function properly such as initializing a database. + * + * @return void + */ + public function onLoad() + { + } + + /** + * Handler for when the bot initially connects to a server. + * + * @return void + */ + public function onConnect() + { + } + + /** + * Handler for each tick, a single iteration of the continuous loop + * executed by the bot to receive, handle, and send events - useful for + * repeated execution of tasks on a time interval. + * + * @return void + */ + public function onTick() + { + } + + /** + * Handler for when any event is received but has not yet been dispatched + * to the plugin handler method specific to its event type. + * + * @return bool|null|void FALSE to short-circuit further event + * processing, TRUE or NULL otherwise + */ + public function preEvent() + { + } + + /** + * Handler for after plugin processing of an event has concluded but + * before any events triggered in response by plugins are sent to the + * server - useful for modifying outgoing events before they are sent. + * + * @return void + */ + public function preDispatch() + { + } + + /** + * Handler for after any events triggered by plugins in response to a + * received event are sent to the server. + * + * @return void + */ + public function postDispatch() + { + } + + /** + * Handler for when the server prompts the client for a nick. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_1_2 + */ + public function onNick() + { + } + + /** + * Handler for when a user obtains operator privileges. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_1_5 + */ + public function onOper() + { + } + + /** + * Handler for when the client session is about to be terminated. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_1_6 + */ + public function onQuit() + { + } + + /** + * Handler for when a user joins a channel. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_1 + */ + public function onJoin() + { + } + + /** + * Handler for when a user leaves a channel. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_2 + */ + public function onPart() + { + } + + /** + * Handler for when a user or channel mode is changed. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_3 + */ + public function onMode() + { + } + + /** + * Handler for when a channel topic is viewed or changed. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_4 + */ + public function onTopic() + { + } + + /** + * Handler for when a message is received from a channel or user. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_4_1 + */ + public function onPrivmsg() + { + } + + /** + * Handler for when the bot receives a CTCP ACTION request. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.4 + */ + public function onAction() + { + } + + /** + * Handler for when a notice is received. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_4_2 + */ + public function onNotice() + { + } + + /** + * Handler for when a user is kicked from a channel. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_8 + */ + public function onKick() + { + } + + /** + * Handler for when the bot receives a ping event from a server, at + * which point it is expected to respond with a pong request within + * a short period else the server may terminate its connection. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_2 + */ + public function onPing() + { + } + + /** + * Handler for when the bot receives a CTCP TIME request. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.6 + */ + public function onTime() + { + } + + /** + * Handler for when the bot receives a CTCP VERSION request. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.1 + */ + public function onVersion() + { + } + + /** + * Handler for when the bot receives a CTCP PING request. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.2 + */ + public function onCtcpPing() + { + } + + /** + * Handler for when the bot receives a CTCP request of an unknown type. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html + */ + public function onCtcp() + { + } + + /** + * Handler for when a reply is received for a CTCP PING request sent by + * the bot. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.2 + */ + public function onPingReply() + { + } + + /** + * Handler for when a reply is received for a CTCP TIME request sent by + * the bot. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.6 + */ + public function onTimeReply() + { + } + + /** + * Handler for when a reply is received for a CTCP VERSION request sent + * by the bot. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html#4.1 + */ + public function onVersionReply() + { + } + + /** + * Handler for when a reply received for a CTCP request of an unknown + * type. + * + * @return void + * @link http://www.invlogic.com/irc/ctcp.html + */ + public function onCtcpReply() + { + } + + /** + * Handler for when the bot receives a kill request from a server. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_1 + */ + public function onKill() + { + } + + /** + * Handler for when the bot receives an invitation to join a channel. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_7 + */ + public function onInvite() + { + } + + /** + * Handler for when a server response is received to a command issued by + * the bot. + * + * @return void + * @link http://irchelp.org/irchelp/rfc/chapter6.html + */ + public function onResponse() + { + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Acl.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Acl.php new file mode 100755 index 000000000..e209e32e1 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Acl.php @@ -0,0 +1,186 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Acl + * @author Phergie Development Team <team@phergie.org> + * @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_Acl + */ + +/** + * Provides an access control system to limit reponses to events based on + * the users who originate them. + * + * Configuration settings: + * acl.whitelist - mapping of user hostmask patterns (optionally by host) to + * plugins and methods where those plugins and methods will + * only be accessible to those users (i.e. and inaccessible + * to other users) + * acl.blacklist - mapping of user hostmasks (optionally by host) to plugins + * and methods where where those plugins and methods will be + * inaccessible to those users but accessible to other users + * acl.ops - TRUE to automatically give access to whitelisted plugins + * and methods to users with ops for events they initiate in + * channels where they have ops + * + * The whitelist and blacklist settings are formatted like so: + * <code> + * 'acl.whitelist' => array( + * 'hostname1' => array( + * 'pattern1' => array( + * 'plugins' => array( + * 'ShortPluginName' + * ), + * 'methods' => array( + * 'methodName' + * ) + * ), + * ) + * ), + * </code> + * + * The hostname array dimension is optional; if not used, rules will be + * applied across all connections. The pattern is a user hostmask pattern + * where asterisks (*) are used for wildcards. Plugins and methods do not + * need to be set to empty arrays if they are not used; simply exclude them. + * + * @category Phergie + * @package Phergie_Plugin_Acl + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Acl + * @uses Phergie_Plugin_UserInfo pear.phergie.org + */ +class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract +{ + /** + * Checks for permission settings and removes the plugin if none are set. + * + * @return void + */ + public function onLoad() + { + $this->plugins->getPlugin('UserInfo'); + + if (!$this->getConfig('acl.blacklist') + && !$this->getConfig('acl.whitelist') + ) { + $this->plugins->removePlugin($this); + } + } + + /** + * Applies a set of rules to a plugin handler iterator. + * + * @param Phergie_Plugin_Iterator $iterator Iterator to receive rules + * @param array $rules Associate array containing + * either a 'plugins' key pointing to an array containing plugin + * short names to filter, a 'methods' key pointing to an array + * containing method names to filter, or both + * + * @return void + */ + protected function applyRules(Phergie_Plugin_Iterator $iterator, array $rules) + { + if (!empty($rules['plugins'])) { + $iterator->addPluginFilter($rules['plugins']); + } + if (!empty($rules['methods'])) { + $iterator->addMethodFilter($rules['methods']); + } + } + + /** + * Checks permission settings and short-circuits event processing for + * blacklisted users. + * + * @return void + */ + public function preEvent() + { + // Ignore server responses + if ($this->event instanceof Phergie_Event_Response) { + return; + } + + // Ignore server-initiated events + if (!$this->event->isFromUser()) { + return; + } + + // Get the iterator used to filter plugins when processing events + $iterator = $this->plugins->getIterator(); + + // Get configuration setting values + $whitelist = $this->getConfig('acl.whitelist', array()); + $blacklist = $this->getConfig('acl.blacklist', array()); + $ops = $this->getConfig('acl.ops', false); + + // Support host-specific lists + $host = $this->connection->getHost(); + foreach (array('whitelist', 'blacklist') as $var) { + foreach ($$var as $pattern => $rules) { + $regex = '/^' . str_replace('*', '.*', $pattern) . '$/i'; + if (preg_match($regex, $host)) { + ${$var} = ${$var}[$pattern]; + break; + } + } + } + + // Get information on the user initiating the current event + $hostmask = $this->event->getHostmask(); + $isOp = $ops + && $this->event->isInChannel() + && $this->plugins->userInfo->isOp( + $this->event->getNick(), + $this->event->getSource() + ); + + // Filter whitelisted commands if the user is not on the whitelist + if (!$isOp) { + $whitelisted = false; + foreach ($whitelist as $pattern => $rules) { + if ($hostmask->matches($pattern)) { + $whitelisted = true; + } + } + if (!$whitelisted) { + foreach ($whitelist as $pattern => $rules) { + $this->applyRules($iterator, $rules); + } + } + } + + // Filter blacklisted commands if the user is on the blacklist + $blacklisted = false; + foreach ($blacklist as $pattern => $rules) { + if ($hostmask->matches($pattern)) { + $this->applyRules($iterator, $rules); + break; + } + } + } + + /** + * Clears filters on the plugin handler iterator. + * + * @return void + */ + public function postDispatch() + { + $this->plugins->getIterator()->clearFilters(); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/AltNick.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/AltNick.php new file mode 100755 index 000000000..16d5f9b9c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/AltNick.php @@ -0,0 +1,95 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_AltNick + * @author Phergie Development Team <team@phergie.org> + * @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_AltNick + */ + +/** + * Handles switching to alternate nicks in cases where the primary nick is + * not available for use. + * + * @category Phergie + * @package Phergie_Plugin_AltNick + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_AltNick + * @uses extension spl + */ +class Phergie_Plugin_AltNick extends Phergie_Plugin_Abstract +{ + /** + * Iterator for the alternate nick list + * + * @var ArrayIterator + */ + protected $iterator; + + /** + * Initializes instance variables. + * + * @return void + */ + public function onConnect() + { + if ($this->config['altnick.nicks']) { + if (is_string($this->config['altnick.nicks'])) { + $this->config['altnick.nicks'] + = array($this->config['altnick.nicks']); + } + $this->iterator = new ArrayIterator($this->config['altnick.nicks']); + } + } + + /** + * Switches to alternate nicks as needed when nick collisions occur. + * + * @return void + */ + public function onResponse() + { + // If no alternate nick list was found, return + if (empty($this->iterator)) { + return; + } + + // If the response event indicates that the nick set is in use... + $code = $this->getEvent()->getCode(); + if ($code == Phergie_Event_Response::ERR_NICKNAMEINUSE) { + + // Attempt to move to the next nick in the alternate nick list + $this->iterator->next(); + + // If another nick is available... + if ($this->iterator->valid()) { + + // Switch to the new nick + $altNick = $this->iterator->current(); + $this->doNick($altNick); + + // Update the connection to reflect the nick change + $this->getConnection()->setNick($altNick); + + } else { + // If no other nicks are available... + + // Terminate the connection + $this->doQuit('All specified alternate nicks are in use'); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/AudioScrobbler.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/AudioScrobbler.php new file mode 100755 index 000000000..ed4030a28 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/AudioScrobbler.php @@ -0,0 +1,191 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_AudioScrobbler + * @author Phergie Development Team <team@phergie.org> + * @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_AudioScrobbler + */ + +/** + * Provides commands to look up information on tracks played by specific + * users on the Last.fm and Libre.fm services. + * + * TODO: Make the "nick-binding" use an SQLite database instead of having them + * hard-coded in to the config file. + * + * Configuration settings: + * "audioscrobbler.lastfm_api_key": API given by last.fm (string). + * "audioscrobbler.librefm_api_key": API key given by libre.fm (string). + * + * @category Phergie + * @package Phergie_Plugin_AudioScrobbler + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_AudioScrobbler + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + * @uses extension simplexml + */ +class Phergie_Plugin_AudioScrobbler extends Phergie_Plugin_Abstract +{ + /** + * Last.FM API entry point + * + * @var string + */ + protected $lastfmUrl = 'http://ws.audioscrobbler.com/2.0/'; + + /** + * Libre.FM API entry point + * + * @var string + */ + protected $librefmUrl = 'http://alpha.dev.libre.fm/2.0/'; + + /** + * Scrobbler query string for user.getRecentTracks + * + * @var string + */ + protected $query = '?method=user.getrecenttracks&user=%s&api_key=%s'; + + /** + * HTTP plugin + * + * @var Phergie_Plugin_Http + */ + protected $http; + + /** + * Check for dependencies. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('simplexml')) { + $this->fail('SimpleXML php extension is required'); + } + + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $this->http = $plugins->getPlugin('Http'); + } + + /** + * Command function to get a user's status on last.fm. + * + * @param string $user User identifier + * + * @return void + */ + public function onCommandLastfm($user = null) + { + if ($key = $this->config['audioscrobbler.lastfm_api_key']) { + $scrobbled = $this->getScrobbled($user, $this->lastfmUrl, $key); + if ($scrobbled) { + $this->doPrivmsg($this->getEvent()->getSource(), $scrobbled); + } + } + } + + /** + * Command function to get a user's status on libre.fm. + * + * @param string $user User identifier + * + * @return void + */ + public function onCommandLibrefm($user = null) + { + if ($key = $this->config['audioscrobbler.librefm_api_key']) { + $scrobbled = $this->getScrobbled($user, $this->librefmUrl, $key); + if ($scrobbled) { + $this->doPrivmsg($this->getEvent()->getSource(), $scrobbled); + } + } + } + + /** + * Simple Scrobbler API function to get a formatted string of the most + * recent track played by a user. + * + * @param string $user Username to look up + * @param string $url Base URL of the scrobbler service + * @param string $key Scrobbler service API key + * + * @return string Formatted string of the most recent track played + */ + public function getScrobbled($user, $url, $key) + { + $event = $this->getEvent(); + $user = $user ? $user : $event->getNick(); + $url = sprintf($url . $this->query, urlencode($user), urlencode($key)); + + $response = $this->http->get($url); + if ($response->isError()) { + $this->doNotice( + $event->getNick(), + 'Can\'t find status for ' . $user . ': HTTP ' . + $response->getCode() . ' ' . $response->getMessage() + ); + return false; + } + + $xml = $response->getContent(); + if ($xml->error) { + $this->doNotice( + $event->getNick(), + 'Can\'t find status for ' . $user . ': API ' . $xml->error + ); + return false; + } + + $recenttracks = $xml->recenttracks; + $track = $recenttracks->track[0]; + + // If the user exists but has not scrobbled anything, the result will + // be empty. + if (empty($track->name) && empty($track->artist)) { + $this->doNotice( + $event->getNick(), + 'Can\'t find track information for ' . $recenttracks['user'] + ); + return false; + } + + if (isset($track['nowplaying'])) { + $msg = sprintf( + '%s is listening to %s by %s', + $recenttracks['user'], + $track->name, + $track->artist + ); + } else { + $msg = sprintf( + '%s, %s was listening to %s by %s', + date('j M Y, H:i', (int) $track->date['uts']), + $recenttracks['user'], + $track->name, + $track->artist + ); + } + if ($track->streamable == 1) { + $msg .= ' - ' . $track->url; + } + return $msg; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/AutoJoin.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/AutoJoin.php new file mode 100755 index 000000000..a6d1adfbb --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/AutoJoin.php @@ -0,0 +1,69 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_AutoJoin + * @author Phergie Development Team <team@phergie.org> + * @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_AutoJoin + */ + +/** + * Automates the process of having the bot join one or more channels upon + * connection to the server. + * + * The configuration setting autojoin.channels is used to determine which + * channels to join. This setting can point to a comma-delimited string or + * enumerated array containing a single list of channels or an associative + * array keyed by hostname where each value is a comma-delimited string or + * enumerated array containing a list of channels to join on the server + * corresponding to that hostname. + * + * @category Phergie + * @package Phergie_Plugin_AutoJoin + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_AutoJoin + */ +class Phergie_Plugin_AutoJoin extends Phergie_Plugin_Abstract +{ + /** + * Intercepts the end of the "message of the day" response and responds by + * joining the channels specified in the configuration file. + * + * @return void + */ + public function onResponse() + { + switch ($this->getEvent()->getCode()) { + case Phergie_Event_Response::RPL_ENDOFMOTD: + case Phergie_Event_Response::ERR_NOMOTD: + if ($channels = $this->config['autojoin.channels']) { + if (is_array($channels)) { + // Support autojoin.channels being in these formats: + // 'hostname' => array('#channel1', '#channel2', ... ) + $host = $this->getConnection()->getHost(); + if (isset($channels[$host])) { + $channels = $channels[$host]; + } + if (is_array($channels)) { + $channels = implode(',', $channels); + } + } + $this->doJoin($channels); + } + $this->getPluginHandler()->removePlugin($this); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer.php new file mode 100644 index 000000000..7213cdee8 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer.php @@ -0,0 +1,82 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Beer + * @author Phergie Development Team <team@phergie.org> + * @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_Beer + */ + +/** + * Processes requests to serve users beer. + * + * @category Phergie + * @package Phergie_Plugin_Beer + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Beer + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Beer extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user a beer. + * + * @param string $request Request including the target and an optional + * suggestion of what beer to serve + * + * @return void + */ + public function onCommandBeer($request) + { + $format = $this->getConfig( + 'beer.format', + 'throws %target% %article% %item%.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Beer/beer.db', + 'beer', + $format, + $request + ); + } + + /** + * Adds a "booze" alias for the "beer" command. + * + * @param string $request Request including the target and an optional + * suggestion of what beer to serve + * + * @return void + */ + public function onCommandBooze($request) + { + $this->onCommandBeer($request); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer/db.php new file mode 100644 index 000000000..c7921e519 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Beer/db.php @@ -0,0 +1,81 @@ +<?php + +if (!defined('__DIR__')) { + define('__DIR__', dirname(__FILE__)); +} + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/beer.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE beer (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX beer_name ON beer (name)'); +$insert = $db->prepare('INSERT INTO beer (name, link) VALUES (:name, :link)'); + +// Get raw beerme.com data set +echo 'Downloading beerme.com data set', PHP_EOL; +$file = __DIR__ . '/beerlist.txt'; +if (!file_exists($file)) { + copy('http://beerme.com/beerlist.php', $file); +} +$contents = file_get_contents($file); + +// Extract data from data set +echo 'Processing beerme.com data', PHP_EOL; +$contents = tidy_repair_string($contents); +libxml_use_internal_errors(true); +$doc = new DOMDocument; +$doc->loadHTML($contents); +libxml_clear_errors(); +$xpath = new DOMXPath($doc); +$beers = $xpath->query('//table[@class="beerlist"]/tr/td[1]'); +$db->beginTransaction(); +foreach ($beers as $beer) { + $name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $beer->textContent); + $name = preg_replace('/\h*\v+\h*/', '', $name); + $link = 'http://beerme.com' . $beer->childNodes->item(1)->getAttribute('href'); + $insert->execute(array($name, $link)); +} +$db->commit(); + +// Clean up +echo 'Cleaning up', PHP_EOL; +unlink($file); + +// Get and decompress openbeerdb.com data set +$archive = __DIR__ . '/beers.zip'; +if (!file_exists($archive)) { + echo 'Downloading openbeerdb.com data set', PHP_EOL; + copy('http://openbeerdb.googlecode.com/files/beers.zip', $archive); +} + +echo 'Decompressing openbeerdb.com data set', PHP_EOL; +$zip = new ZipArchive; +$zip->open($archive); +$zip->extractTo(__DIR__, 'beers/beers.csv'); +$zip->close(); +$file = __DIR__ . '/beers/beers.csv'; + +// Extract data from data set +echo 'Processing openbeerdb.com data', PHP_EOL; +$fp = fopen($file, 'r'); +$columns = fgetcsv($fp, 0, '|'); +$db->beginTransaction(); +while ($line = fgetcsv($fp, 0, '|')) { + $line = array_combine($columns, $line); + $name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $line['name']); + $name = preg_replace('/\h*\v+\h*/', '', $name); + $link = null; + $insert->execute(array($name, $link)); +} +$db->commit(); +fclose($fp); + +// Clean up +echo 'Cleaning up', PHP_EOL; +unlink($file); +unlink($archive); +rmdir(__DIR__ . '/beers'); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/BeerScore.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/BeerScore.php new file mode 100644 index 000000000..16c671f68 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/BeerScore.php @@ -0,0 +1,156 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_BeerScore + * @author Phergie Development Team <team@phergie.org> + * @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_BeerScore + */ + +/** + * Handles incoming requests for beer scores. + * + * @category Phergie + * @package Phergie_Plugin_BeerScore + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_BeerScore + * @uses Phergie_Plugin_Http pear.phergie.org + */ +class Phergie_Plugin_BeerScore extends Phergie_Plugin_Abstract +{ + /** + * Score result type + * + * @const string + */ + const TYPE_SCORE = 'SCORE'; + + /** + * Search result type + * + * @const string + */ + const TYPE_SEARCH = 'SEARCH'; + + /** + * Refine result type + * + * @const type + */ + const TYPE_REFINE = 'REFINE'; + + /** + * Base API URL + * + * @const string + */ + const API_BASE_URL = 'http://caedmon.net/beerscore/'; + + /** + * HTTP plugin + * + * @var Phergie_Plugin_Http + */ + protected $http; + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->http = $this->getPluginHandler()->getPlugin('Http'); + } + + /** + * Handles beerscore commands. + * + * @param string $searchstring String to use in seaching for beer scores + * + * @return void + */ + public function onCommandBeerscore($searchstring) + { + $event = $this->getEvent(); + $target = $event->getNick(); + $source = $event->getSource(); + + $apiurl = self::API_BASE_URL . rawurlencode($searchstring); + $response = $this->http->get($apiurl); + + if ($response->isError()) { + $this->doNotice($target, 'Score not found (or failed to contact API)'); + return; + } + + $result = $response->getContent(); + switch ($result->type) { + case self::TYPE_SCORE: + // small enough number to get scores + foreach ($result->beer as $beer) { + if ($beer->score === -1) { + $score = '(not rated)'; + } else { + $score = $beer->score; + } + $str + = "{$target}: rating for {$beer->name}" . + " = {$score} ({$beer->url})"; + $this->doPrivmsg($source, $str); + } + break; + + case self::TYPE_SEARCH: + // only beer names, no scores + $str = ''; + $found = 0; + foreach ($result->beer as $beer) { + if (isset($beer->score)) { + ++$found; + if ($beer->score === -1) { + $score = '(not rated)'; + } else { + $score = $beer->score; + } + $str + = "{$target}: rating for {$beer->name}" . + " = {$score} ({$beer->url})"; + $this->doPrivmsg($source, $str); + } else { + $str .= "({$beer->name} -> {$beer->url}) "; + } + } + $foundnum = $result->num - $found; + $more = $found ? 'more ' : ''; + $str = "{$target}: {$foundnum} {$more}results... {$str}"; + $this->doPrivmsg($source, $str); + break; + + case self::TYPE_REFINE: + // Too many results; only output search URL + if ($result->num < 100) { + $num = $result->num; + } else { + $num = 'at least 100'; + } + $resultsword = (($result->num > 1) ? 'results' : 'result'); + $str = "{$target}: {$num} {$resultsword}; {$result->searchurl}"; + $this->doPrivmsg($source, $str); + break; + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cache.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cache.php new file mode 100644 index 000000000..2b54fab68 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cache.php @@ -0,0 +1,106 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Cache + * @author Phergie Development Team <team@phergie.org> + * @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_Cache + */ + +/** + * Implements a generic cache to be used by other plugins. + * + * @category Phergie + * @package Phergie_Plugin_Cache + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Cache + */ +class Phergie_Plugin_Cache extends Phergie_Plugin_Abstract +{ + /** + * Key-value data storage for the cache + * + * @var array + */ + protected $cache = array(); + + /** + * Stores a value in the cache. + * + * @param string $key Key to associate with the value + * @param mixed $data Data to be stored + * @param int|null $ttl Time to live in seconds or NULL for forever + * @param bool $overwrite TRUE to overwrite any existing value + * associated with the specified key + * + * @return bool + */ + public function store($key, $data, $ttl = 3600, $overwrite = true) + { + if (!$overwrite && isset($this->cache[$key])) { + return false; + } + + if ($ttl) { + $expires = time()+$ttl; + } else { + $expires = null; + } + + $this->cache[$key] = array('data' => $data, 'expires' => $expires); + return true; + + } + + /** + * Fetches a previously stored value. + * + * @param string $key Key associated with the value + * + * @return mixed Stored value or FALSE if no value or an expired value + * is associated with the specified key + */ + public function fetch($key) + { + if (!isset($this->cache[$key])) { + return false; + } + + $item = $this->cache[$key]; + if (!is_null($item['expires']) && $item['expires'] < time()) { + $this->expire($key); + return false; + } + + return $item['data']; + } + + /** + * Expires a value that has exceeded its time to live. + * + * @param string $key Key associated with the value to expire + * + * @return bool + */ + protected function expire($key) + { + if (!isset($this->cache[$key])) { + return false; + } + unset($this->cache[$key]); + return true; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine.php new file mode 100644 index 000000000..2b76bd312 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine.php @@ -0,0 +1,69 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Caffeine + * @author Phergie Development Team <team@phergie.org> + * @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_Caffeine + */ + +/** + * Processes requests to serve users caffeinated beverages. + * + * @category Phergie + * @package Phergie_Plugin_Caffeine + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Caffeine + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Caffeine extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user a caffeinated beverage. + * + * @param string $request Request including the target and an optional + * suggestion of what caffeinated beverage to serve + * + * @return void + */ + public function onCommandCaffeine($request) + { + $format = $this->getConfig( + 'beer.format', + 'throws %target% %article% %item%.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Caffeine/caffeine.db', + 'caffeine', + $format, + $request + ); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine/db.php new file mode 100644 index 000000000..cdff52fd8 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Caffeine/db.php @@ -0,0 +1,51 @@ +<?php + +if (!defined('__DIR__')) { + define('__DIR__', dirname(__FILE__)); +} + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/caffeine.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE caffeine (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX caffeine_name ON caffeine (name)'); +$insert = $db->prepare('INSERT INTO caffeine (name, link) VALUES (:name, :link)'); + +// Get raw energyfiend.com data set +echo 'Downloading energyfiend.com data set', PHP_EOL; +$file = __DIR__ . '/the-caffeine-database.html'; +if (!file_exists($file)) { + copy('http://www.energyfiend.com/the-caffeine-database', $file); +} +$contents = file_get_contents($file); + +// Extract data from data set +echo 'Processing energyfiend.com data', PHP_EOL; +$contents = tidy_repair_string($contents); +libxml_use_internal_errors(true); +$doc = new DOMDocument; +$doc->loadHTML($contents); +libxml_clear_errors(); +$xpath = new DOMXPath($doc); +$caffeine = $xpath->query('//table[@id="caffeinedb"]//tr/td[1]'); +$db->beginTransaction(); +foreach ($caffeine as $drink) { + $name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $drink->textContent); + $name = preg_replace('/\s*\v+\s*/', ' ', $name); + if ($drink->firstChild->nodeName == 'a') { + $link = 'http://energyfiend.com' + . $drink->firstChild->getAttribute('href'); + } else { + $link = null; + } + $insert->execute(array($name, $link)); +} +$db->commit(); + +// Clean up +echo 'Cleaning up', PHP_EOL; +unlink($file); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Censor.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Censor.php new file mode 100755 index 000000000..99c69d873 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Censor.php @@ -0,0 +1,120 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Censor + * @author Phergie Development Team <team@phergie.org> + * @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_Censor + */ + +/** + * Facilitates censoring of event content or discardment of events + * containing potentially offensive phrases depending on the value of the + * configuration setting censor.mode ('off', 'censor', 'discard'). Also + * provides access to a web service for detecting censored words so that + * other plugins may optionally integrate and adjust behavior accordingly to + * prevent discardment of events. + * + * @category Phergie + * @package Phergie_Plugin_Censor + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Censor + * @uses extension soap + */ +class Phergie_Plugin_Censor extends Phergie_Plugin_Abstract +{ + /** + * SOAP client to interact with the CDYNE Profanity Filter API + * + * @var SoapClient + */ + protected $soap; + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('soap')) { + $this->fail('The PHP soap extension is required'); + } + + if (!in_array($this->config['censor.mode'], array('censor', 'discard'))) { + $this->plugins->removePlugin($this); + } + } + + /** + * Returns a "clean" version of a given string. + * + * @param string $string String to clean + * + * @return string Cleaned string + */ + public function cleanString($string) + { + if (empty($this->soap)) { + $this->soap = new SoapClient('http://ws.cdyne.com/ProfanityWS/Profanity.asmx?wsdl'); + } + $params = array('Text' => $string); + $attempts = 0; + while ($attempts < 3) { + try { + $response = $this->soap->SimpleProfanityFilter($params); + break; + } catch (SoapFault $e) { + $attempts++; + sleep(1); + } + } + if ($attempts == 3) { + return $string; + } + return $response->SimpleProfanityFilterResult->CleanText; + } + + /** + * Processes events before they are dispatched and either censors their + * content or discards them if they contain potentially offensive + * content. + * + * @return void + */ + public function preDispatch() + { + $events = $this->events->getEvents(); + + foreach ($events as $event) { + switch ($event->getType()) { + case Phergie_Event_Request::TYPE_PRIVMSG: + case Phergie_Event_Request::TYPE_ACTION: + case Phergie_Event_Request::TYPE_NOTICE: + $text = $event->getArgument(1); + $clean = $this->cleanString($text); + if ($text != $clean) { + if ($this->config['censor.mode'] == 'censor') { + $event->setArgument(1, $clean); + } else { + $this->events->removeEvent($event); + } + } + break; + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail.php new file mode 100644 index 000000000..eafeb657b --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail.php @@ -0,0 +1,71 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Cocktail + * @author Phergie Development Team <team@phergie.org> + * @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_Cocktail + */ + +/** + * Processes requests to serve users cocktail. + * + * @category Phergie + * @package Phergie_Plugin_Cocktail + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Cocktail + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Cocktail extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user a cocktail. + * + * @param string $request Request including the target and an optional + * suggestion of what cocktail to serve + * + * @return void + */ + public function onCommandCocktail($request) + { + $format = $this->getConfig( + 'cocktail.format', + 'throws %target% %article% %item%.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Cocktail/cocktail.db', + 'cocktail', + $format, + $request, + true + ); + } +} + diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail/db.php new file mode 100644 index 000000000..2e61dd0bd --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cocktail/db.php @@ -0,0 +1,74 @@ +<?php + +if (!defined('__DIR__')) { + define('__DIR__', dirname(__FILE__)); +} + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/cocktail.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE cocktail (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX cocktail_name ON cocktail (name)'); +$insert = $db->prepare('INSERT INTO cocktail (name, link) VALUES (:name, :link)'); + +// Get raw webtender.com data set +echo 'Downloading webtender.com data set', PHP_EOL; +$start = 1; +do { + $file = __DIR__ . '/' . $start . '.html'; + if (file_exists($file)) { + continue; + } + copy( + 'http://www.webtender.com/db/browse?level=2&dir=drinks&char=%2A&start=' . $start, + $file + ); + if (!isset($limit)) { + $contents = file_get_contents($file); + preg_match('/([0-9]+) found/', $contents, $match); + $limit = $match[1] + (150 - ($match[1] % 150)); + } + echo 'Got records ', $start, ' - ', min($start + 150, $limit), ' of ', $limit, PHP_EOL; + $start += 150; +} while ($start < $limit); + +// Extract data from data set +$start = 1; +while ($start < $limit) { + echo 'Processing ', $start, ' - ', min($start + 150, $limit), ' of ', $limit, PHP_EOL; + + $file = __DIR__ . '/' . $start . '.html'; + $contents = file_get_contents($file); + $contents = tidy_repair_string($contents); + libxml_use_internal_errors(true); + $doc = new DOMDocument; + $doc->loadHTML($contents); + libxml_clear_errors(); + $xpath = new DOMXPath($doc); + + $cocktails = $xpath->query('//li/a'); + $db->beginTransaction(); + foreach ($cocktails as $cocktail) { + $name = $cocktail->nodeValue; + $name = preg_replace('/ The$|^The |\s*\([^)]+\)\s*| #[0-9]+$/', '', $name); + $name = html_entity_decode($name); + $link = 'http://www.webtender.com' . $cocktail->getAttribute('href'); + $insert->execute(array($name, $link)); + } + $db->commit(); + + $start += 150; +} + +// Clean up +echo 'Cleaning up', PHP_EOL; +$start = 1; +while ($start < $limit) { + $file = __DIR__ . '/' . $start . '.html'; + unlink($file); + $start += 150; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Command.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Command.php new file mode 100644 index 000000000..2058977ed --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Command.php @@ -0,0 +1,146 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Command + * @author Phergie Development Team <team@phergie.org> + * @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_Command + */ + +/** + * Handles parsing and execution of commands sent by users via messages sent + * to channels in which the bot is present or directly to the bot. + * + * @category Phergie + * @package Phergie_Plugin_Command + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Command + * @uses extension reflection + * @uses Phergie_Plugin_Message pear.phergie.org + */ +class Phergie_Plugin_Command extends Phergie_Plugin_Abstract +{ + /** + * Prefix for command method names + * + * @var string + */ + const METHOD_PREFIX = 'onCommand'; + + /** + * Cache for command lookups used to confirm that methods exist and + * parameter counts match + * + * @var array + */ + protected $methods = array(); + + /** + * Load the Message plugin + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Message'); + } + + /** + * Populates the methods cache. + * + * @return void + */ + public function populateMethodCache() + { + foreach ($this->getPluginHandler()->getPlugins() as $plugin) { + $reflector = new ReflectionClass($plugin); + foreach ($reflector->getMethods() as $method) { + $name = $method->getName(); + if (strpos($name, self::METHOD_PREFIX) === 0 + && !isset($this->methods[$name]) + ) { + $this->methods[$name] = array( + 'total' => $method->getNumberOfParameters(), + 'required' => $method->getNumberOfRequiredParameters() + ); + } + } + } + } + + /** + * Parses a given message and, if its format corresponds to that of a + * defined command, calls the handler method for that command with any + * provided parameters. + * + * @return void + */ + public function onPrivmsg() + { + // Populate the methods cache if needed + if (empty($this->methods)) { + $this->populateMethodCache(); + } + + // Check for a prefixed message + $msg = $this->plugins->message->getMessage(); + if ($msg === false) { + return; + } + + // Separate the command and arguments + $parsed = preg_split('/\s+/', $msg, 2); + $command = strtolower(array_shift($parsed)); + $args = count($parsed) ? array_shift($parsed) : ''; + + // Resolve aliases to their corresponding commands + $aliases = $this->getConfig('command.aliases', array()); + $result = preg_grep('/^' . preg_quote($command, '/') . '$/i', array_keys($aliases)); + if ($result) { + $command = $aliases[array_shift($result)]; + } + + // Check to ensure the command exists + $method = self::METHOD_PREFIX . ucfirst($command); + if (empty($this->methods[$method])) { + return; + } + + // If no arguments are passed... + if (empty($args)) { + + // If the method requires no arguments, call it + if (empty($this->methods[$method]['required'])) { + $this->getPluginHandler()->$method(); + } + + } else { + // If arguments are passed... + + // Parse the arguments + $args = preg_split('/\s+/', $args, $this->methods[$method]['total']); + + // If the minimum arguments are passed, call the method + if ($this->methods[$method]['required'] <= count($args)) { + call_user_func_array( + array($this->getPluginHandler(), $method), + $args + ); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie.php new file mode 100644 index 000000000..4bc2ceecb --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie.php @@ -0,0 +1,69 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Cookie + * @author Phergie Development Team <team@phergie.org> + * @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_Cookie + */ + +/** + * Processes requests to serve users cookies. + * + * @category Phergie + * @package Phergie_Plugin_Cookie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Cookie + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Cookie extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user a cookie. + * + * @param string $request Request including the target and an optional + * suggestion of what cookie to serve + * + * @return void + */ + public function onCommandCookie($request) + { + $format = $this->getConfig( + 'cookie.format', + 'throws %target% %article% %item%.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Cookie/cookie.db', + 'cookies', + $format, + $request + ); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie/db.php new file mode 100644 index 000000000..27763159c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cookie/db.php @@ -0,0 +1,55 @@ +<?php + +if (!defined('__DIR__')) { + define('__DIR__', dirname(__FILE__)); +} + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/cookie.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE cookies (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX cookie_name ON cookies (name)'); +$insert = $db->prepare('INSERT INTO cookies (name, link) VALUES (:name, :link)'); + +// Get Cookies list from http://en.wikipedia.org/wiki/List_of_cookies +echo 'Downloading data from Wikipedia', PHP_EOL; +$file = __DIR__ . '/cookieslist.txt'; +if (!file_exists($file)) { + copy('http://en.wikipedia.org/wiki/List_of_cookies', $file); +} +$contents = file_get_contents($file); + +// Extract data from data set +echo 'Processing Wikipedia\'s cookies list', PHP_EOL; +$contents = tidy_repair_string($contents); +libxml_use_internal_errors(true); +$doc = new DOMDocument; +$doc->loadHTML($contents); +libxml_clear_errors(); +$xpath = new DOMXPath($doc); + +$cookies = $xpath->query('//table[@width="90%"]/tr/td[1]/a'); + +foreach ($cookies as $cookie) { + $name = $cookie->textContent; + $name = str_replace( + array('(',')',"\n", 'cookies'), + array('','', ' ', 'cookie'), + $name + ); + $name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $name); + $name = trim($name); + $name = rtrim($name, 's'); + + $link = 'http://en.wikipedia.org' . $cookie->getAttribute('href'); + $insert->execute(array($name, $link)); + echo 'added [' . $name . '] -> '. $link . PHP_EOL; +} + +// Clean up +echo 'Cleaning up', PHP_EOL; +unlink($file); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Cron.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cron.php new file mode 100644 index 000000000..d24910fa3 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Cron.php @@ -0,0 +1,147 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Cron + * @author Phergie Development Team <team@phergie.org> + * @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_Cron + */ + +/** + * Allows callbacks to be registered for asynchronous execution. + * + * @category Phergie + * @package Phergie_Plugin_Cron + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Cron + */ +class Phergie_Plugin_Cron extends Phergie_Plugin_Abstract +{ + /** + * Array of all registered callbacks with delays and arguments + * + * @var array + */ + protected $callbacks; + + /** + * Returns a human-readable representation of a callback for debugging + * purposes. + * + * @param callback $callback Callback to analyze + * + * @return string|boolean String representation of the callback or FALSE + * if the specified value is not a valid callback + */ + protected function getCallbackString($callback) + { + if (!is_callable($callback)) { + return false; + } + + if (is_array($callback)) { + $class = is_string($callback[0]) ? + $callback[0] : get_class($callback[0]); + $method = $class . '::' . $callback[1]; + return $method; + } + + return $callback; + } + + /** + * Registers a callback for execution sometime after a given delay + * relative to now. + * + * @param callback $callback Callback to be registered + * @param int $delay Delay in seconds from now when the callback + * will be executed + * @param array $arguments Arguments to pass to the callback when + * it's executed + * @param bool $repeat TRUE to automatically re-register the + * callback for the same delay after it's executed, FALSE + * otherwise + * + * @return void + */ + public function registerCallback($callback, $delay, + array $arguments = array(), $repeat = false) + { + $callbackString = $this->getCallbackString($callback); + if ($callbackString === false) { + echo 'DEBUG(Cron): Invalid callback specified - ', + var_export($callback, true), PHP_EOL; + return; + } + + $registered = time(); + $scheduled = $registered + $delay; + + $this->callbacks[] = array( + 'callback' => $callback, + 'delay' => $delay, + 'arguments' => $arguments, + 'registered' => $registered, + 'scheduled' => $scheduled, + 'repeat' => $repeat, + ); + + echo 'DEBUG(Cron): Callback ', $callbackString, + ' scheduled for ', date('H:i:s', $scheduled), PHP_EOL; + } + + /** + * Handles callback execution. + * + * @return void + */ + public function onTick() + { + $time = time(); + foreach ($this->callbacks as $key => &$callback) { + $callbackString = $this->getCallbackString($callback); + + $scheduled = $callback['scheduled']; + if ($time < $scheduled) { + continue; + } + + if (empty($callback['arguments'])) { + call_user_func($callback['callback']); + } else { + call_user_func_array( + $callback['callback'], + $callback['arguments'] + ); + } + + echo 'DEBUG(Cron): Callback ', $callbackString, + ' scheduled for ', date('H:i:s', $scheduled), ',', + ' executed at ', date('H:i:s', $now), PHP_EOL; + + if ($callback['repeat']) { + $callback['scheduled'] = $time + $callback['delay']; + echo 'DEBUG(Cron): Callback ', $callbackString, + ' scheduled for ', date('H:i:s', $callback['scheduled']), + PHP_EOL; + } else { + echo 'DEBUG(Cron): Callback ', $callbackString, + ' removed from callback list', PHP_EOL; + unset($this->callbacks[$key]); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Ctcp.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ctcp.php new file mode 100755 index 000000000..e79fcf13e --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ctcp.php @@ -0,0 +1,91 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Ctcp + * @author Phergie Development Team <team@phergie.org> + * @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_Ctcp + */ + +/** + * Responds to various CTCP requests sent by the server and users. + * + * @category Phergie + * @package Phergie_Plugin_Ctcp + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Ctcp + * @link http://www.irchelp.org/irchelp/rfc/ctcpspec.html + */ +class Phergie_Plugin_Ctcp extends Phergie_Plugin_Abstract +{ + /** + * Responds to a CTCP TIME request from a user with the current local + * time. + * + * @return void + */ + public function onTime() + { + $source = $this->getEvent()->getSource(); + $this->doTime($source, strftime('%c %z')); + } + + /** + * Responds to a CTCP VERSION request from a user with the codebase + * version. + * + * @return void + */ + public function onVersion() + { + $source = $this->getEvent()->getSource(); + $msg = 'Phergie ' . Phergie_Bot::VERSION . ' (http://phergie.org)'; + $this->doVersion($source, $msg); + } + + /** + * Responds to a CTCP PING request from a user. + * + * @return void + */ + public function onCtcpPing() + { + $event = $this->getEvent(); + $source = $event->getSource(); + $handshake = $event->getArgument(1); + $this->doPing($source, $handshake); + } + + /** + * Responds to a CTCP FINGER request from a user. + * + * @return void + */ + public function onFinger() + { + $connection = $this->getConnection(); + $name = $connection->getNick(); + $realname = $connection->getRealname(); + $username = $connection->getUsername(); + + $finger + = (empty($realname) ? $realname : $name) . + ' (' . (!empty($username) ? $username : $name) . ')'; + + $this->doFinger($source, $finger); + } +} +?> diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Daddy.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Daddy.php new file mode 100644 index 000000000..ed258e10f --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Daddy.php @@ -0,0 +1,56 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Daddy + * @author Phergie Development Team <team@phergie.org> + * @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_Daddy + */ + +/** + * Simply responds to messages addressed to the bot that contain the phrase + * "Who's your daddy?" and related variations. + * + * @category Phergie + * @package Phergie_Plugin_Daddy + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Daddy + */ +class Phergie_Plugin_Daddy extends Phergie_Plugin_Abstract +{ + /** + * Checks messages for the question to which it should respond and sends a + * response when appropriate + * + * @return void + */ + public function onPrivmsg() + { + $config = $this->getConfig(); + $prefix = $config['command.prefix']; + $event = $this->getEvent(); + $text = $event->getArgument(1); + $target = $event->getNick(); + $source = $event->getSource(); + $pattern + = '/' . preg_quote($prefix) . + '\s*?who\'?s y(?:our|a) ([^?]+)\??/iAD'; + if (preg_match($pattern, $text, $m)) { + $msg = 'You\'re my ' . $m[1] . ', ' . $target . '!'; + $this->doPrivmsg($source, $msg); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Encoding.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Encoding.php new file mode 100644 index 000000000..419322bd7 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Encoding.php @@ -0,0 +1,182 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Encoding + * @author Phergie Development Team <team@phergie.org> + * @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_Encoding + */ + +/** + * Handles decoding markup entities and converting text between character + * encodings. + * + * @category Phergie + * @package Phergie_Plugin_Encoding + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Encoding + */ +class Phergie_Plugin_Encoding extends Phergie_Plugin_Abstract +{ + /** + * Lookup table for entity conversions not supported by + * html_entity_decode() + * + * @var array + * @link http://us.php.net/manual/en/function.get-html-translation-table.php#73409 + * @link http://us.php.net/manual/en/function.get-html-translation-table.php#73410 + */ + protected static $entities = array( + 'α' => 913, + ''' => 39, + 'β' => 914, + '•' => 149, + 'χ' => 935, + 'ˆ' => 94, + 'δ' => 916, + 'ε' => 917, + 'η' => 919, + 'ƒ' => 402, + 'γ' => 915, + 'ι' => 921, + 'κ' => 922, + 'λ' => 923, + '“' => 147, + '‹' => 139, + '‘' => 145, + '—' => 151, + '−' => 45, + 'μ' => 924, + '–' => 150, + 'ν' => 925, + 'œ' => 140, + 'ω' => 937, + 'ο' => 927, + 'φ' => 934, + 'π' => 928, + 'ϖ' => 982, + 'ψ' => 936, + '”' => 148, + 'ρ' => 929, + '›' => 155, + '’' => 146, + 'š' => 138, + 'σ' => 931, + 'ς' => 962, + 'τ' => 932, + 'θ' => 920, + 'ϑ' => 977, + '˜' => 126, + '™' => 153, + 'ϒ' => 978, + 'υ' => 933, + 'ξ' => 926, + 'ÿ' => 159, + 'ζ' => 918, + ); + + /** + * Decodes markup entities in a given string. + * + * @param string $string String containing markup entities + * @param string $charset Optional character set name to use in decoding + * entities, defaults to UTF-8 + * + * @return string String with markup entities decoded + */ + public function decodeEntities($string, $charset = 'UTF-8') + { + $string = str_ireplace( + array_keys(self::$entities), + array_map('chr', self::$entities), + $string + ); + $string = html_entity_decode($string, ENT_QUOTES, $charset); + $string = preg_replace( + array('/�*([0-9]+);/me', '/�*([a-f0-9]+);/mei'), + array('$this->codeToUtf(\\1)', '$this->codeToUtf(hexdec(\\1))'), + $string + ); + return $string; + } + + /** + * Converts a given unicode to its UTF-8 equivalent. + * + * @param int $code Code to convert + * @return string Character corresponding to code + */ + public function codeToUtf8($code) + { + $code = (int) $code; + switch ($code) { + // 1 byte, 7 bits + case 0: + return chr(0); + case ($code & 0x7F): + return chr($code); + + // 2 bytes, 11 bits + case ($code & 0x7FF): + return chr(0xC0 | (($code >> 6) & 0x1F)) . + chr(0x80 | ($code & 0x3F)); + + // 3 bytes, 16 bits + case ($code & 0xFFFF): + return chr(0xE0 | (($code >> 12) & 0x0F)) . + chr(0x80 | (($code >> 6) & 0x3F)) . + chr(0x80 | ($code & 0x3F)); + + // 4 bytes, 21 bits + case ($code & 0x1FFFFF): + return chr(0xF0 | ($code >> 18)) . + chr(0x80 | (($code >> 12) & 0x3F)) . + chr(0x80 | (($code >> 6) & 0x3F)) . + chr(0x80 | ($code & 0x3F)); + } + } + + /** + * Transliterates characters in a given string where possible. + * + * @param string $string String containing characters to + * transliterate + * @param string $charsetFrom Optional character set of the string, + * defaults to UTF-8 + * @param string $charsetTo Optional character set to which the string + * should be converted, defaults to ISO-8859-1 + * + * @return string String with characters transliterated or the original + * string if transliteration was not possible + */ + public function transliterate($string, $charsetFrom = 'UTF-8', $charsetTo = 'ISO-8859-1') + { + // @link http://pecl.php.net/package/translit + if (function_exists('transliterate')) { + $string = transliterate($string, array('han_transliterate', 'diacritical_remove'), $charsetFrom, $charsetTo); + } elseif (function_exists('iconv')) { + $string = iconv($charsetFrom, $charsetTo . '//TRANSLIT', $string); + } else { + // @link http://stackoverflow.com/questions/1284535/php-transliteration/1285491#1285491 + $string = preg_replace( + '~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', + '$1', + htmlentities($string, ENT_COMPAT, $charsetFrom) + ); + } + return $string; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Exception.php new file mode 100755 index 000000000..ca4d53fed --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Exception.php @@ -0,0 +1,120 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to plugin handling. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Plugin_Exception extends Phergie_Exception +{ + /** + * Error indicating that a path containing plugins was specified, but + * did not reference a readable directory + */ + const ERR_DIRECTORY_NOT_READABLE = 1; + + /** + * Error indicating that an attempt was made to locate the class for a + * specified plugin, but the class could not be found + */ + const ERR_CLASS_NOT_FOUND = 2; + + /** + * Error indicating that an attempt was made to locate the class for a + * specified plugin, but that the found class did not extend the base + * plugin class + */ + const ERR_INCORRECT_BASE_CLASS = 3; + + /** + * Error indicating that an attempt was made to locate the class for a + * specified plugin, but that the found class cannot be instantiated + */ + const ERR_CLASS_NOT_INSTANTIABLE = 4; + + /** + * Error indicating that an attempt was made to access a plugin that had + * not been loaded and autoloading was not enabled to load it + */ + const ERR_PLUGIN_NOT_LOADED = 5; + + /** + * Error indicating that an attempt was made to access the configuration + * handler before one had been set + */ + const ERR_NO_CONFIG_HANDLER = 6; + + /** + * Error indicating that an attempt was made to access the plugin + * handler before one had been set + */ + const ERR_NO_PLUGIN_HANDLER = 7; + + /** + * Error indicating that an attempt was made to access the event + * handler before one had been set + */ + const ERR_NO_EVENT_HANDLER = 8; + + /** + * Error indicating that an attempt was made to access the connection + * before one had been set + */ + const ERR_NO_CONNECTION = 9; + + /** + * Error indicating that an attempt was made to access the current + * incoming event before one had been set + */ + const ERR_NO_EVENT = 10; + + /** + * Error indicating that a dependency of the plugin was unavailable at + * the time that an attempt was made to load it + */ + const ERR_REQUIREMENT_UNSATISFIED = 11; + + /** + * Error indicating that a call was made to a nonexistent plugin method + * and that its __call() implementation did not process that call as an + * attempt to trigger an event - this is intended to aid in debugging of + * such situations + */ + const ERR_INVALID_CALL = 12; + + /** + * Error indicating that a fatal runtime issue was encountered within a + * plugin + */ + const ERR_FATAL_ERROR = 13; + + /** + * Error indicating that a class specified to be used for iterating + * plugins cannot be found by the autoloader or does not extend + * FilterIterator + */ + const ERR_INVALID_ITERATOR_CLASS = 14; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Google.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Google.php new file mode 100644 index 000000000..d2a9d4dce --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Google.php @@ -0,0 +1,459 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Google + * @author Phergie Development Team <team@phergie.org> + * @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_Google + */ + +/** + * Provides commands used to access several services offered by Google + * including search, translation, weather, maps, and currency and general + * value unit conversion. + * + * @category Phergie + * @package Phergie_Plugin_Google + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Google + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + * @uses Phergie_Plugin_Temperature pear.phergie.org + */ +class Phergie_Plugin_Google extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $plugins->getPlugin('Http'); + $plugins->getPlugin('Weather'); + } + + /** + * Returns the first result of a Google search. + * + * @param string $query Search term + * + * @return void + * @todo Implement use of URL shortening here + */ + public function onCommandG($query) + { + $url = 'http://ajax.googleapis.com/ajax/services/search/web'; + $params = array( + 'v' => '1.0', + 'q' => $query + ); + $response = $this->plugins->http->get($url, $params); + $json = $response->getContent()->responseData; + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + if ($json->cursor->estimatedResultCount > 0) { + $msg + = $nick + . ': [ ' + . $json->results[0]->titleNoFormatting + . ' ] - ' + . $json->results[0]->url + . ' - More results: ' + . $json->cursor->moreResultsUrl; + $this->doPrivmsg($source, $msg); + } else { + $msg = $nick . ': No results for this query.'; + $this->doPrivmsg($source, $msg); + } + } + + /** + * Performs a Google Count search for the given term. + * + * @param string $query Search term + * + * @return void + */ + public function onCommandGc($query) + { + $url = 'http://ajax.googleapis.com/ajax/services/search/web'; + $params = array( + 'v' => '1.0', + 'q' => $query + ); + $response = $this->plugins->http->get($url, $params); + $json = $response->getContent()->responseData->cursor; + $count = $json->estimatedResultCount; + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + if ($count) { + $msg + = $nick . ': ' . + number_format($count, 0) . + ' estimated results for ' . $query; + $this->doPrivmsg($source, $msg); + } else { + $this->doPrivmsg($source, $nick . ': No results for this query.'); + } + } + + /** + * Performs a Google Translate search for the given term. + * + * @param string $from Language of the search term + * @param string $to Language to which the search term should be + * translated + * @param string $query Term to translate + * + * @return void + */ + public function onCommandGt($from, $to, $query) + { + $url = 'http://ajax.googleapis.com/ajax/services/language/translate'; + $params = array( + 'v' => '1.0', + 'q' => $query, + 'langpair' => $from . '|' . $to + ); + $response = $this->plugins->http->get($url, $params); + $json = $response->getContent(); + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + if (empty($json->responseData->translatedText)) { + $this->doPrivmsg($source, $nick . ': ' . $json->responseDetails); + } else { + $this->doPrivmsg( + $source, + $nick . ': ' . $json->responseData->translatedText + ); + } + } + + /** + * Performs a Google Weather search for the given term. + * + * @param string $location Location to search for + * @param int $offset Optional day offset from the current date + * between 0 and 3 to get the forecast + * + * @return void + */ + public function onCommandGw($location, $offset = null) + { + $url = 'http://www.google.com/ig/api'; + $params = array( + 'weather' => $location, + 'hl' => $this->getConfig('google.lang', 'en'), + 'oe' => 'UTF-8' + ); + $response = $this->plugins->http->get($url, $params); + $xml = $response->getContent()->weather; + + $event = $this->getEvent(); + $source = $event->getSource(); + $msg = ''; + if ($event->isInChannel()) { + $msg .= $event->getNick() . ': '; + } + + if (isset($xml->problem_cause)) { + $msg .= $xml->problem_cause->attributes()->data[0]; + $this->doPrivmsg($source, $msg); + return; + } + + $temperature = $this->plugins->getPlugin('Temperature'); + + $forecast = $xml->forecast_information; + $city = $forecast->city->attributes()->data[0]; + $zip = $forecast->postal_code->attributes()->data[0]; + + if ($offset !== null) { + $offset = (int) $offset; + if ($offset < 0) { + $this->doNotice($source, 'Past weather data is not available'); + return; + } elseif ($offset > 3) { + $this->doNotice($source, 'Future weather data is limited to 3 days from today'); + return; + } + + $linha = $xml->forecast_conditions[$offset]; + $low = $linha->low->attributes()->data[0]; + $high = $linha->high->attributes()->data[0]; + $units = $forecast->unit_system->attributes()->data[0]; + $condition = $linha->condition->attributes()->data[0]; + $day = $linha->day_of_week->attributes()->data[0]; + + $date = ($offset == 0) ? time() : strtotime('next ' . $day); + $day = ucfirst($day) . ' ' . date('n/j/y', $date); + + if ($units == 'US') { + $lowF = $low; + $lowC = $temperature->convertFahrenheitToCelsius($low); + $highF = $high; + $highC = $temperature->convertFahrenheitToCelsius($high); + } else { + $lowC = $low; + $lowF = $temperature->convertCelsiusToFahrenheit($lowC); + $highC = $high; + $highF = $temperature->convertCelsiusToFahrenheit($high); + } + + $msg .= 'Forecast for ' . $city . ' (' . $zip . ')' + . ' on ' . $day . ' ::' + . ' Low: ' . $lowF . 'F/' . $lowC . 'C,' + . ' High: ' . $highF . 'F/' . $highC . 'C,' + . ' Conditions: ' . $condition; + } else { + $conditions = $xml->current_conditions; + $condition = $conditions->condition->attributes()->data[0]; + $tempF = $conditions->temp_f->attributes()->data[0]; + $tempC = $conditions->temp_c->attributes()->data[0]; + $humidity = $conditions->humidity->attributes()->data[0]; + $wind = $conditions->wind_condition->attributes()->data[0]; + $time = $forecast->current_date_time->attributes()->data[0]; + $time = date('n/j/y g:i A', strtotime($time)) . ' +0000'; + + $hiF = $temperature->getHeatIndex($tempF, $humidity); + $hiC = $temperature->convertFahrenheitToCelsius($hiF); + + $msg .= 'Weather for ' . $city . ' (' . $zip . ') -' + . ' Temperature: ' . $tempF . 'F/' . $tempC . 'C,' + . ' ' . $humidity . ',' + . ' Heat Index: ' . $hiF . 'F/' . $hiC . 'C,' + . ' Conditions: ' . $condition . ',' + . ' Updated: ' . $time; + } + + $this->doPrivmsg($source, $msg); + } + + /** + * Performs a Google Maps search for the given term. + * + * @param string $location Location to search for + * + * @return void + */ + public function onCommandGmap($location) + { + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + + $location = utf8_encode($location); + $url = 'http://maps.google.com/maps/geo'; + $params = array( + 'q' => $location, + 'output' => 'json', + 'gl' => $this->getConfig('google.lang', 'en'), + 'sensor' => 'false', + 'oe' => 'utf8', + 'mrt' => 'all', + 'key' => $this->getConfig('google.key') + ); + $response = $this->plugins->http->get($url, $params); + $json = $response->getContent(); + if (!empty($json)) { + $qtd = count($json->Placemark); + if ($qtd > 1) { + if ($qtd <= 3) { + foreach ($json->Placemark as $places) { + $xy = $places->Point->coordinates; + $address = utf8_decode($places->address); + $url = 'http://maps.google.com/maps?sll=' . $xy[1] . ',' + . $xy[0] . '&z=15'; + $msg = $nick . ' -> ' . $address . ' - ' . $url; + $this->doPrivmsg($source, $msg); + } + } else { + $msg + = $nick . + ', there are a lot of places with that query.' . + ' Try to be more specific!'; + $this->doPrivmsg($source, $msg); + } + } elseif ($qtd == 1) { + $xy = $json->Placemark[0]->Point->coordinates; + $address = utf8_decode($json->Placemark[0]->address); + $url = 'http://maps.google.com/maps?sll=' . $xy[1] . ',' . $xy[0] + . '&z=15'; + $msg = $nick . ' -> ' . $address . ' - ' . $url; + $this->doPrivmsg($source, $msg); + } else { + $this->doPrivmsg($source, $nick . ', I found nothing.'); + } + } else { + $this->doPrivmsg($source, $nick . ', we have a problem.'); + } + } + + /** + * Perform a Google Convert query to convert a value from one metric to + * another. + * + * @param string $value Value to convert + * @param string $from Source metric + * @param string $to Destination metric + * + * @return void + */ + public function onCommandGconvert($value, $from, $to) + { + $url = 'http://www.google.com/finance/converter'; + $params = array( + 'a' => $value, + 'from' => $from, + 'to' => $to + ); + $response = $this->plugins->http->get($url, $params); + $contents = $response->getContent(); + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + if ($contents) { + libxml_use_internal_errors(true); + $doc = new DOMDocument; + $doc->loadHTML($contents); + libxml_clear_errors(); + $xpath = new DOMXPath($doc); + $result = $xpath->query('//div[@id="currency_converter_result"]'); + $div = $result->item(0); + $text = rtrim($div->textContent); + $this->doPrivmsg($source, $text); + } + } + + /** + * Performs a Google search to convert a value from one unit to another. + * + * @param string $query Query of the form "[quantity] [unit] to [unit2]" + * + * @return void + * + * @pluginCmd [quantity] [unit] to [unit2] Convert a value from one + * metric to another + */ + public function onCommandConvert($query) + { + $url = 'http://www.google.com/search?q=' . urlencode($query); + $response = $this->plugins->http->get($url); + $contents = $response->getContent(); + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + + if ($response->isError()) { + $code = $response->getCode(); + $message = $response->getMessage(); + $this->doNotice($nick, 'ERROR: ' . $code . ' ' . $message); + return; + } + + $start = strpos($contents, '<h3 class=r>'); + if ($start !== false) { + $end = strpos($contents, '</b>', $start); + $text = strip_tags(substr($contents, $start, $end - $start)); + $text = str_replace( + array(chr(195), chr(151), chr(160)), + array('x', '', ' '), + $text + ); + } + + if (isset($text)) { + $this->doPrivmsg($source, $nick . ': ' . $text); + } else { + $this->doNotice($nick, 'Sorry I couldn\'t find an answer.'); + } + } + + + /** + * Returns the first definition of a Google Dictionary search. + * + * @param string $query Word to get the definition + * + * @return void + * @todo Implement use of URL shortening here + */ + public function onCommandDefine($query) + { + $lang = $this->getConfig('google.lang', 'en'); + $url = 'http://www.google.com/dictionary/json'; + $params = array( + 'callback' => 'result', + 'q' => $query, + 'sl' => $lang, + 'tl' => $lang, + 'restrict' => 'pr,de' + ); + $response = $this->plugins->http->get($url, $params); + $json = $response->getContent(); + + // Remove some garbage from the JSON and decode it + $json = str_replace(array('result(', ',200,null)'), '', $json); + $json = str_replace('"', '¿?¿', $json); + $json = strip_tags(stripcslashes($json)); + $json = str_replace('"', "'", $json); + $json = str_replace('¿?¿', '"', $json); + $json = json_decode($json); + + $event = $this->getEvent(); + $source = $event->getSource(); + $nick = $event->getNick(); + if (!empty($json->webDefinitions)) { + $results = 0; + foreach ($json->primaries[0]->entries as $entry) { + if ($entry->type == 'meaning') { + $results++; + if (empty($text)) { + foreach ($entry->terms as $term) { + if ($term->type == 'text') { + $text = trim($term->text); + } + } + } + } + } + $more = $results > 1 ? ($results - 1) . ' ' : ''; + $lang_code = substr($lang, 0, 2); + $msg = $nick . ': ' . $text + . ' - You can find ' . $more . 'more results at ' + . 'http://www.google.com/dictionary' + . '?aq=f' + . '&langpair=' . $lang_code . '%7C' . $lang_code + . '&q=' . $query + . '&hl=' . $lang_code; + $this->doPrivmsg($source, $msg); + } else { + if ($lang != 'en'){ + $lang = 'en'; + $this->onCommandDefine($query); + } else { + $msg = $nick . ': No results for this query.'; + $this->doPrivmsg($source, $msg); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php new file mode 100755 index 000000000..c3086587b --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php @@ -0,0 +1,501 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Handles on-demand loading of, iteration over, and access to plugins. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Plugin_Handler implements IteratorAggregate, Countable +{ + /** + * Current list of plugin instances + * + * @var array + */ + protected $plugins; + + /** + * Paths in which to search for plugin class files + * + * @var array + */ + protected $paths; + + /** + * Flag indicating whether plugin classes should be instantiated on + * demand if they are requested but no instance currently exists + * + * @var bool + */ + protected $autoload; + + /** + * Phergie_Config instance that should be passed in to any plugin + * instantiated within the handler + * + * @var Phergie_Config + */ + protected $config; + + /** + * Phergie_Event_Handler instance that should be passed in to any plugin + * instantiated within the handler + * + * @var Phergie_Event_Handler + */ + protected $events; + + /** + * Name of the class to use for iterating over all currently loaded + * plugins + * + * @var string + */ + protected $iteratorClass = 'Phergie_Plugin_Iterator'; + + /** + * Constructor to initialize class properties and add the path for core + * plugins. + * + * @param Phergie_Config $config configuration to pass to any + * instantiated plugin + * @param Phergie_Event_Handler $events event handler to pass to any + * instantiated plugin + * + * @return void + */ + public function __construct( + Phergie_Config $config, + Phergie_Event_Handler $events + ) { + $this->config = $config; + $this->events = $events; + + $this->plugins = array(); + $this->paths = array(); + $this->autoload = false; + + if (!empty($config['plugins.paths'])) { + foreach ($config['plugins.paths'] as $dir => $prefix) { + $this->addPath($dir, $prefix); + } + } + + $this->addPath(dirname(__FILE__), 'Phergie_Plugin_'); + } + + + /** + * Adds a path to search for plugin class files. Paths are searched in + * the reverse order in which they are added. + * + * @param string $path Filesystem directory path + * @param string $prefix Optional class name prefix corresponding to the + * path + * + * @return Phergie_Plugin_Handler Provides a fluent interface + * @throws Phergie_Plugin_Exception + */ + public function addPath($path, $prefix = '') + { + if (!is_readable($path)) { + throw new Phergie_Plugin_Exception( + 'Path "' . $path . '" does not reference a readable directory', + Phergie_Plugin_Exception::ERR_DIRECTORY_NOT_READABLE + ); + } + + $this->paths[] = array( + 'path' => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, + 'prefix' => $prefix + ); + + return $this; + } + + /** + * Returns metadata corresponding to a specified plugin. + * + * @param string $plugin Short name of the plugin class + * + * @throws Phergie_Plugin_Exception Class file can't be found + * + * @return array|boolean Associative array containing the path to the + * class file and its containing directory as well as the full + * class name + */ + public function getPluginInfo($plugin) + { + foreach (array_reverse($this->paths) as $path) { + $file = $path['path'] . $plugin . '.php'; + if (file_exists($file)) { + $path = array( + 'dir' => $path['path'], + 'file' => $file, + 'class' => $path['prefix'] . $plugin, + ); + return $path; + } + } + + // If the class can't be found, display an error + throw new Phergie_Plugin_Exception( + 'Class file for plugin "' . $plugin . '" cannot be found', + Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND + ); + } + + /** + * Adds a plugin instance to the handler. + * + * @param string|Phergie_Plugin_Abstract $plugin Short name of the + * plugin class or a plugin object + * @param array $args Optional array of + * arguments to pass to the plugin constructor if a short name is + * passed for $plugin + * + * @return Phergie_Plugin_Abstract New plugin instance + */ + public function addPlugin($plugin, array $args = null) + { + // If a short plugin name is specified... + if (is_string($plugin)) { + $index = strtolower($plugin); + if (isset($this->plugins[$index])) { + return $this->plugins[$index]; + } + + // Attempt to locate and load the class + $info = $this->getPluginInfo($plugin); + $file = $info['file']; + $class = $info['class']; + include_once $file; + if (!class_exists($class, false)) { + throw new Phergie_Plugin_Exception( + 'File "' . $file . '" does not contain class "' . $class . '"', + Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND + ); + } + + // Check to ensure the class is a plugin class + if (!is_subclass_of($class, 'Phergie_Plugin_Abstract')) { + $msg + = 'Class for plugin "' . $plugin . + '" does not extend Phergie_Plugin_Abstract'; + throw new Phergie_Plugin_Exception( + $msg, + Phergie_Plugin_Exception::ERR_INCORRECT_BASE_CLASS + ); + } + + // Check to ensure the class can be instantiated + $reflection = new ReflectionClass($class); + if (!$reflection->isInstantiable()) { + throw new Phergie_Plugin_Exception( + 'Class for plugin "' . $plugin . '" cannot be instantiated', + Phergie_Plugin_Exception::ERR_CLASS_NOT_INSTANTIABLE + ); + } + + // If the class is found, instantiate it + if (!empty($args)) { + $instance = $reflection->newInstanceArgs($args); + } else { + $instance = new $class; + } + + // Store the instance + $this->plugins[$index] = $instance; + $plugin = $instance; + + } elseif ($plugin instanceof Phergie_Plugin_Abstract) { + // If a plugin instance is specified... + + // Add the plugin instance to the list of plugins + $this->plugins[strtolower($plugin->getName())] = $plugin; + } + + // Configure and initialize the instance + $plugin->setPluginHandler($this); + $plugin->setConfig($this->config); + $plugin->setEventHandler($this->events); + $plugin->onLoad(); + + return $plugin; + } + + /** + * Adds multiple plugin instances to the handler. + * + * @param array $plugins List of elements where each is of the form + * 'ShortPluginName' or array('ShortPluginName', array($arg1, + * ..., $argN)) + * + * @return Phergie_Plugin_Handler Provides a fluent interface + */ + public function addPlugins(array $plugins) + { + foreach ($plugins as $plugin) { + if (is_array($plugin)) { + $this->addPlugin($plugin[0], $plugin[1]); + } else { + $this->addPlugin($plugin); + } + } + + return $this; + } + + /** + * Removes a plugin instance from the handler. + * + * @param string|Phergie_Plugin_Abstract $plugin Short name of the + * plugin class or a plugin object + * + * @return Phergie_Plugin_Handler Provides a fluent interface + */ + public function removePlugin($plugin) + { + if ($plugin instanceof Phergie_Plugin_Abstract) { + $plugin = $plugin->getName(); + } + $plugin = strtolower($plugin); + + unset($this->plugins[$plugin]); + + return $this; + } + + /** + * Returns the corresponding instance for a specified plugin, loading it + * if it is not already loaded and autoloading is enabled. + * + * @param string $name Short name of the plugin class + * + * @return Phergie_Plugin_Abstract Plugin instance + */ + public function getPlugin($name) + { + // If the plugin is loaded, return the instance + $lower = strtolower($name); + if (isset($this->plugins[$lower])) { + return $this->plugins[$lower]; + } + + // If autoloading is disabled, display an error + if (!$this->autoload) { + $msg + = 'Plugin "' . $name . '" has been requested, ' . + 'is not loaded, and autoload is disabled'; + throw new Phergie_Plugin_Exception( + $msg, + Phergie_Plugin_Exception::ERR_PLUGIN_NOT_LOADED + ); + } + + // If autoloading is enabled, attempt to load the plugin + $plugin = $this->addPlugin($name); + + // Return the added plugin + return $plugin; + } + + /** + * Returns the corresponding instances for multiple specified plugins, + * loading them if they are not already loaded and autoloading is + * enabled. + * + * @param array $names Optional list of short names of the plugin + * classes to which the returned plugin list will be limited, + * defaults to all presently loaded plugins + * + * @return array Associative array mapping lowercased plugin class short + * names to corresponding plugin instances + */ + public function getPlugins(array $names = array()) + { + if (empty($names)) { + return $this->plugins; + } + + $plugins = array(); + foreach ($names as $name) { + $plugins[strtolower($name)] = $this->getPlugin($name); + } + return $plugins; + } + + /** + * Returns whether or not at least one instance of a specified plugin + * class is loaded. + * + * @param string $name Short name of the plugin class + * + * @return bool TRUE if an instance exists, FALSE otherwise + */ + public function hasPlugin($name) + { + return isset($this->plugins[strtolower($name)]); + } + + /** + * Sets a flag used to determine whether plugins should be loaded + * automatically if they have not been explicitly loaded. + * + * @param bool $flag TRUE to have plugins autoload (default), FALSE + * otherwise + * + * @return Phergie_Plugin_Handler Provides a fluent interface. + */ + public function setAutoload($flag = true) + { + $this->autoload = $flag; + + return $this; + } + + /** + * Returns the value of a flag used to determine whether plugins should + * be loaded automatically if they have not been explicitly loaded. + * + * @return bool TRUE if autoloading is enabled, FALSE otherwise + */ + public function getAutoload() + { + return $this->autoload; + } + + /** + * Allows plugin instances to be accessed as properties of the handler. + * + * @param string $name Short name of the plugin + * + * @return Phergie_Plugin_Abstract Requested plugin instance + */ + public function __get($name) + { + return $this->getPlugin($name); + } + + /** + * Allows plugin instances to be detected as properties of the handler. + * + * @param string $name Short name of the plugin + * + * @return bool TRUE if the plugin is loaded, FALSE otherwise + */ + public function __isset($name) + { + return $this->hasPlugin($name); + } + + /** + * Allows plugin instances to be removed as properties of handler. + * + * @param string $name Short name of the plugin + * + * @return void + */ + public function __unset($name) + { + $this->removePlugin($name); + } + + /** + * Returns an iterator for all currently loaded plugin instances. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new $this->iteratorClass( + new ArrayIterator($this->plugins) + ); + } + + /** + * Sets the iterator class used for all currently loaded plugin + * instances. + * + * @param string $class Name of a class that extends FilterIterator + * + * @return Phergie_Plugin_Handler Provides a fluent API + * @throws Phergie_Plugin_Exception Class cannot be found or is not an + * FilterIterator-based class + */ + public function setIteratorClass($class) + { + $valid = true; + + try { + $error_reporting = error_reporting(0); // ignore autoloader errors + $r = new ReflectionClass($class); + error_reporting($error_reporting); + if (!$r->isSubclassOf('FilterIterator')) { + $message = 'Class ' . $class . ' is not a subclass of FilterIterator'; + $valid = false; + } + } catch (ReflectionException $e) { + $message = $e->getMessage(); + $valid = false; + } + + if (!$valid) { + throw new Phergie_Plugin_Exception( + $message, + Phergie_Plugin_Exception::ERR_INVALID_ITERATOR_CLASS + ); + } + + $this->iteratorClass = $class; + } + + /** + * Proxies method calls to all plugins containing the called method. + * + * @param string $name Name of the method called + * @param array $args Arguments passed in the method call + * + * @return void + */ + public function __call($name, array $args) + { + foreach ($this->getIterator() as $plugin) { + call_user_func_array(array($plugin, $name), $args); + } + return true; + } + + /** + * Returns the number of plugins contained within the handler. + * + * @return int Plugin count + */ + public function count() + { + return count($this->plugins); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Help.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Help.php new file mode 100644 index 000000000..4c2c49b31 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Help.php @@ -0,0 +1,276 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Help + * @author Phergie Development Team <team@phergie.org> + * @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_Help + */ + +/** + * Provides access to descriptions of plugins and the commands they provide. + * + * @category Phergie + * @package Phergie_Plugin_Help + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Help + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Help extends Phergie_Plugin_Abstract +{ + /** + * Registry of help data indexed by plugin name + * + * @var array + */ + protected $registry; + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Creates a registry of plugin metadata on connect. + * + * @return void + */ + public function onConnect() + { + $this->populateRegistry(); + } + + /** + * Creates a registry of plugin metadata. + * + * @return void + */ + public function populateRegistry() + { + $this->registry = array(); + + foreach ($this->plugins as $plugin) { + $class = new ReflectionClass($plugin); + $pluginName = strtolower($plugin->getName()); + + // Parse the plugin description + $docblock = $class->getDocComment(); + $annotations = $this->getAnnotations($docblock); + if (isset($annotations['pluginDesc'])) { + $pluginDesc = implode(' ', $annotations['pluginDesc']); + } else { + $pluginDesc = $this->parseShortDescription($docblock); + } + $this->registry[$pluginName] = array( + 'desc' => $pluginDesc, + 'cmds' => array() + ); + + // Parse command method descriptions + $methodPrefix = Phergie_Plugin_Command::METHOD_PREFIX; + $methodPrefixLength = strlen($methodPrefix); + foreach ($class->getMethods() as $method) { + if (strpos($method->getName(), $methodPrefix) !== 0) { + continue; + } + + $cmd = strtolower(substr($method->getName(), $methodPrefixLength)); + $docblock = $method->getDocComment(); + $annotations = $this->getAnnotations($docblock); + + if (isset($annotations['pluginCmd'])) { + $cmdDesc = implode(' ', $annotations['pluginCmd']); + } else { + $cmdDesc = $this->parseShortDescription($docblock); + } + + $cmdParams = array(); + if (!empty($annotations['param'])) { + foreach ($annotations['param'] as $param) { + $match = null; + if (preg_match('/\h+\$([^\h]+)\h+/', $param, $match)) { + $cmdParams[] = $match[1]; + } + } + } + + $this->registry[$pluginName]['cmds'][$cmd] = array( + 'desc' => $cmdDesc, + 'params' => $cmdParams + ); + } + + if (empty($this->registry[$pluginName]['cmds'])) { + unset($this->registry[$pluginName]); + } + } + } + + /** + * Displays a list of plugins with help information available or + * commands available for a specific plugin. + * + * @param string $query Optional short name of a plugin for which commands + * should be returned or a command; if unspecified, a list of + * plugins with help information available is returned + * + * @return void + */ + public function onCommandHelp($query = null) + { + if ($query == 'refresh') { + $this->populateRegistry(); + } + + $nick = $this->getEvent()->getNick(); + $delay = $this->getConfig('help.delay', 2); + + // Handle requests for a plugin list + if (!$query) { + $msg = 'These plugins have help information available: ' + . implode(', ', array_keys($this->registry)); + $this->doPrivmsg($nick, $msg); + return; + } + + // Handle requests for plugin information + $query = strtolower($query); + if (isset($this->registry[$query]) + && empty($this->registry[$query]['cmds'][$query])) { + $msg = $query . ' - ' . $this->registry[$query]['desc']; + $this->doPrivmsg($nick, $msg); + + $msg = 'Available commands - ' + . implode(', ', array_keys($this->registry[$query]['cmds'])); + $this->doPrivmsg($nick, $msg); + + if ($this->getConfig('command.prefix')) { + $msg + = 'Note that these commands must be prefixed with "' + . $this->getConfig('command.prefix') + . '" (without quotes) when issued in a public channel.'; + $this->doPrivmsg($nick, $msg); + } + + return; + } + + // Handle requests for command information + foreach ($this->registry as $plugin => $data) { + if (empty($data['cmds'])) { + continue; + } + + $result = preg_grep('/^' . $query . '$/i', array_keys($data['cmds'])); + if (!$result) { + continue; + } + + $cmd = $data['cmds'][array_shift($result)]; + $msg = $query; + if (!empty($cmd['params'])) { + $msg .= ' [' . implode('] [', $cmd['params']) . ']'; + } + $msg .= ' - ' . $cmd['desc']; + $this->doPrivmsg($nick, $msg); + } + } + + /** + * Parses and returns the short description from a docblock. + * + * @param string $docblock Docblock comment code + * + * @return string Short description (i.e. content from the start of the + * docblock up to the first double-newline) + */ + protected function parseShortDescription($docblock) + { + $desc = preg_replace( + array('#^\h*\*\h*#m', '#^/\*\*\h*\v+\h*#', '#(?:\r?\n){2,}.*#s', '#\s*\v+\s*#'), + array('', '', '', ' '), + $docblock + ); + return $desc; + } + + /** + * Taken from PHPUnit/Util/Test.php and modified to fix an issue with + * tag content spanning multiple lines. + * + * PHPUnit + * + * Copyright (c) 2002-2010, Sebastian Bergmann <sb@sebastian-bergmann.de>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @param string $docblock docblock to parse + * + * @return array + */ + protected function getAnnotations($docblock) + { + $annotations = array(); + + $regex = '/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?(?:\*\/|\* @)/ms'; + + if (preg_match_all($regex, $docblock, $matches)) { + $numMatches = count($matches[0]); + + for ($i = 0; $i < $numMatches; ++$i) { + $annotation = $matches['value'][$i]; + $annotation = preg_replace('/\s*\v+\s*\*\s*/', ' ', $annotation); + $annotation = rtrim($annotation); + $annotations[$matches['name'][$i]][] = $annotation; + } + } + + return $annotations; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Http.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Http.php new file mode 100644 index 000000000..7c5c8704c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Http.php @@ -0,0 +1,284 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Http + * @author Phergie Development Team <team@phergie.org> + * @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_Http + */ + +/** + * Provides an HTTP client for plugins to use in contacting web services or + * retrieving feeds or web pages. + * + * @category Phergie + * @package Phergie_Plugin_Http + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Http + * @uses extension simplexml optional + * @uses extension json optional + */ +class Phergie_Plugin_Http extends Phergie_Plugin_Abstract +{ + /** + * Response to the last executed HTTP request + * + * @var Phergie_Plugin_Http_Response + */ + protected $response; + + /** + * Mapping of content types to handlers for them + * + * @var array + */ + protected $handlers; + + /** + * Initializes the handler lookup table. + * + * @return void + */ + public function onLoad() + { + $this->handlers = array( + '(?:application|text)/xml(?:;.*)?' + => 'simplexml_load_string', + '(?:(?:application|text)/(?:x-)?json)|text/javascript.*' + => 'json_decode', + ); + + if (is_array($this->config['http.handlers'])) { + $this->handlers = array_merge( + $this->handlers, + $this->config['http.handlers'] + ); + } + } + + /** + * Sets a handler callback for a content type, which is called when a + * response of that content type is received to perform any needed + * transformations on the response body content before storing it in the + * response object. Note that the calling plugin is responsible for + * indicating any dependencies related to specified handler callbacks. + * + * @param string $type PCRE regular expression (without delimiters) that + * matches one or more MIME types + * @param callback $callback Callback to execute when a response of a content + * type matched by $type is encountered + * + * @return Phergie_Plugin_Http Provides a fluent interface + */ + public function setHandler($type, $callback) + { + if (!is_callable($callback)) { + throw new Phergie_Plugin_Exception( + 'Invalid callback specified', + Phergie_Plugin_Exception::ERR_FATAL_ERROR + ); + } + + $this->handlers[$type] = $callback; + + return $this; + } + + /** + * Supporting method that parses the status line of an HTTP response + * message. + * + * @param string $status Status line + * + * @return array Associative array containing the HTTP version, response + * code, and response description + */ + protected function parseStatusLine($status) + { + $parts = explode(' ', $status, 3); + $parsed = array( + 'version' => str_replace('HTTP/', '', $parts[0]), + 'code' => $parts[1], + 'message' => rtrim($parts[2]) + ); + return $parsed; + } + + /** + * Supporting method that acts as an error handler to intercept HTTP + * responses resulting in PHP-level errors. + * + * @param int $errno Level of the error raised + * @param string $errstr Error message + * @param string $errfile Name of the file in which the error was raised + * @param string $errline Line number on which the error was raised + * + * @return bool Always returns TRUE to allow normal execution to + * continue once this method terminates + */ + public function handleError($errno, $errstr, $errfile, $errline) + { + if ($httperr = strstr($errstr, 'HTTP/')) { + $parts = $this->parseStatusLine($httperr); + $this->response + ->setCode($parts['code']) + ->setMessage($parts['message']); + } + + return true; + } + + /** + * Supporting method that executes a request and handles the response. + * + * @param string $url URL to request + * @param array $context Associative array of stream context parameters + * + * @return Phergie_Plugin_Http_Response Object representing the response + * resulting from the request + */ + public function request($url, array $context) + { + $this->response = new Phergie_Plugin_Http_Response; + + $url = (string) $url; + $context = stream_context_create(array('http' => $context)); + + set_error_handler(array($this, 'handleError'), E_WARNING); + $stream = fopen($url, 'r', false, $context); + if ($stream) { + $meta = stream_get_meta_data($stream); + $status = $this->parseStatusLine($meta['wrapper_data'][0]); + $code = $status['code']; + $message = $status['message']; + $headers = array(); + foreach (array_slice($meta['wrapper_data'], 1) as $header) { + if (!strpos($header, ':')) { + continue; + } + list($name, $value) = explode(': ', $header, 2); + $headers[$name] = $value; + } + unset($meta['wrapper_data']); + + $this->response + ->setCode($code) + ->setMessage($message) + ->setHeaders($headers) + ->setMeta($meta); + + $body = stream_get_contents($stream); + $type = $this->response->getHeaders('content-type'); + foreach ($this->handlers as $expr => $handler) { + if (preg_match('#^' . $expr . '$#i', $type)) { + $handled = call_user_func($handler, $body); + if (!empty($handled)) { + $body = $handled; + } + } + } + + $this->response->setContent($body); + } + restore_error_handler(); + + return $this->response; + } + + /** + * Performs a GET request. + * + * @param string $url URL for the request + * @param array $query Optional associative array of parameters + * constituting the URL query string if $url has none + * @param array $context Optional associative array of additional stream + * context parameters + * + * @return Phergie_Plugin_Http_Response Received response data + */ + public function get($url, array $query = array(), array $context = array()) + { + if (!empty($query)) { + $url .= '?' . http_build_query($query); + } + + $context['method'] = 'GET'; + + return $this->request($url, $context); + } + + /** + * Performs a HEAD request. + * + * @param string $url URL for the request + * @param array $query Optional associative array of parameters + * constituting the URL query string if $url has none + * @param array $context Optional associative array of additional stream + * context parameters + * + * @return Phergie_Plugin_Http_Response Received response data + */ + public function head($url, array $query = array(), array $context = array()) + { + if (!empty($query)) { + $url .= '?' . http_build_query($query); + } + + $context['method'] = 'HEAD'; + + return $this->request($url, $context); + } + + /** + * Performs a POST request. + * + * @param string $url URL for the request + * @param array $query Optional associative array of parameters + * constituting the URL query string if $url has none + * @param array $post Optional associative array of parameters + * constituting the POST request body if it is using the + * traditional URL-encoded format + * @param array $context Optional associative array of additional stream + * context parameters + * + * @return Phergie_Plugin_Http_Response Received response data + */ + public function post($url, array $query = array(), + array $post = array(), array $context = array() + ) { + if (!empty($query)) { + $url .= '?' . http_build_query($query); + } + + $context['method'] = 'POST'; + + if (!empty($post) + && (!empty($context['header']) + xor stripos($context['header'], 'Content-Type')) + ) { + if (!empty($context['header'])) { + $context['header'] .= "\r\n"; + } else { + $context['header'] = ''; + } + $context['header'] .= + 'Content-Type: application/x-www-form-urlencoded'; + $context['content'] = http_build_query($post); + } + + return $this->request($url, $context); + } +}
\ No newline at end of file diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Http/Response.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Http/Response.php new file mode 100644 index 000000000..b9e377cf6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Http/Response.php @@ -0,0 +1,281 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Http + * @author Phergie Development Team <team@phergie.org> + * @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_Http + */ + +/** + * Data structure for HTTP response information. + * + * @category Phergie + * @package Phergie_Plugin_Http + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Http + */ +class Phergie_Plugin_Http_Response +{ + /** + * HTTP response code or 0 if no HTTP response was received + * + * @var string + */ + protected $code; + + /** + * HTTP response strings + * + * @var array + */ + protected static $codeStrings = array( + 0 => 'No Response', + 100 => 'Continue', + 200 => 'OK', + 201 => 'Created', + 204 => 'No Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 408 => 'Request Timeout', + 410 => 'Gone', + 413 => 'Request Entity Too Large', + 414 => 'Request URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Method Not Implemented', + 503 => 'Service Unavailable', + 506 => 'Variant Also Negotiates' + ); + + /** + * Description of the HTTP response code or the error message if no HTTP + * response was received + * + * @var string + */ + protected $message; + + /** + * Content of the response body, decoded for supported content types + * + * @var mixed + */ + protected $content; + + /** + * Associative array mapping response header names to their values + * + * @var array + */ + protected $headers; + + /** + * Associative array containing other metadata about the response + * + * @var array + */ + protected $meta; + + /** + * Sets the HTTP response code. + * + * @param string $code Response code + * + * @return Phergie_Plugin_Http_Response Provides a fluent interface + */ + public function setCode($code) + { + $this->code = $code; + return $this; + } + + /** + * Returns the HTTP response code. + * + * @return string Response code + */ + public function getCode() + { + return $this->code; + } + + /** + * Returns the HTTP response code text. + * + * @return string Response code text + */ + public function getCodeAsString() + { + $code = $this->code; + + if (!isset(self::$codeStrings[$code])) { + return 'Unkown HTTP Status'; + } + + return self::$codeStrings[$code]; + } + + /** + * Returns whether the response indicates a client- or server-side error. + * + * @return bool TRUE if the response indicates an error, FALSE otherwise + */ + public function isError() + { + switch (substr($this->code, 0, 1)) { + case '0': + case '4': + case '5': + return true; + default: + return false; + } + } + + /** + * Sets the HTTP response description. + * + * @param string $message Response description + * + * @return Phergie_Plugin_Http_Response Provides a fluent interface + */ + public function setMessage($message) + { + $this->message = $message; + return $this; + } + + /** + * Returns the HTTP response description. + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Sets the content of the response body. + * + * @param mixed $content Response body content + * + * @return Phergie_Plugin_Http_Response Provides a fluent interface + */ + public function setContent($content) + { + $this->content = $content; + return $this; + } + + /** + * Returns the content of the response body. + * + * @return mixed Response body content, decoded for supported content + * types + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets the response headers. + * + * @param array $headers Associative array of response headers indexed + * by header name + * + * @return Phergie_Plugin_Http_Response Provides a fluent interface + */ + public function setHeaders(array $headers) + { + $names = array_map('strtolower', array_keys($headers)); + $values = array_values($headers); + $this->headers = array_combine($names, $values); + return $this; + } + + /** + * Returns all response headers or the value of a single specified + * response header. + * + * @param string $name Optional name of a single header for which the + * associated value should be returned + * + * @return array|string Associative array of all header values, a string + * containing the value of the header indicated by $name if one + * is set, or null if one is not + */ + public function getHeaders($name = null) + { + if ($name) { + $name = strtolower($name); + if (empty($this->headers[$name])) { + return null; + } + return $this->headers[$name]; + } + return $this->headers; + } + + /** + * Sets the response metadata. + * + * @param array $meta Associative array of response metadata + * + * @return Phergie_Plugin_Http_Response Provides a fluent interface + */ + public function setMeta(array $meta) + { + $this->meta = $meta; + return $this; + } + + /** + * Returns all metadata or the value of a single specified metadatum. + * + * @param string $name Optional name of a single metadatum for which the + * associated value should be returned + * + * @return array|string|null Associative array of all metadata values, a + * string containing the value of the metadatum indicated by + * $name if one is set, or null if one is not + */ + public function getMeta($name = null) + { + if ($name) { + if (empty($this->meta[$name])) { + return null; + } + return $this->meta[$name]; + } + return $this->meta; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Ideone.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ideone.php new file mode 100644 index 000000000..3af88dda9 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ideone.php @@ -0,0 +1,180 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Ideone + * @author Phergie Development Team <team@phergie.org> + * @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_Ideone + */ + +/** + * Interfaces with ideone.com to execute code and return the result. + * + * @category Phergie + * @package Phergie_Plugin_Ideone + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Ideone + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Ideone extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->plugins->getPlugin('Command'); + } + + /** + * Checks a service response for an error, sends a notice to the event + * source if an error has occurred, and returns whether an error was found. + * + * @param array $result Associative array representing the service response + * + * @return boolean TRUE if an error is found, FALSE otherwise + */ + protected function isError($result) + { + if ($result['error'] != 'OK') { + $this->doNotice($this->event->getNick(), 'ideone error: ' . $result['error']); + return true; + } + return false; + } + + /** + * Executes a source code sequence in a specified language and returns + * the result. + * + * @param string $language Programming language the source code is in + * @param string $code Source code to execute + * + * @return void + */ + public function onCommandIdeone($language, $code) + { + $source = $this->event->getSource(); + $nick = $this->event->getNick(); + + // Get authentication credentials + $user = $this->getConfig('ideone.user', 'test'); + $pass = $this->getConfig('ideone.pass', 'test'); + + // Normalize the command parameters + $language = strtolower($language); + + // Massage PHP code to allow for convenient shorthand + if ($language == 'php') { + if (!preg_match('/^<\?(?:php)?/', $code)) { + $code = '<?php ' . $code; + } + switch (substr($code, -1)) { + case '}': + case ';': + break; + default: + $code .= ';'; + break; + } + } + + // Identify the language to use + $client = new SoapClient('http://ideone.com/api/1/service.wsdl'); + $response = $client->getLanguages($user, $pass); + if ($this->isError($response)) { + return; + } + $languageLength = strlen($language); + foreach ($response['languages'] as $languageId => $languageName) { + if (strncasecmp($language, $languageName, $languageLength) == 0) { + break; + } + } + + // Send the paste data + $response = $client->createSubmission( + $user, + $pass, + $code, + $languageId, + null, // string input - data from stdin + true, // boolean run - TRUE to execute the code + false // boolean private - FALSE to make the paste public + ); + if ($this->isError($response)) { + return; + } + $link = $response['link']; + + // Wait until the paste data is processed or the service fails + $attempts = $this->getConfig('ideone.attempts', 10); + foreach (range(1, $attempts) as $attempt) { + $response = $client->getSubmissionStatus($user, $pass, $link); + if ($this->isError($response)) { + return; + } + if ($response['status'] == 0) { + $result = $response['result']; + break; + } else { + $result = null; + sleep(1); + } + } + if ($result == null) { + $this->doNotice($nick, 'ideone error: Timed out'); + return; + } + if ($result != 15) { + $this->doNotice($nick, 'ideone error: Status code ' . $result); + return; + } + + // Get details for the created paste + $response = $client->getSubmissionDetails( + $user, + $pass, + $link, + false, // boolean withSource - FALSE to not return the source code + false, // boolean withInput - FALSE to not return stdin data + true, // boolean withOutput - TRUE to include output + true, // boolean withStderr - TRUE to return stderr data + false // boolean withCmpinfo - TRUE to return compilation info + ); + if ($this->isError($response)) { + return; + } + + // Replace the output if it exceeds a specified maximum length + $outputLimit = $this->getConfig('ideone.output_limit', 100); + var_dump($response); + if ($outputLimit && strlen($response['output']) > $outputLimit) { + $response['output'] = 'Output is too long to post'; + } + + // Format the message + $msg = $this->getConfig('ideone.format', '%nick%: [ %link% ] %output%'); + $response['nick'] = $nick; + $response['link'] = 'http://ideone.com/' . $link; + foreach ($response as $key => $value) { + $msg = str_replace('%' . $key . '%', $value, $msg); + } + $this->doPrivmsg($source, $msg); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Invisible.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Invisible.php new file mode 100755 index 000000000..622f7d3db --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Invisible.php @@ -0,0 +1,44 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Invisible + * @author Phergie Development Team <team@phergie.org> + * @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_Invisible + */ + +/** + * Marks the bot as invisible when it connects to the server. + * + * @category Phergie + * @package Phergie_Plugin_Invisible + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Invisible + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_2_3_2 + */ +class Phergie_Plugin_Invisible extends Phergie_Plugin_Abstract +{ + /** + * Marks the bot as invisible when it connects to the server. + * + * @return void + */ + public function onConnect() + { + $this->doMode($this->getConnection()->getNick(), '+i'); + $this->getPluginHandler()->removePlugin($this); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Iterator.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Iterator.php new file mode 100644 index 000000000..d61096298 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Iterator.php @@ -0,0 +1,142 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Implements a filtering iterator for limiting executing of methods across + * a group of plugins. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Plugin_Iterator extends FilterIterator +{ + /** + * List of short names of plugins to exclude when iterating + * + * @var array + */ + protected $plugins = array(); + + /** + * List of method names where plugins with these methods will be + * excluded when iterating + * + * @var array + */ + protected $methods = array(); + + /** + * Overrides the parent constructor to reset the internal iterator's + * pointer to the current item, which the parent class errantly does not + * do. + * + * @param Iterator $iterator Iterator to filter + * + * @return void + * @link http://bugs.php.net/bug.php?id=52560 + */ + public function __construct(Iterator $iterator) + { + parent::__construct($iterator); + $this->rewind(); + } + + /** + * Adds to a list of plugins to exclude when iterating. + * + * @param mixed $plugins String containing the short name of a single + * plugin to exclude or an array of short names of multiple + * plugins to exclude + * + * @return Phergie_Plugin_Iterator Provides a fluent interface + */ + public function addPluginFilter($plugins) + { + if (is_array($plugins)) { + $this->plugins = array_unique( + array_merge($this->plugins, $plugins) + ); + } else { + $this->plugins[] = $plugins; + } + return $this; + } + + /** + * Adds to a list of method names where plugins defining these methods + * will be excluded when iterating. + * + * @param mixed $methods String containing the name of a single method + * or an array containing the name of multiple methods + * + * @return Phergie_Plugin_Iterator Provides a fluent interface + */ + public function addMethodFilter($methods) + { + if (is_array($methods)) { + $this->methods = array_merge($this->methods, $methods); + } else { + $this->methods[]= $methods; + } + return $this; + } + + /** + * Clears any existing plugin and methods filters. + * + * @return Phergie_Plugin_Iterator Provides a fluent interface + */ + public function clearFilters() + { + $this->plugins = array(); + $this->methods = array(); + } + + /** + * Implements FilterIterator::accept(). + * + * @return boolean TRUE to include the current item in those by returned + * during iteration, FALSE otherwise + */ + public function accept() + { + if (!$this->plugins && !$this->methods) { + return true; + } + + $current = $this->current(); + + if (in_array($current->getName(), $this->plugins)) { + return false; + } + + foreach ($this->methods as $method) { + if (method_exists($current, $method)) { + return false; + } + } + + return true; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Join.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Join.php new file mode 100755 index 000000000..50c3ad8e6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Join.php @@ -0,0 +1,56 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Join + * @author Phergie Development Team <team@phergie.org> + * @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_Join + */ + +/** + * Joins a specified channel on command from a user. + * + * @category Phergie + * @package Phergie_Plugin_Join + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Join + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Join extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Joins a channel. + * + * @param string $channels Comma-delimited list of channels to join + * @param string $keys Optional comma-delimited list of channel keys + * + * @return void + */ + public function onCommandJoin($channels, $keys = null) + { + $this->doJoin($channels, $keys); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php new file mode 100644 index 000000000..27b4a087d --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php @@ -0,0 +1,451 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Karma + * @author Phergie Development Team <team@phergie.org> + * @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_Karma + */ + +/** + * Handles requests for incrementation or decrementation of a maintained list + * of counters for specified terms. + * + * @category Phergie + * @package Phergie_Plugin_Karma + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Karma + * @uses extension PDO + * @uses extension pdo_sqlite + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract +{ + /** + * SQLite object + * + * @var resource + */ + protected $db = null; + + /** + * Prepared statement to add a new karma record + * + * @var PDOStatement + */ + protected $insertKarma; + + /** + * Prepared statement to update an existing karma record + * + * @var PDOStatement + */ + protected $updateKarma; + + /** + * Retrieves an existing karma record + * + * @var PDOStatement + */ + protected $fetchKarma; + + /** + * Retrieves an existing fixed karma record + * + * @var PDOStatement + */ + protected $fetchFixedKarma; + + /** + * Retrieves a positive answer for a karma comparison + * + * @var PDOStatement + */ + protected $fetchPositiveAnswer; + + /** + * Retrieves a negative answer for a karma comparison + * + * @var PDOStatement + */ + protected $fetchNegativeAnswer; + + /** + * Check for dependencies and initializes a database connection and + * prepared statements. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $this->getDb(); + } + + /** + * Initializes prepared statements used by the plugin. + * + * @return void + */ + protected function initializePreparedStatements() + { + $this->fetchKarma = $this->db->prepare(' + SELECT karma + FROM karmas + WHERE term = :term + LIMIT 1 + '); + + $this->insertKarma = $this->db->prepare(' + INSERT INTO karmas (term, karma) + VALUES (:term, :karma) + '); + + $this->updateKarma = $this->db->prepare(' + UPDATE karmas + SET karma = :karma + WHERE term = :term + '); + + $this->fetchFixedKarma = $this->db->prepare(' + SELECT karma + FROM fixed_karmas + WHERE term = :term + LIMIT 1 + '); + + $this->fetchPositiveAnswer = $this->db->prepare(' + SELECT answer + FROM positive_answers + ORDER BY RANDOM() + LIMIT 1 + '); + + $this->fetchNegativeAnswer = $this->db->prepare(' + SELECT answer + FROM negative_answers + ORDER BY RANDOM() + LIMIT 1 + '); + } + + /** + * Returns a connection to the plugin database, initializing one if none + * is explicitly set. + * + * @return PDO Database connection + */ + public function getDb() + { + if (empty($this->db)) { + $this->db = new PDO('sqlite:' . dirname(__FILE__) . '/Karma/karma.db'); + $this->initializePreparedStatements(); + } + return $this->db; + } + + /** + * Sets the connection to the plugin database, mainly intended for unit + * testing. + * + * @param PDO $db Database connection + * + * @return Phergie_Plugin_Karma Provides a fluent interface + */ + public function setDb(PDO $db) + { + $this->db = $db; + $this->initializePreparedStatements(); + return $this; + } + + /** + * Get the canonical form of a given term. + * + * In the canonical form all sequences of whitespace + * are replaced by a single space and all characters + * are lowercased. + * + * @param string $term Term for which a canonical form is required + * + * @return string Canonical term + */ + protected function getCanonicalTerm($term) + { + $canonicalTerm = strtolower(preg_replace('|\s+|', ' ', trim($term, '()'))); + switch ($canonicalTerm) { + case 'me': + $canonicalTerm = strtolower($this->event->getNick()); + break; + case 'all': + case '*': + case 'everything': + $canonicalTerm = 'everything'; + break; + } + return $canonicalTerm; + } + + /** + * Intercepts a message and processes any contained recognized commands. + * + * @return void + */ + public function onPrivmsg() + { + $message = $this->getEvent()->getText(); + + $termPattern = '\S+?|\([^<>]+?\)+'; + $actionPattern = '(?P<action>\+\+|--)'; + + $modifyPattern = <<<REGEX + {^ + (?J) # allow overwriting capture names + \s* # ignore leading whitespace + + (?: # start with ++ or -- before the term + $actionPattern + (?P<term>$termPattern) + | # follow the term with ++ or -- + (?P<term>$termPattern) + $actionPattern # allow no whitespace between the term and the action + ) + $}ix +REGEX; + + $versusPattern = <<<REGEX + {^ + (?P<term0>$termPattern) + \s+(?P<method><|>)\s+ + (?P<term1>$termPattern)$# + $}ix +REGEX; + + $match = null; + + if (preg_match($modifyPattern, $message, $match)) { + $action = $match['action']; + $term = $this->getCanonicalTerm($match['term']); + $this->modifyKarma($term, $action); + } elseif (preg_match($versusPattern, $message, $match)) { + $term0 = trim($match['term0']); + $term1 = trim($match['term1']); + $method = $match['method']; + $this->compareKarma($term0, $term1, $method); + } + } + + /** + * Get the karma rating for a given term. + * + * @param string $term Term for which the karma rating needs to be + * retrieved + * + * @return void + */ + public function onCommandKarma($term) + { + $source = $this->getEvent()->getSource(); + $nick = $this->getEvent()->getNick(); + + $canonicalTerm = $this->getCanonicalTerm($term); + + $fixedKarma = $this->fetchFixedKarma($canonicalTerm); + if ($fixedKarma) { + $message = $nick . ': ' . $term . ' ' . $fixedKarma; + $this->doPrivmsg($source, $message); + return; + } + + $karma = $this->fetchKarma($canonicalTerm); + + $message = $nick . ': '; + + if ($term == 'me') { + $message .= 'You have'; + } else { + $message .= $term . ' has'; + } + + $message .= ' '; + + if ($karma) { + $message .= 'karma of ' . $karma; + } else { + $message .= 'neutral karma'; + } + + $message .= '.'; + + $this->doPrivmsg($source, $message); + } + + /** + * Resets the karma for a term to 0. + * + * @param string $term Term for which to reset the karma rating + * + * @return void + */ + public function onCommandReincarnate($term) + { + $data = array( + ':term' => $term, + ':karma' => 0 + ); + $this->updateKarma->execute($data); + } + + /** + * Compares the karma between two terms. Optionally increases/decreases + * the karma of either term. + * + * @param string $term0 First term + * @param string $term1 Second term + * @param string $method Comparison method (< or >) + * + * @return void + */ + protected function compareKarma($term0, $term1, $method) + { + $event = $this->getEvent(); + $nick = $event->getNick(); + $source = $event->getSource(); + + $canonicalTerm0 = $this->getCanonicalTerm($term0); + $canonicalTerm1 = $this->getCanonicalTerm($term1); + + $fixedKarma0 = $this->fetchFixedKarma($canonicalTerm0); + $fixedKarma1 = $this->fetchFixedKarma($canonicalTerm1); + + if ($fixedKarma0 || $fixedKarma1) { + return; + } + + if ($canonicalTerm0 == 'everything') { + $change = $method == '<' ? '++' : '--'; + $karma0 = 0; + $karma1 = $this->modifyKarma($canonicalTerm1, $change); + } elseif ($canonicalTerm1 == 'everything') { + $change = $method == '<' ? '--' : '++'; + $karma0 = $this->modifyKarma($canonicalTerm0, $change); + $karma1 = 0; + } else { + $karma0 = $this->fetchKarma($canonicalTerm0); + $karma1 = $this->fetchKarma($canonicalTerm1); + } + + // Combining the first and second branches here causes an odd + // single-line lapse in code coverage, but the lapse disappears if + // they're separated + if ($method == '<' && $karma0 < $karma1) { + $replies = $this->fetchPositiveAnswer; + } elseif ($method == '>' && $karma0 > $karma1) { + $replies = $this->fetchPositiveAnswer; + } else { + $replies = $this->fetchNegativeAnswer; + } + $replies->execute(); + $reply = $replies->fetchColumn(); + + if (max($karma0, $karma1) == $karma1) { + list($canonicalTerm0, $canonicalTerm1) = + array($canonicalTerm1, $canonicalTerm0); + } + + $message = str_replace( + array('%owner%','%owned%'), + array($canonicalTerm0, $canonicalTerm1), + $reply + ); + + $this->doPrivmsg($source, $message); + } + + /** + * Modifes a term's karma. + * + * @param string $term Term to modify + * @param string $action Karma action (either ++ or --) + * + * @return int Modified karma rating + */ + protected function modifyKarma($term, $action) + { + $karma = $this->fetchKarma($term); + if ($karma !== false) { + $statement = $this->updateKarma; + } else { + $statement = $this->insertKarma; + } + + $karma += ($action == '++') ? 1 : -1; + + $args = array( + ':term' => $term, + ':karma' => $karma + ); + $statement->execute($args); + + return $karma; + } + + /** + * Returns the karma rating for a specified term for which the karma + * rating can be modified. + * + * @param string $term Term for which to fetch the corresponding karma + * rating + * + * @return integer|boolean Integer value denoting the term's karma or + * FALSE if there is the specified term has no associated karma + * rating + */ + protected function fetchKarma($term) + { + $this->fetchKarma->execute(array(':term' => $term)); + $result = $this->fetchKarma->fetch(PDO::FETCH_ASSOC); + + if ($result === false) { + return false; + } + + return (int) $result['karma']; + } + + /** + * Returns a phrase describing the karma rating for a specified term for + * which the karma rating is fixed. + * + * @param string $term Term for which to fetch the corresponding karma + * rating + * + * @return string Phrase describing the karma rating, which may be append + * to the term to form a complete response + */ + protected function fetchFixedKarma($term) + { + $this->fetchFixedKarma->execute(array(':term' => $term)); + $result = $this->fetchFixedKarma->fetch(PDO::FETCH_ASSOC); + + if ($result === false) { + return false; + } + + return $result['karma']; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Lart.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Lart.php new file mode 100644 index 000000000..d00cae0dd --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Lart.php @@ -0,0 +1,303 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Lart + * @author Phergie Development Team <team@phergie.org> + * @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_Lart + */ + +/** + * Accepts terms and corresponding definitions for storage to a local data + * source and performs and returns the result of lookups for term definitions + * as they are requested. + * + * @category Phergie + * @package Phergie_Plugin_Lart + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Lart + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses extension PDO + * @uses extension pdo_sqlite + */ +class Phergie_Plugin_Lart extends Phergie_Plugin_Abstract +{ + /** + * PDO instance for the database + * + * @var PDO + */ + protected $db; + + /** + * Prepared statement for inserting a new definition + * + * @var PDOStatement + */ + protected $save; + + /** + * Prepared statement for deleting the definition for a given term + * + * @var PDOStatement + */ + protected $delete; + + /** + * Prepared statement for searching for a definition for which the term + * matches as a regular expression against a given search string + * + * @var PDOStatement + */ + protected $process; + + /** + * Prepared statement for searching for a definition by its exact term + * + * @var PDOStatement + */ + protected $select; + + /** + * Checks for dependencies and initializes the database. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + $this->fail('PDO and pdo_sqlite extensions must be installed'); + } + + $this->plugins->getPlugin('Command'); + + $dir = dirname(__FILE__) . '/' . $this->getName(); + $path = $dir . '/lart.db'; + $exists = file_exists($path); + if (!$exists) { + mkdir($dir); + } + + try { + $this->db = new PDO('sqlite:' . $path); + } catch (PDO_Exception $e) { + throw new Phergie_Plugin_Exception($e->getMessage()); + } + + $this->db->sqliteCreateFunction('preg_match', 'preg_match'); + + if (!$exists) { + $this->db->exec(' + CREATE TABLE lart ( + name VARCHAR(255), + definition TEXT, + hostmask VARCHAR(50), + tstamp VARCHAR(19) + ) + '); + $this->db->exec(' + CREATE UNIQUE INDEX lart_name ON lart (name) + '); + } + + $this->save = $this->db->prepare(' + REPLACE INTO lart (name, definition, hostmask, tstamp) + VALUES (:name, :definition, :hostmask, :tstamp) + '); + + $this->process = $this->db->prepare(' + SELECT * + FROM lart + WHERE preg_match(name, :name) + '); + + $this->select = $this->db->prepare(' + SELECT * + FROM lart + WHERE name = :name + '); + + $this->delete = $this->db->prepare(' + DELETE FROM lart + WHERE name = :name + '); + } + + /** + * Retrieves the definition for a given term if it exists. + * + * @param string $term Term to search for + * + * @return mixed String containing the definition or FALSE if no definition + * exists + */ + protected function getLart($term) + { + $this->process->execute(array(':name' => $term)); + $row = $this->process->fetchObject(); + if ($row === false) { + return false; + } + preg_match($row->name, $term, $match); + $definition = preg_replace( + "/(?:\\\\|\\$)([0-9]+)/e", + '$match[\1]', + $row->definition + ); + $event = $this->getEvent(); + $definition = str_replace( + array('$source', '$nick'), + array($event->getSource(), $event->getNick()), + $definition + ); + return $definition; + } + + /** + * Deletes a given definition. + * + * @param string $term Term for which the definition should be deleted + * + * @return boolean TRUE if the definition was found and deleted, FALSE + * otherwise + */ + protected function deleteLart($term) + { + $this->delete->execute(array(':name' => $term)); + return ($this->delete->rowCount() > 0); + } + + /** + * Saves a given definition. + * + * @param string $term Term to trigger a response containing the + * corresponding definition, may be a regular expression + * @param string $definition Definition corresponding to the term + * + * @return boolean TRUE if the definition was saved successfully, FALSE + * otherwise + */ + protected function saveLart($term, $definition) + { + $data = array( + ':name' => $term, + ':definition' => $definition, + ':hostmask' => (string) $this->getEvent()->getHostmask(), + ':tstamp' => time() + ); + $this->save->execute($data); + return ($this->save->rowCount() > 0); + } + + /** + * Returns information about a definition. + * + * @param string $term Term about which to return information + * + * @return void + */ + public function onCommandLartinfo($term) + { + $this->select->execute(array(':name' => $term)); + $row = $this->select->fetchObject(); + $msg = $this->getEvent()->getNick() . ': '; + if (!$row) { + $msg .= 'Lart not found'; + } else { + $msg .= 'Term: ' . $row->name + . ', Definition: ' . $row->definition + . ', User: ' . $row->hostmask + . ', Added: ' . date('n/j/y g:i A', $row->tstamp); + } + $this->doNotice($this->getEvent()->getSource(), $msg); + } + + /** + * Creates a new definition. + * + * @param string $term Term to add + * @param string $definition Definition to add + * + * @return void + */ + public function onCommandAddlart($term, $definition) + { + $result = $this->saveLart($term, $definition); + if ($result) { + $msg = 'Lart saved successfully'; + } else { + $msg = 'Lart could not be saved'; + } + $this->doNotice($this->getEvent()->getSource(), $msg); + } + + /** + * Removes an existing definition. + * + * @param string $term Term for which the definition should be removed + * + * @return void + */ + public function onCommandDeletelart($term) + { + $source = $this->getEvent()->getSource(); + if ($this->deleteLart($term)) { + $msg = 'Lart deleted successfully'; + } else { + $msg = 'Lart not found'; + } + $this->doNotice($source, $msg); + } + + /** + * Processes definition triggers in the text of the current event. + * + * @return void + */ + protected function processLart() + { + $lart = $this->getLart($this->getEvent()->getText()); + if ($lart) { + if (strpos($lart, '/me') === 0) { + $lart = substr($lart, 4); + $method = 'doAction'; + } else { + $method = 'doPrivmsg'; + } + $this->$method($this->getEvent()->getSource(), $lart); + } + } + + /** + * Processes definition triggers in messages. + * + * @return void + */ + public function onPrivmsg() + { + $this->processLart(); + } + + /** + * Processes definition triggers in CTCP actions. + * + * @return void + */ + public function onAction() + { + $this->processLart(); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Message.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Message.php new file mode 100644 index 000000000..af8fc7287 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Message.php @@ -0,0 +1,112 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Message + * @author Phergie Development Team <team@phergie.org> + * @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_Message + */ + +/** + * Generalized plugin providing utility methods for + * prefix and bot named based message extraction. + * + * @category Phergie + * @package Phergie_Plugin_Message + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Message + */ +class Phergie_Plugin_Message extends Phergie_Plugin_Abstract +{ + + /** + * Check whether a message is specifically targeted at the bot. + * This is the case when the message starts with the bot's name + * followed by [,:>] or when it is a private message. + * + * @return boolean true when the message is specifically targeted at the bot, + * false otherwise. + */ + public function isTargetedMessage() + { + $event = $this->getEvent(); + + $self = preg_quote($this->connection->getNick()); + + $targetPattern = <<<REGEX + {^ + \s*{$self}\s*[:>,].* # expect the bots name, followed by a [:>,] + $}ix +REGEX; + + return !$event->isInChannel() + || preg_match($targetPattern, $event->getText()) > 0; + } + + /** + * Allow for prefix and bot name aware extraction of a message + * + * @return string|bool $message The message, which is possibly targeted at the + * bot or false if a prefix requirement failed + */ + public function getMessage() + { + $event = $this->getEvent(); + + $prefix = preg_quote($this->getConfig('command.prefix')); + $self = preg_quote($this->connection->getNick()); + $message = $event->getText(); + + // $prefixPattern matches : Phergie, do command <parameters> + // where $prefix = 'do' : do command <parameters> + // : Phergie, command <parameters> + $prefixPattern = <<<REGEX + {^ + (?: + \s*{$self}\s*[:>,]\s* # start with bot name + (?:{$prefix})? # which is optionally followed by the prefix + | + \s*{$prefix} # or start with the prefix + ) + \s*(.*) # always end with the message + $}ix +REGEX; + + // $noPrefixPattern matches : Phergie, command <parameters> + // : command <parameters> + $noPrefixPattern = <<<REGEX + {^ + \s*(?:{$self}\s*[:>,]\s*)? # optionally start with the bot name + (.*?) # always end with the message + $}ix +REGEX; + + $pattern = $noPrefixPattern; + + // If a prefix is set, force it as a requirement + if ($prefix && $event->isInChannel()) { + $pattern = $prefixPattern; + } + + $match = null; + + if (!preg_match($pattern, $message, $match)) { + return false; + } + + return $match[1]; + } +}
\ No newline at end of file diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php new file mode 100644 index 000000000..e97abecf7 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/NickServ.php @@ -0,0 +1,178 @@ +<?php
+/**
+ * Phergie
+ *
+ * PHP version 5
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.
+ * It is also available through the world-wide-web at this URL:
+ * http://phergie.org/license
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_NickServ
+ * @author Phergie Development Team <team@phergie.org>
+ * @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_NickServ
+ */
+
+/**
+ * Intercepts and responds to messages from the NickServ agent requesting that
+ * the bot authenticate its identify.
+ *
+ * The password configuration setting should contain the password registered
+ * with NickServ for the nick used by the bot.
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_NickServ
+ * @author Phergie Development Team <team@phergie.org>
+ * @license http://phergie.org/license New BSD License
+ * @link http://pear.phergie.org/package/Phergie_Plugin_NickServ
+ * @uses Phergie_Plugin_Command pear.phergie.org
+ */
+class Phergie_Plugin_NickServ extends Phergie_Plugin_Abstract
+{
+ /**
+ * Nick of the NickServ bot
+ *
+ * @var string
+ */
+ protected $botNick;
+
+ /**
+ * Identify message
+ */
+ protected $identifyMessage;
+
+ /**
+ * Checks for dependencies and required configuration settings.
+ *
+ * @return void
+ */
+ public function onLoad()
+ {
+ $this->getPluginHandler()->getPlugin('Command');
+
+ // Get the name of the NickServ bot, defaults to NickServ
+ $this->botNick = $this->getConfig('nickserv.botnick', 'NickServ');
+
+ // Get the identify message
+ $this->identifyMessage = $this->getConfig(
+ 'nickserv.identify_message',
+ '/This nickname is registered./'
+ );
+ }
+
+ /**
+ * Checks for a notice from NickServ and responds accordingly if it is an
+ * authentication request or a notice that a ghost connection has been
+ * killed.
+ *
+ * @return void
+ */
+ public function onNotice()
+ {
+ $event = $this->event;
+ if (strtolower($event->getNick()) == strtolower($this->botNick)) {
+ $message = $event->getArgument(1);
+ $nick = $this->connection->getNick();
+ if (preg_match($this->identifyMessage, $message)) {
+ $password = $this->config['nickserv.password'];
+ if (!empty($password)) {
+ $this->doPrivmsg($this->botNick, 'IDENTIFY ' . $password);
+ }
+ unset($password);
+ } elseif (preg_match('/^.*' . $nick . '.* has been killed/', $message)) {
+ $this->doNick($nick);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if the original nick has quit; if so, take the name back.
+ *
+ * @return void
+ */
+ public function onQuit()
+ {
+ $eventNick = $this->event->getNick();
+ $nick = $this->connection->getNick();
+ if ($eventNick == $nick) {
+ $this->doNick($nick);
+ }
+ }
+
+ /**
+ * Changes the in-memory configuration setting for the bot nick if it is
+ * successfully changed.
+ *
+ * @return void
+ */
+ public function onNick()
+ {
+ $event = $this->event;
+ $connection = $this->connection;
+ if ($event->getNick() == $connection->getNick()) {
+ $connection->setNick($event->getArgument(0));
+ }
+ }
+
+ /**
+ * Provides a command to terminate ghost connections.
+ *
+ * @return void
+ */
+ public function onCommandGhostbust()
+ {
+ $event = $this->event;
+ $user = $event->getNick();
+ $conn = $this->connection;
+ $nick = $conn->getNick();
+
+ if ($nick != $this->config['connections'][$conn->getHost()]['nick']) {
+ $password = $this->config['nickserv.password'];
+ if (!empty($password)) {
+ $this->doPrivmsg(
+ $this->event->getSource(),
+ $user . ': Attempting to ghost ' . $nick .'.'
+ );
+ $this->doPrivmsg(
+ $this->botNick,
+ 'GHOST ' . $nick . ' ' . $password,
+ true
+ );
+ }
+ }
+ }
+
+ /**
+ * Automatically send the GHOST command if the bot's nick is in use.
+ *
+ * @return void
+ */
+ public function onResponse()
+ {
+ if ($this->event->getCode() == Phergie_Event_Response::ERR_NICKNAMEINUSE) {
+ $password = $this->config['nickserv.password'];
+ if (!empty($password)) {
+ $this->doPrivmsg(
+ $this->botNick,
+ 'GHOST ' . $this->connection->getNick() . ' ' . $password
+ );
+ }
+ }
+ }
+
+ /**
+ * Handle the server sending a KILL request.
+ *
+ * @return void
+ */
+ public function onKill()
+ {
+ $this->doQuit($this->event->getArgument(1));
+ }
+}
diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Part.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Part.php new file mode 100755 index 000000000..c07cdd918 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Part.php @@ -0,0 +1,55 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Part + * @author Phergie Development Team <team@phergie.org> + * @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_Part + */ + +/** + * Parts a specified channel on command from a user. + * + * @category Phergie + * @package Phergie_Plugin_Part + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Part + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Part extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Parts a channel. + * + * @param string $channels Comma-delimited list of channels to leave + * + * @return void + */ + public function onCommandPart($channels) + { + $this->doPart($channels); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Php.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php.php new file mode 100644 index 000000000..e10d101e7 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php.php @@ -0,0 +1,78 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @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_Php + */ + +/** + * Returns information on PHP functions as requested. + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Php + * @uses extension pdo + * @uses extension pdo_sqlite + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Php extends Phergie_Plugin_Abstract +{ + /** + * Data source to use + * + * @var Phergie_Plugin_Php_Source + */ + protected $source; + + /** + * Check for dependencies. + * + * @return void + */ + public function onLoad() + { + // @todo find a way to move this to Phergie_Plugin_Php_Source_Local + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + $this->fail('PDO and pdo_sqlite extensions must be installed'); + } + + $this->getPluginHandler()->getPlugin('Command'); + + $this->source = new Phergie_Plugin_Php_Source_Local; + } + + /** + * Searches the data source for the requested function. + * + * @param string $functionName Name of the function to search for + * + * @return void + */ + public function onCommandPhp($functionName) + { + $nick = $this->event->getNick(); + if ($function = $this->source->findFunction($functionName)) { + $msg = $nick . ': ' . $function['description']; + $this->doPrivmsg($this->event->getSource(), $msg); + } else { + $msg = 'Search for function ' . $functionName . ' returned no results.'; + $this->doNotice($nick, $msg); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source.php new file mode 100644 index 000000000..c6cf3261b --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source.php @@ -0,0 +1,46 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @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_Php + */ + +/** + * Data source interface for the Php plugin. + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Php + * @uses extension pdo + * @uses extension pdo_sqlite + * @uses Phergie_Plugin_Command pear.phergie.org + */ +interface Phergie_Plugin_Php_Source +{ + /** + * Searches for a description of the function. + * + * @param string $function Search pattern to match against the function + * name, wildcards supported using % + * + * @return array|null Associative array containing the function name and + * description or NULL if no results are found + */ + public function findFunction($function); +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source/Local.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source/Local.php new file mode 100644 index 000000000..9292bea84 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source/Local.php @@ -0,0 +1,208 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @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_Php + */ + +/** + * Data source for {@see Phergie_Plugin_Php}. This source reads function + * descriptions from a file and stores them in a SQLite database. When a + * function description is requested, the function is retrieved from the + * local database. + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Php + * @uses extension pdo + * @uses extension pdo_sqlite + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Php_Source_Local implements Phergie_Plugin_Php_Source +{ + /** + * Local database for storage + * + * @var PDO + */ + protected $database; + + /** + * Source of the PHP function summary + * + * @var string + */ + protected $url = 'http://cvs.php.net/viewvc.cgi/phpdoc/funcsummary.txt?revision=HEAD'; + + /** + * Constructor to initialize the data source. + * + * @return void + */ + public function __construct() + { + $path = dirname(__FILE__); + + try { + $this->database = new PDO('sqlite:' . $path . '/functions.db'); + $this->buildDatabase(); + // @todo Modify this to be rethrown as an appropriate + // Phergie_Plugin_Exception and handled in Phergie_Plugin_Php + } catch (PDOException $e) { + echo 'PDO failure: '.$e->getMessage(); + } + } + + /** + * Searches for a description of the function. + * + * @param string $function Search pattern to match against the function + * name, wildcards supported using % + * + * @return array|null Associative array containing the function name and + * description or NULL if no results are found + */ + public function findFunction($function) + { + // Remove possible parentheses + $split = preg_split('{\(|\)}', $function); + $function = (count($split)) ? array_shift($split) : $function; + + // Prepare the database statement + $stmt = $this->database->prepare('SELECT `name`, `description` FROM `functions` WHERE `name` LIKE :function'); + $stmt->execute(array(':function' => $function)); + + // Check the results + if (count($stmt) > 0) { + $result = $stmt->fetch(PDO::FETCH_ASSOC); + /** + * @todo add class and function URLS + * class methods: http://php.net/manual/en/classname.methodname.php + * functions: http://php.net/manual/en/function.functionname.php + * where '_' is replaced with '-' + */ + return $result; + } + + // No results found, return + return null; + } + + /** + * Build the database and parses the function summary file into it. + * + * @param bool $rebuild TRUE to force a rebuild of the table used to + * house function information, FALSE otherwise, defaults to FALSE + * + * @return void + */ + protected function buildDatabase($rebuild = false) + { + // Check to see if the functions table exists + $checkstmt = $this->database->query("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'"); + $checkstmt->execute(); + $result = $checkstmt->fetch(PDO::FETCH_ASSOC); + unset( $checkstmt ); + $table = $result['COUNT(*)']; + unset( $result ); + // If the table doesn't exist, create it + if (!$table) { + $this->database->exec('CREATE TABLE `functions` (`name` VARCHAR(255), `description` TEXT)'); + $this->database->exec('CREATE UNIQUE INDEX `functions_name` ON `functions` (`name`)'); + } + + // If we created a new table, fill it with data + if (!$table || $rebuild) { + // Get the contents of the source file + // @todo Handle possible error cases better here; the @ operator + // shouldn't be needed + $contents = @file($this->url, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES); + + if (!$contents) { + return; + } + + // Parse the contents + $valid = array(); + $firstPart = ''; + $lineNumber = 0; + foreach ($contents as $line) { + // Clean the current line + $line = trim($line); + + // Skip comment lines + if (0 === strpos($line, '#')) { + // reset the line if the current line is odd + if (($lineNumber % 2) !== 0) { + $lineNumber--; + } + continue; + } + + /* + * If the current line is even, it's the first part of the + * complete function description ... + */ + if (($lineNumber % 2) === 0) { + $firstPart = $line; + } else { + // ... it's the last part of the complete function description + $completeLine = $firstPart . ' ' . $line; + $firstPart = ''; + if (preg_match('{^([^\s]*)[\s]?([^)]*)\(([^\)]*)\)[\sU]+([\sa-zA-Z0-9\.,\-_()]*)$}', $completeLine, $matches)) { + $valid[] = $matches; + } + } + // Up the line number before going to the next line + $lineNumber++; + } + // free up some memory + unset($contents); + + // Process the valid matches + if (count($valid) > 0) { + // Clear the database + $this->database->exec('DELETE * FROM `functions`'); + + // Prepare the sql statement + $stmt = $this->database->prepare('INSERT INTO `functions` (`name`, `description`) VALUES (:name, :description)'); + $this->database->beginTransaction(); + + // Insert the data + foreach ($valid as $function) { + // Extract function values + list( , $retval, $name, $params, $desc) = $function; + if (empty($name)) { + $name = $retval; + $retval = ''; + } + // Reconstruct the complete function line + $line = trim($retval . ' ' . $name . '(' . $params . ') - ' . $desc); + // Execute the statement + $stmt->execute(array(':name' => $name, ':description' => $line)); + } + + // Commit the changes to the database + $this->database->commit(); + } + // free up some more memory + unset($valid); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Ping.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ping.php new file mode 100755 index 000000000..021670d97 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Ping.php @@ -0,0 +1,162 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Ping + * @author Phergie Development Team <team@phergie.org> + * @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_Ping + */ + +/** + * Uses a self CTCP PING to ensure that the client connection has not been + * dropped. + * + * @category Phergie + * @package Phergie_Plugin_Ping + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Ping + */ +class Phergie_Plugin_Ping extends Phergie_Plugin_Abstract +{ + /** + * Timestamp for the last instance in which an event was received + * + * @var int + */ + protected $lastEvent; + + /** + * Timestamp for the last instance in which a PING was sent + * + * @var int + */ + protected $lastPing; + + /** + * Initialize event timestamps upon connecting to the server. + * + * @return void + */ + public function onConnect() + { + $this->lastEvent = time(); + $this->lastPing = null; + } + + /** + * Updates the timestamp since the last received event when a new event + * arrives. + * + * @return void + */ + public function preEvent() + { + $this->lastEvent = time(); + } + + /** + * Clears the ping time if a reply is received. + * + * @return void + */ + public function onPingResponse() + { + $this->lastPing = null; + } + + /** + * Performs a self ping if the event threshold has been exceeded or + * issues a termination command if the ping threshold has been exceeded. + * + * @return void + */ + public function onTick() + { + $time = time(); + if (!empty($this->lastPing)) { + if ($time - $this->lastPing > $this->getConfig('ping.ping', 20)) { + $this->doQuit(); + } + } elseif ( + $time - $this->lastEvent > $this->getConfig('ping.event', 300) + ) { + $this->lastPing = $time; + $this->doPing($this->getConnection()->getNick(), $this->lastPing); + } + } + + /** + * Gets the last ping time + * lastPing needs exposing for things such as unit testing + * + * @return int timestamp of last ping + */ + public function getLastPing() + { + return $this->lastPing; + } + + /** + * Set the last ping time + * lastPing needs to be exposed for unit testing + * + * @param int|null $ping timestamp of last ping + * + * @return self + */ + public function setLastPing($ping = null) + { + if (null === $ping) { + $ping = time(); + } + if (!is_int($ping)) { + throw new InvalidArgumentException('$ping must be an integer or null'); + } + $this->lastPing = $ping; + return $this; + } + + /** + * Gets the last event time + * lastEvent needs exposing for things such as unit testing + * + * @return int timestamp of last ping + */ + public function getLastEvent() + { + return $this->lastEvent; + } + + /** + * Set the last event time + * lastEvent needs to be exposed for unit testing + * + * @param int|null $event timestamp of last ping + * + * @return self + */ + public function setLastEvent($event = null) + { + if (null === $event) { + $event = time(); + } + if (!is_int($event)) { + throw new InvalidArgumentException('$ping must be an integer or null'); + } + $this->lastEvent = $event; + return $this; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Pong.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Pong.php new file mode 100755 index 000000000..54e19fc06 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Pong.php @@ -0,0 +1,44 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Pong + * @author Phergie Development Team <team@phergie.org> + * @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_Pong + */ + +/** + * Responds to PING requests from the server. + * + * @category Phergie + * @package Phergie_Plugin_Pong + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Pong + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_2 + * @link http://irchelp.org/irchelp/rfc/chapter4.html#c4_6_3 + */ +class Phergie_Plugin_Pong extends Phergie_Plugin_Abstract +{ + /** + * Sends a PONG response for each PING request received by the server. + * + * @return void + */ + public function onPing() + { + $this->doPong($this->getEvent()->getArgument(0)); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Prioritize.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Prioritize.php new file mode 100755 index 000000000..2312567fb --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Prioritize.php @@ -0,0 +1,99 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Prioritize + * @author Phergie Development Team <team@phergie.org> + * @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_Prioritize + */ + +/** + * Prioritizes events such that they are executed in order from least to most + * destructive. + * + * @category Phergie + * @package Phergie_Plugin_Prioritize + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Prioritize + */ +class Phergie_Plugin_Prioritize extends Phergie_Plugin_Abstract +{ + /** + * Event types ordered by priority of execution + * + * @var array + */ + protected $priority = array( + 'raw', + 'pass', + 'user', + 'ping', + 'pong', + 'notice', + 'join', + 'list', + 'names', + 'version', + 'stats', + 'links', + 'time', + 'trace', + 'admin', + 'info', + 'who', + 'whois', + 'whowas', + 'mode', + 'privmsg', + 'action', + 'nick', + 'topic', + 'invite', + 'kill', + 'part', + 'quit' + ); + + /** + * Prioritizes events from least to most destructive. + * + * @return void + */ + public function preDispatch() + { + $events = $this->getEventHandler(); + + // Categorize events by type + $categorized = array(); + foreach ($events as $event) { + $type = $event->getType(); + if (!isset($categorized[$type])) { + $categorized[$type] = array(); + } + $categorized[$type][] = $event; + } + + // Order events by type from least to most destructive + $types = array_intersect($this->priority, array_keys($categorized)); + $prioritized = array(); + foreach ($types as $type) { + $prioritized = array_merge($prioritized, $categorized[$type]); + } + + // Replace the original events array with the prioritized one + $events->replaceEvents($prioritized); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Puppet.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Puppet.php new file mode 100644 index 000000000..bede0be2c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Puppet.php @@ -0,0 +1,89 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Puppet + * @author Phergie Development Team <team@phergie.org> + * @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_Puppet + */ + +/** + * Allows a user to effectively speak and act as the bot. + * + * @category Phergie + * @package Phergie_Plugin_Puppet + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Puppet + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Puppet extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Handles a request for the bot to repeat a given message in a specified + * channel. + * + * <code>say #chan message</code> + * + * @param string $channel Name of the channel + * @param string $message Message to repeat + * + * @return void + */ + public function onCommandSay($channel, $message) + { + $this->doPrivmsg($channel, $message); + } + + /** + * Handles a request for the bot to repeat a given action in a specified + * channel. + * + * <code>act #chan action</code> + * + * @param string $channel Name of the channel + * @param string $action Action to perform + * + * @return void + */ + public function onCommandAct($channel, $action) + { + $this->doAction($channel, $action); + } + + /** + * Handles a request for the bot to send the server a raw message + * + * <code>raw message</code> + * + * @param string $message Message to send + * + * @return void + */ + public function onCommandRaw($message) + { + $this->doRaw($message); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Quit.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Quit.php new file mode 100755 index 000000000..eca22a98e --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Quit.php @@ -0,0 +1,54 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Quit + * @author Phergie Development Team <team@phergie.org> + * @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_Quit + */ + +/** + * Terminates the current connection upon command. + * + * @category Phergie + * @package Phergie_Plugin_Quit + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Quit + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Issues a quit command when a message is received requesting that the + * bot terminate the current connection. + * + * @return void + */ + public function onCommandQuit() + { + $this->doQuit('Requested by ' . $this->getEvent()->getNick()); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Reload.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Reload.php new file mode 100755 index 000000000..430577081 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Reload.php @@ -0,0 +1,122 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Reload + * @author Phergie Development Team <team@phergie.org> + * @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_Reload + */ + +/** + * Facilitates reloading of individual plugins for development purposes. + * Note that, because existing class definitions cannot be removed from + * memory, increased memory usage is an expected result of using this plugin. + * + * @category Phergie + * @package Phergie_Plugin_Reload + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Reload + * @uses Phergie_Plugin_Command pear.phergie.org + */ +class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Command'); + } + + /** + * Reloads a specified plugin. + * + * @param string $plugin Short name of the plugin to reload + * + * @return void + */ + public function onCommandReload($plugin) + { + $plugin = ucfirst($plugin); + + $evalClass = true; + if (strpos($plugin, ' ') !== false) { + $args = explode(' ', $plugin); + $plugin = $args[0]; + if (strtolower($args[1]) == 'force') { + $evalClass = false; + } + } + + if (!$this->plugins->hasPlugin($plugin)) { + echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL; + try { + $this->plugins->getPlugin($plugin); + $this->plugins->command->populateMethodCache(); + } catch (Phergie_Plugin_Exception $e) { + if ($e->getCode() == Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND) { + echo 'DEBUG(Reload): ', $e->getMessage(), PHP_EOL; + } else { + throw $e; + } + } + return; + } + + try { + $info = $this->plugins->getPluginInfo($plugin); + } catch (Phergie_Plugin_Exception $e) { + $source = $this->event->getSource(); + $nick = $this->event->getNick(); + $this->doNotice($source, $nick . ': ' . $e->getMessage()); + return; + } + + $class = $info['class']; + $contents = file_get_contents($info['file']); + $newClass = $class . '_' . sha1($contents); + + if (class_exists($newClass, false)) { + if ($evalClass == true) { + echo 'DEBUG(Reload): Class ', $class, ' has not changed since last reload', PHP_EOL; + return; + } + } else { + $contents = preg_replace( + array('/^<\?(?:php)?/', '/class\s+' . $class . '/i'), + array('', 'class ' . $newClass), + $contents + ); + eval($contents); + } + + $instance = new $newClass; + $instance->setName($plugin); + $instance->setEvent($this->event); + $this->plugins + ->removePlugin($plugin) + ->addPlugin($instance); + + $this->plugins->command->populateMethodCache(); + if ($this->plugins->hasPlugin('Help')) { + $this->plugins->help->populateRegistry(); + } + + echo 'DEBUG(Reload): Reloaded ', $class, ' to ', $newClass, PHP_EOL; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Remind.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Remind.php new file mode 100644 index 000000000..42d674c68 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Remind.php @@ -0,0 +1,378 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Remind + * @author Phergie Development Team <team@phergie.org> + * @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_Remind + */ + +/** + * Parses and logs messages that should be relayed to other users the next time + * the recipient is active on the same channel. + * + * @category Phergie + * @package Phergie_Plugin_Remind + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Remind + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Time pear.phergie.org + * @uses extension PDO + * @uses extension pdo_sqlite + */ +class Phergie_Plugin_Remind extends Phergie_Plugin_Abstract +{ + /** + * Number of reminders to show in public. + */ + protected $publicReminders = 3; + + /** + * PDO resource for a SQLite database containing the reminders. + * + * @var resource + */ + protected $db; + + /** + * Flag that indicates whether or not to use an in-memory reminder list. + * + * @var bool + */ + protected $keepListInMemory = true; + + /** + * In-memory store for pending reminders. + */ + protected $msgStorage = array(); + + /** + * Check for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $plugins->getPlugin('Time'); + + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + $this->fail('PDO and pdo_sqlite extensions must be installed'); + } + + $dir = dirname(__FILE__) . '/' . $this->getName(); + $path = $dir . '/reminder.db'; + if (!file_exists($dir)) { + mkdir($dir); + } + + if (isset($this->config['remind.use_memory'])) { + $this->keepListInMemory = (bool) $this->config['remind.use_memory']; + } + + if (isset($this->config['remind.public_reminders'])) { + $this->publicReminders = (int) $this->config['remind.public_reminders']; + $this->publicReminders = max($this->publicReminders, 0); + } + + try { + $this->db = new PDO('sqlite:' . $path); + $this->createTables(); + } catch (PDO_Exception $e) { + throw new Phergie_Plugin_Exception($e->getMessage()); + } + } + + /** + * Intercepts a message and processes any contained recognized commands. + * + * @return void + */ + public function onPrivmsg() + { + $source = $this->getEvent()->getSource(); + $nick = $this->getEvent()->getNick(); + + $this->deliverReminders($source, $nick); + } + + /** + * Handle reminder requests + * + * @param string $recipient recipient of the message + * @param string $message message to tell the recipient + * + * @return void + * @see handleRemind() + */ + public function onCommandTell($recipient, $message) + { + $this->handleRemind($recipient, $message); + } + + /** + * Handle reminder requests + * + * @param string $recipient recipient of the message + * @param string $message message to tell the recipient + * + * @return void + * @see handleRemind() + */ + public function onCommandAsk($recipient, $message) + { + $this->handleRemind($recipient, $message); + } + + /** + * Handle reminder requests + * + * @param string $recipient recipient of the message + * @param string $message message to tell the recipient + * + * @return void + * @see handleRemind() + */ + public function onCommandRemind($recipient, $message) + { + $this->handleRemind($recipient, $message); + } + + /** + * Handles the tell/remind command (stores the message) + * + * @param string $recipient name of the recipient + * @param string $message message to store + * + * @return void + */ + protected function handleRemind($recipient, $message) + { + $source = $this->getEvent()->getSource(); + $nick = $this->getEvent()->getNick(); + + if (!$this->getEvent()->isInChannel()) { + $this->doPrivmsg($source, 'Reminders must be requested in-channel.'); + return; + } + + $q = $this->db->prepare( + 'INSERT INTO remind + ( + time, + channel, + recipient, + sender, + message + ) + VALUES + ( + :time, + :channel, + :recipient, + :sender, + :message + )' + ); + try { + $q->execute( + array( + 'time' => date(DATE_RFC822), + 'channel' => $source, + 'recipient' => strtolower($recipient), + 'sender' => strtolower($nick), + 'message' => $message + ) + ); + } catch (PDOException $e) { + } + + if ($rowid = $this->db->lastInsertId()) { + $this->doPrivmsg($source, 'ok, ' . $nick . ', message stored'); + } else { + $this->doPrivmsg( + $source, + $nick . ': bad things happened. Message not saved.' + ); + return; + } + + if ($this->keepListInMemory) { + $this->msgStorage[$source][strtolower($recipient)] = $rowid; + } + } + + /** + * Determines if the user has pending reminders, and if so, delivers them. + * + * @param string $channel channel to check + * @param string $nick nick to check + * + * @return void + */ + protected function deliverReminders($channel, $nick) + { + if ($channel[0] != '#') { + // private message, not a channel, so don't check + return; + } + + // short circuit if there's no message in memory (if allowed) + if ($this->keepListInMemory + && !isset($this->msgStorage[$channel][strtolower($nick)]) + ) { + return; + } + + // fetch and deliver messages + $reminders = $this->fetchMessages($channel, $nick); + if (count($reminders) > $this->publicReminders) { + $msgs = array_slice($reminders, 0, $this->publicReminders); + $privmsgs = array_slice($reminders, $this->publicReminders); + } else { + $msgs = $reminders; + $privmsgs = false; + } + + foreach ($msgs as $msg) { + $ts = $this->plugins->time->getCountdown($msg['time']); + $formatted = sprintf( + '%s: (from %s, %s ago) %s', + $nick, $msg['sender'], $ts, $msg['message'] + ); + $this->doPrivmsg($channel, $formatted); + $this->deleteMessage($msg['rowid'], $channel, $nick); + } + + if ($privmsgs) { + foreach ($privmsgs as $msg) { + $ts = $this->plugins->time->getCountdown($msg['time']); + $formatted = sprintf( + 'from %s, %s ago: %s', + $msg['sender'], $ts, $msg['message'] + ); + $this->doPrivmsg($nick, $formatted); + $this->deleteMessage($msg['rowid'], $channel, $nick); + } + $formatted = sprintf( + '%s: (%d more messages sent in private.)', + $nick, count($privmsgs) + ); + $this->doPrivmsg($channel, $formatted); + } + } + + /** + * Get pending messages (for a specific channel/recipient) + * + * @param string $channel channel on which to check for pending messages + * @param string $recipient user for which to check pending messages + * + * @return array of records + */ + protected function fetchMessages($channel = null, $recipient = null) + { + if ($channel) { + $qClause = 'WHERE channel = :channel AND recipient LIKE :recipient'; + $params = compact('channel', 'recipient'); + } else { + $qClause = ''; + $params = array(); + } + $q = $this->db->prepare( + 'SELECT rowid, channel, sender, recipient, time, message + FROM remind ' . $qClause + ); + $q->execute($params); + return $q->fetchAll(); + } + + /** + * Deletes a delivered message + * + * @param int $rowid ID of the message to delete + * @param string $channel message's channel + * @param string $nick message's recipient + * + * @return void + */ + protected function deleteMessage($rowid, $channel, $nick) + { + $nick = strtolower($nick); + $q = $this->db->prepare('DELETE FROM remind WHERE rowid = :rowid'); + $q->execute(array('rowid' => $rowid)); + + if ($this->keepListInMemory) { + if (isset($this->msgStorage[$channel][$nick]) + && $this->msgStorage[$channel][$nick] == $rowid + ) { + unset($this->msgStorage[$channel][$nick]); + } + } + } + + /** + * Determines if a table exists + * + * @param string $name Table name + * + * @return bool + */ + protected function haveTable($name) + { + $sql = 'SELECT COUNT(*) FROM sqlite_master WHERE name = ' + . $this->db->quote($name); + return (bool) $this->db->query($sql)->fetchColumn(); + } + + /** + * Creates the database table(s) (if they don't exist) + * + * @return void + */ + protected function createTables() + { + if (!$this->haveTable('remind')) { + $this->db->exec( + 'CREATE TABLE + remind + ( + time INTEGER, + channel TEXT, + recipient TEXT, + sender TEXT, + message TEXT + )' + ); + } + } + + /** + * Populates the in-memory cache of pending reminders + * + * @return void + */ + protected function populateMemory() + { + if (!$this->keepListInMemory) { + return; + } + foreach ($this->fetchMessages() as $msg) { + $this->msgStorage[$msg['channel']][$msg['recipient']] = $msg['rowid']; + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Serve.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Serve.php new file mode 100755 index 000000000..cdb8f7f91 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Serve.php @@ -0,0 +1,160 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Serve + * @author Phergie Development Team <team@phergie.org> + * @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_Serve + */ + +/** + * Processes requests to serve a user something from a database. + * + * @category Phergie + * @package Phergie_Plugin_Serve + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Serve + * @uses extension pdo + * @uses extension pdo_sqlite + */ +class Phergie_Plugin_Serve extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + $this->fail('PDO and pdo_sqlite extensions must be installed'); + } + } + + /** + * Retrieves a random item from the database table. + * + * @param string $database Path to the SQLite database file + * @param string $table Name of the database table + * @param array $request Parsed request + * + * @return object Retrieved item + */ + protected function getItem($database, $table, array $request) + { + $db = new PDO('sqlite:' . $database); + if (!empty($request['suggestion'])) { + $query = 'SELECT * FROM ' . $table . ' WHERE name LIKE ? ORDER BY RANDOM() LIMIT 1'; + $stmt = $db->prepare($query); + $stmt->execute(array('%' . $request['suggestion'] . '%')); + $item = $stmt->fetchObject(); + if (!$item) { + $item = new stdClass; + $item->name = $request['suggestion']; + $item->link = null; + } + } else { + $query = 'SELECT * FROM ' . $table . ' ORDER BY RANDOM() LIMIT 1'; + $stmt = $db->query($query); + $item = $stmt->fetchObject(); + } + return $item; + } + + /** + * Processes a request to serve a user something. + * + * @param string $database Path to the SQLite database file + * @param string $table Name of the database table + * @param string $format Format of the response where %target%, + * %item%, %article%', and %link will be replaced with their + * respective data + * @param string $request Request string including the target and an + * optional suggestion of the item to fetch + * @param boolean $censor TRUE to integrate with the Censor plugin, + * defaults to FALSE + * + * @return boolean TRUE if the request was processed successfully, FALSE + * otherwise + */ + public function serve($database, $table, $format, $request, $censor = false) + { + // Parse the request + $result = preg_match( + '/(?P<target>[^\s]+)(\s+an?\s+)?(?P<suggestion>.*)?/', + $request, + $match + ); + + if (!$result) { + return false; + } + + // Resolve the target + $target = $match['target']; + if ($target == 'me') { + $target = $this->event->getNick(); + } + + // Process the request + $item = $this->getItem($database, $table, $match); + + // Reprocess the request for censorship if required + if ($this->plugins->hasPlugin('Censor')) { + $plugin = $this->plugins->getPlugin('Censor'); + $attempts = 0; + while ($censor && $attempts < 3) { + $clean = $plugin->cleanString($item->name); + if ($item->name != $clean) { + $attempts++; + $item = $this->getItem($database, $table, $match); + } else { + $censor = false; + } + } + if ($censor && $attempts == 3) { + $this->doAction($this->event->getSource(), 'shrugs.'); + } + } + + // Derive the proper article for the item + if (preg_match('/^[aeiou]/i', $item->name)) { + $article = 'an'; + } else { + $article = 'a'; + } + + // Format the message + $replacements = array( + 'target' => $target, + 'item' => $item->name, + 'link' => $item->link, + 'article' => $article + ); + + $msg = $format; + foreach ($replacements as $placeholder => $value) { + $msg = str_replace( + '%' . $placeholder . '%', + $value, + $msg + ); + } + + // Send the message + $this->doAction($this->event->getSource(), $msg); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/SpellCheck.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/SpellCheck.php new file mode 100644 index 000000000..b731cffc8 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/SpellCheck.php @@ -0,0 +1,118 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_TerryChay + * @author Phergie Development Team <team@phergie.org> + * @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_TerryChay + */ + +/** + * Handles requests for checking spelling of specified words and returning + * either confirmation of correctly spelled words or potential correct + * spellings for misspelled words. + * + * @category Phergie + * @package Phergie_Plugin_SpellCheck + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_TerryChay + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses extension pspell + */ +class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract +{ + /** + * Spell check dictionary handler + * + * @var resource + */ + protected $pspell; + + /** + * Limit on the number of potential correct spellings returned + * + * @var int + */ + protected $limit; + + /** + * Check for dependencies. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('pspell')) { + $this->fail('pspell php extension is required'); + } + + if (!$this->getConfig('spellcheck.lang')) { + $this->fail('Setting spellcheck.lang must be filled-in'); + } + + $this->plugins->getPlugin('Command'); + + set_error_handler(array($this, 'loadDictionaryError')); + $this->pspell = pspell_new($this->getConfig('spellcheck.lang')); + restore_error_handler(); + + $this->limit = $this->getConfig('spellcheck.limit', 5); + } + + /** + * Intercepts and handles requests for spell checks. + * + * @param string $word the string to perform checks against + * + * @return void + */ + public function onCommandSpell($word) + { + $source = $this->event->getSource(); + $target = $this->event->getNick(); + + $message = $target . ': The word "' . $word; + $message .= '" seems to be spelled correctly.'; + if (!pspell_check($this->pspell, $word)) { + $suggestions = pspell_suggest($this->pspell, $word); + + $message = $target; + $message .= ': I could not find any suggestions for "' . $word . '".'; + if (!empty($suggestions)) { + $suggestions = array_splice($suggestions, 0, $this->limit); + $message = $target . ': Suggestions for "'; + $message .= $word . '": ' . implode(', ', $suggestions) . '.'; + } + } + + $this->doPrivmsg($source, $message); + } + + /** + * Handle any errors from loading dictionary + * + * @param integer $errno Error code + * @param string $errstr Error message + * @param string $errfile File that errored + * @param integer $errline Line where the error happened + * + * @return void + */ + protected function loadDictionaryError($errno, $errstr, $errfile, $errline) + { + $this->fail($errstr); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php new file mode 100644 index 000000000..dc2680a6d --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Statusnet.php @@ -0,0 +1,143 @@ +<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Talks to the Statusnet IM architecture to enqueue incoming message messages
+ * and notify result of nickname registration checks
+ *
+ * @category Phergie
+ * @package Phergie_Plugin_Statusnet
+ * @author Luke Fitzgerald <lw.fitzgerald@googlemail.com>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class Phergie_Plugin_Statusnet extends Phergie_Plugin_Abstract {
+ /**
+ * Message callback details
+ *
+ * @var array
+ */
+ protected $messageCallback;
+
+ /**
+ * Registration check callback details
+ *
+ * @var array
+ */
+ protected $regCallback;
+
+ /**
+ * Connection established callback details
+ *
+ * @var array
+ */
+ protected $connectedCallback;
+
+ /**
+ * Load callback from config
+ */
+ public function onLoad() {
+ $messageCallback = $this->config['statusnet.messagecallback'];
+ if (is_callable($messageCallback)) {
+ $this->messageCallback = $messageCallback;
+ } else {
+ $this->messageCallback = NULL;
+ }
+
+ $regCallback = $this->config['statusnet.regcallback'];
+ if (is_callable($regCallback)) {
+ $this->regCallback = $regCallback;
+ } else {
+ $this->regCallback = NULL;
+ }
+
+ $connectedCallback = $this->config['statusnet.connectedcallback'];
+ if (is_callable($connectedCallback)) {
+ $this->connectedCallback = $connectedCallback;
+ } else {
+ $this->connectedCallback = NULL;
+ }
+
+ $this->unregRegexp = $this->getConfig('statusnet.unregregexp', '/\x02(.*?)\x02 (?:isn\'t|is not) registered/i');
+ $this->regRegexp = $this->getConfig('statusnet.regregexp', '/(?:\A|\x02)(\w+?)\x02? (?:\(account|is \w+?\z)/i');
+ }
+
+ /**
+ * Passes incoming messages to StatusNet
+ *
+ * @return void
+ */
+ public function onPrivmsg() {
+ if ($this->messageCallback !== NULL) {
+ $event = $this->getEvent();
+ $source = $event->getSource();
+ $sender = $event->getNick();
+ $message = trim($event->getText());
+
+ if (strpos($source, '#') === 0) {
+ $botNick = $this->getConnection()->getNick();
+ $nickPos = strpos($message, $botNick);
+ $nickLen = strlen($botNick);
+ $colonPos = strpos($message, ':', $nickLen);
+ $commandStr = trim(substr($message, $colonPos+1));
+ if ($nickPos === 0 && $colonPos == $nickLen && !empty($commandStr)) {
+ call_user_func($this->messageCallback, array('source' => $source, 'sender' => $sender, 'message' => $commandStr));
+ }
+ } else {
+ call_user_func($this->messageCallback, array('source' => $source, 'sender' => $sender, 'message' => $message));
+ }
+ }
+ }
+
+ /**
+ * Catches the response from NickServ
+ *
+ * @return void
+ */
+ public function onNotice() {
+ if ($this->regCallback !== NULL) {
+ $event = $this->getEvent();
+ if ($event->getNick() == 'NickServ') {
+ $message = $event->getArgument(1);
+ if (preg_match($this->unregRegexp, $message, $groups)) {
+ $screenname = $groups[1];
+ call_user_func($this->regCallback, array('screenname' => $screenname, 'registered' => false));
+ } elseif (preg_match($this->regRegexp, $message, $groups)) {
+ $screenname = $groups[1];
+ call_user_func($this->regCallback, array('screenname' => $screenname, 'registered' => true));
+ }
+ }
+ }
+ }
+
+ /**
+ * Intercepts the end of the "message of the day" response and tells
+ * StatusNet we're connected
+ *
+ * @return void
+ */
+ public function onResponse() {
+ switch ($this->getEvent()->getCode()) {
+ case Phergie_Event_Response::RPL_ENDOFMOTD:
+ case Phergie_Event_Response::ERR_NOMOTD:
+ if ($this->connectedCallback !== NULL) {
+ call_user_func($this->connectedCallback);
+ }
+ }
+ }
+}
diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea.php new file mode 100644 index 000000000..c6453566c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea.php @@ -0,0 +1,69 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Tea + * @author Phergie Development Team <team@phergie.org> + * @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_Tea + */ + +/** + * Processes requests to serve users tea. + * + * @category Phergie + * @package Phergie_Plugin_Tea + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Tea + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Tea extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user tea. + * + * @param string $request Request including the target and an optional + * suggestion of what tea to serve + * + * @return void + */ + public function onCommandTea($request) + { + $format = $this->getConfig( + 'tea.format', + 'serves %target% a cup of %item% tea.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Tea/tea.db', + 'tea', + $format, + $request + ); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea/db.php new file mode 100644 index 000000000..21fe1950d --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tea/db.php @@ -0,0 +1,49 @@ +<?php + +if (!defined('__DIR__')) { + define('__DIR__', dirname(__FILE__)); +} + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/tea.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE tea (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX tea_name ON tea (name)'); +$insert = $db->prepare('INSERT INTO tea (name, link) VALUES (:name, :link)'); + +// Get raw teacuppa.com data set +echo 'Downloading teacuppa.com data set', PHP_EOL; +$file = __DIR__ . '/tea-list.html'; +if (!file_exists($file)) { + copy('http://www.teacuppa.com/tea-list.asp', $file); +} +$contents = file_get_contents($file); + +// Extract data from data set +echo 'Processing teacuppa.com data', PHP_EOL; +$contents = tidy_repair_string($contents); +libxml_use_internal_errors(true); +$doc = new DOMDocument; +$doc->loadHTML($contents); +libxml_clear_errors(); +$xpath = new DOMXPath($doc); +$teas = $xpath->query('//p[@class="page_title"]/following-sibling::table//a'); +$db->beginTransaction(); +foreach ($teas as $tea) { + $name = preg_replace( + array('/\s*\v+\s*/', '/\s+tea\s*$/i'), + array(' ', ''), + $tea->textContent + ); + $link = 'http://teacuppa.com/' . $tea->getAttribute('href'); + $insert->execute(array($name, $link)); +} +$db->commit(); + +// Clean up +echo 'Cleaning up', PHP_EOL; +unlink($file); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Temperature.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Temperature.php new file mode 100644 index 000000000..541fd85cf --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Temperature.php @@ -0,0 +1,81 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Temperature + * @author Phergie Development Team <team@phergie.org> + * @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_Temperature + */ + +/** + * Performs temperature calculations for other plugins. + * + * @category Phergie + * @package Phergie_Plugin_Temperature + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Temperature + */ +class Phergie_Plugin_Temperature extends Phergie_Plugin_Abstract +{ + /** + * Converts a temperature in Celsius to Fahrenheit. + * + * @param int $temp Temperature in Celsius + * + * @return int Temperature converted to Fahrenheit + */ + public function convertCelsiusToFahrenheit($temp) + { + return round(((((int) $temp * 9) / 5) + 32)); + } + + /** + * Converts a temperature in Fahrenheit to Celsius. + * + * @param int $temp Temperature in Fahrenheit + * + * @return int Temperature converted to Celsius + */ + public function convertFahrenheitToCelsius($temp) + { + return round(((((int) $temp - 32) * 5) / 9)); + } + + /** + * Calculates the heat index (i.e. "feels like" temperature) based on + * temperature and relative humidity. + * + * @param int $temperature Temperature in degrees Fahrenheit + * @param int $humidity Relative humidity (ex: 68) + * @return int Heat index in degrees Fahrenheit + */ + public function getHeatIndex($temperature, $humidity) + { + $temperature2 = $temperature * $temperature; + $humidity2 = $humidity * $humidity; + return round( + -42.379 + + (2.04901523 * $temperature) + + (10.14333127 * $humidity) - + (0.22475541 * $temperature * $humidity) - + (0.00683783 * $temperature2) - + (0.05481717 * $humidity2) + + (0.00122874 * $temperature2 * $humidity) + + (0.00085282 * $temperature * $humidity2) - + (0.00000199 * $temperature2 * $humidity2) + ); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/TerryChay.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/TerryChay.php new file mode 100644 index 000000000..246cfc398 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/TerryChay.php @@ -0,0 +1,94 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_TerryChay + * @author Phergie Development Team <team@phergie.org> + * @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_TerryChay + */ + +/** + * Parses incoming messages for the words "Terry Chay" or tychay and responds + * with a random Terry fact retrieved from the Chayism web service. + * + * @category Phergie + * @package Phergie_Plugin_TerryChay + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_TerryChay + * @uses Phergie_Plugin_Http pear.phergie.org + */ +class Phergie_Plugin_TerryChay extends Phergie_Plugin_Abstract +{ + /** + * URL to the web service + * + * @const string + */ + const URL = 'http://phpdoc.info/chayism/'; + + /** + * HTTP plugin + * + * @var Phergie_Plugin_Http + */ + protected $http; + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $this->getPluginHandler()->getPlugin('Http'); + } + + /** + * Fetches a chayism. + * + * @return string|bool Fetched chayism or FALSE if the operation failed + */ + public function getChayism() + { + return $this + ->getPluginHandler() + ->getPlugin('Http') + ->get(self::URL) + ->getContent(); + } + + /** + * Parses incoming messages for "Terry Chay" and related variations and + * responds with a chayism. + * + * @return void + */ + public function onPrivmsg() + { + $event = $this->getEvent(); + $source = $event->getSource(); + $message = $event->getText(); + $pattern + = '{^(' . preg_quote($this->getConfig('command.prefix')) . + '\s*)?.*(terry\s+chay|tychay)}ix'; + + if (preg_match($pattern, $message)) { + if($fact = $this->getChayism()) { + $this->doPrivmsg($source, 'Fact: ' . $fact); + } + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/TheFuckingWeather.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/TheFuckingWeather.php new file mode 100644 index 000000000..8559426b6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/TheFuckingWeather.php @@ -0,0 +1,144 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_TheFuckingWeather + * @author Phergie Development Team <team@phergie.org> + * @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_TheFuckingWeather + */ + +/** + * Detects and responds to requests for current weather conditions in a + * particular location using data from a web service. + * + * @category Phergie + * @package Phergie_Plugin_TheFuckingWeather + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_TheFuckingWeather + * @link http://thefuckingweather.com + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + */ + +class Phergie_Plugin_TheFuckingWeather extends Phergie_Plugin_Abstract +{ + /** + * HTTP plugin + * + * @var Phergie_Plugin_Http + */ + protected $http = null; + + /** + * Base API URL + * + * @var string + */ + protected $url = 'http://www.thefuckingweather.com/?zipcode='; + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $pluginHandler = $this->getPluginHandler(); + $pluginHandler->getPlugin('Command'); + $this->http = $pluginHandler->getPlugin('Http'); + } + + /** + * Returns the weather from the specified location. + * + * @param string $location Location term + * + * @return void + * @todo Implement use of URL shortening here + */ + public function onCommandThefuckingweather($location) + { + $source = $this->getEvent()->getSource(); + $target = $this->getEvent()->getNick(); + $out = $this->getWeather($location); + if (!$out) { + $this->doNotice($source, $out); + } else { + $this->doPrivmsg($source, $target . ': ' . $out); + } + } + + /** + * Alias for TheFuckingWeather command. + * + * @param string $location Location term + * + * @return void + */ + public function onCommandTfw($location) + { + $this->onCommandThefuckingweather($location); + } + + /** + * Get the necessary content and returns the search result. + * + * @param string $location Location term + * + * @return string|bool Search result or FALSE if none is found + * @todo Try to optimize pregs + */ + protected function getWeather($location) + { + $url = $this->url . urlencode($location); + $response = $this->http->get($url); + $content = $response->getContent(); + + preg_match_all( + '#<div><span class="small">(.*?)<\/span><\/div>#im', + $content, $matches + ); + $location = $matches[1][0]; + + if (!empty($location)) { + preg_match_all( + '#<div class="large" >(.*?)<br \/>#im', + $content, $matches + ); + $temp_numb = (int) $matches[1][0]; + $temp_numb .= ' F / ' . round(($temp_numb - 32) / 1.8, 0) . ' C?!'; + + preg_match_all( + '#<br \/>(.*?)<\/div><div id="remark"><br \/>#im', + $content, $matches + ); + $temp_desc = $matches[1][0]; + + preg_match_all( + '#<div id="remark"><br \/>\n<span>(.*?)<\/span><\/div>#im', + $content, $matches + ); + $remark = $matches[1][0]; + + $result = "{$location}: {$temp_numb} {$temp_desc} ({$remark})"; + $result = preg_replace('/</', ' <', $result); + $result = strip_tags($result); + return html_entity_decode($result); + } else { + return 'No fucking clue where that is.'; + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Time.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Time.php new file mode 100644 index 000000000..0d90bd8a6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Time.php @@ -0,0 +1,72 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Time + * @author Phergie Development Team <team@phergie.org> + * @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_Time + */ + +/** + * Helper plugin to assist other plugins with time manipulation, display. + * + * Any shared time-related code should go into this class. + * + * @category Phergie + * @package Phergie_Plugin_Time + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Time + */ +class Phergie_Plugin_Time extends Phergie_Plugin_Abstract +{ + /** + * Returns the time interval between the current time and a given + * timestamp. + * + * @param string $timestamp Timestamp compatible with strtotime() + * + * @return string + */ + public function getCountdown($timestamp) + { + $time = time() - strtotime($timestamp); + $return = array(); + + $days = floor($time / 86400); + if ($days > 0) { + $return[] = $days . 'd'; + $time %= 86400; + } + + $hours = floor($time / 3600); + if ($hours > 0) { + $return[] = $hours . 'h'; + $time %= 3600; + } + + $minutes = floor($time / 60); + if ($minutes > 0) { + $return[] = $minutes . 'm'; + $time %= 60; + } + + if ($time > 0 || count($return) <= 0) { + $return[] = ($time > 0 ? $time : '0') . 's'; + } + + return implode(' ', $return); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld.php new file mode 100644 index 000000000..d7d64a471 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld.php @@ -0,0 +1,148 @@ +<?php + +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Url + * @author Phergie Development Team <team@phergie.org> + * @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_Url + */ + +/** + * Responds to a request for a TLD (formatted as .tld where tld is the TLD to + * be looked up) with its corresponding description. + * + * @category Phergie + * @package Phergie_Plugin_Tld + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Tld + * @uses extension PDO + * @uses extension pdo_sqlite + */ +class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract +{ + /** + * Connection to the database + * + * @var PDO + */ + protected $db; + + /** + * Prepared statement for selecting a single TLD + * + * @var PDOStatement + */ + protected $select; + + /** + * Prepared statement for selecting all TLDs + * + * @var PDOStatement + */ + protected $selectAll; + + /** + * Checks for dependencies and sets up the database and hard-coded values. + * + * @return void + */ + public function onLoad() + { + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + $this->fail('PDO and pdo_sqlite extensions must be installed'); + } + + $dbFile = dirname(__FILE__) . '/Tld/tld.db'; + try { + $this->db = new PDO('sqlite:' . $dbFile); + + $this->select = $this->db->prepare(' + SELECT type, description + FROM tld + WHERE LOWER(tld) = LOWER(:tld) + '); + + $this->selectAll = $this->db->prepare(' + SELECT tld, type, description + FROM btld + '); + } catch (PDOException $e) { + $this->getPluginHandler()->removePlugin($this); + } + } + + /** + * takes a tld in the format '.tld' and returns its related data + * + * @param string $tld tld to process + * + * @return null + */ + public function onCommandTld($tld) + { + $tld = ltrim($tld, '.'); + $description = $this->getTld($tld); + $this->doPrivmsg( + $this->event->getSource(), + "{$this->getEvent()->getNick()}: .{$tld} -> " + . ($description ? $description : 'Unknown TLD') + ); + } + + /** + * Retrieves the definition for a given TLD if it exists + * + * @param string $tld TLD to search for + * + * @return mixed Definition of the given TLD as a string or false if unknown + */ + public function getTld($tld) + { + $tld = trim(strtolower($tld)); + if ($this->select->execute(array('tld' => $tld))) { + $tlds = $this->select->fetch(); + if (is_array($tlds)) { + return '(' . $tlds['type'] . ') ' . $tlds['description']; + } + } + return false; + } + + /** + * Retrieves a list of all the TLDs and their definitions + * + * @return mixed Array of all the TLDs and their definitions or FALSE on + * failure + */ + public function getTlds() + { + if ($this->selectAll->execute()) { + $tlds = $this->selectAll->fetchAll(); + if (is_array($tlds)) { + $tldinfo = array(); + foreach ($tlds as $key => $tld) { + if (!empty($tld['tld'])) { + $tldinfo[$tld['tld']] = "({$tld['type']}) " + . $tld['description']; + } + } + return $tldinfo; + } + } + return false; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld/db.php new file mode 100644 index 000000000..28f963ad1 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Tld/db.php @@ -0,0 +1,68 @@ +<?php + +$dbFile = 'tld.db'; + +if (file_exists($dbFile)) { + exit; +} + +$db = new PDO('sqlite:' . dirname(__FILE__) . '/' . $dbFile); + +$query = ' + CREATE TABLE tld ( + tld VARCHAR(20), + type VARCHAR(20), + description VARCHAR(255) + ) +'; +$db->exec($query); + +$insert = $db->prepare(' + INSERT INTO tld (tld, type, description) + VALUES (:tld, :type, :description) +'); + +$contents = file_get_contents( + 'http://www.iana.org/domains/root/db/' +); + +libxml_use_internal_errors(true); +$doc = new DOMDocument; +$doc->loadHTML($contents); +libxml_clear_errors(); + +$descriptions = array( + 'com' => 'Commercial', + 'info' => 'Information', + 'net' => 'Network', + 'org' => 'Organization', + 'edu' => 'Educational', + 'name' => 'Individuals, by name' +); + +$xpath = new DOMXPath($doc); +$rows = $xpath->query('//tr[contains(@class, "iana-group")]'); +foreach (range(0, $rows->length - 1) as $index) { + $row = $rows->item($index); + $tld = strtolower(ltrim($row->childNodes->item(0)->textContent, '.')); + $type = $row->childNodes->item(1)->nodeValue; + if (isset($descriptions[$tld])) { + $description = $descriptions[$tld]; + } else { + $description = $row->childNodes->item(2)->textContent; + $regex = '{(^(?:Reserved|Restricted)\s*(?:exclusively\s*)?' + . '(?:for|to)\s*(?:members of\s*)?(?:the|support)?' + . '\s*|\s*as advised.*$)}i'; + $description = preg_replace($regex, '', $description); + $description = ucfirst(trim($description)); + } + $data = array_map( + 'html_entity_decode', + array( + 'tld' => $tld, + 'type' => $type, + 'description' => $description + ) + ); + $insert->execute($data); +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter.php new file mode 100644 index 000000000..4a77d1e41 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter.php @@ -0,0 +1,206 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Twitter + * @author Phergie Development Team <team@phergie.org> + * @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_Twitter + */ + +/** + * These requires are for library code, so they don't fit Autoload's normal + * conventions. + * + * @link http://github.com/scoates/simpletweet + */ +require dirname(__FILE__) . '/Twitter/twitter.class.php'; +require dirname(__FILE__) . '/Twitter/laconica.class.php'; + +/** + * Twitter plugin; Allows tweet (if configured) and twitter commands + * + * Usage: + * tweet text to tweet + * (sends a message to twitter and Phergie will give you the link) + * twitter username + * (fetches and displays the last tweet by @username) + * twitter username 3 + * (fetches and displays the third last tweet by @username) + * twitter 1234567 + * (fetches and displays tweet number 1234567) + * http://twitter.com/username/statuses/1234567 + * (same as `twitter 1234567`) + * + * @category Phergie + * @package Phergie_Plugin_Twitter + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Twitter + * @uses Phergie_Plugin_Time pear.phergie.org + */ +class Phergie_Plugin_Twitter extends Phergie_Plugin_Abstract +{ + /** + * Twitter object (from Simpletweet) + */ + protected $twitter; + + /** + * Twitter user + */ + protected $twitteruser = null; + + /** + * Password + */ + protected $twitterpassword = null; + + /** + * Register with the URL plugin, if possible + * + * @return void + */ + public function onConnect() + { + if ($url = $this->getPluginHandler()->getPlugin('Url')) { + $url->registerRenderer($this); + } + } + + /** + * Initialize (set up configuration vars) + * + * @return void + */ + public function onLoad() + { + if (!isset($this->config['twitter.class']) + || !$twitterClass = $this->config['twitter.class'] + ) { + $twitterClass = 'Twitter'; + } + + $this->twitteruser = $this->config['twitter.user']; + $this->twitterpassword = $this->config['twitter.password']; + $url = $this->config['twitter.url']; + + $this->twitter = new $twitterClass( + $this->twitteruser, + $this->twitterpassword, + $url + ); + + } + + /** + * Fetches the associated tweet and relays it to the channel + * + * @param string $tweeter if numeric the tweet number/id, otherwise the + * twitter user name (optionally prefixed with @) + * @param int $num optional tweet number for this user (number of + * tweets ago) + * + * @return void + */ + public function onCommandTwitter($tweeter = null, $num = 1) + { + $source = $this->getEvent()->getSource(); + if (is_numeric($tweeter)) { + $tweet = $this->twitter->getTweetByNum($tweeter); + } else if (is_null($tweeter) && $this->twitteruser) { + $tweet = $this->twitter->getLastTweet($this->twitteruser, 1); + } else { + $tweet = $this->twitter->getLastTweet(ltrim($tweeter, '@'), $num); + } + if ($tweet) { + $this->doPrivmsg($source, $this->formatTweet($tweet)); + } + } + + /** + * Sends a tweet to Twitter as the configured user + * + * @param string $txt the text to tweet + * + * @return void + */ + public function onCommandTweet($txt) + { + $nick = $this->getEvent()->getNick(); + if (!$this->twitteruser) { + return; + } + $source = $this->getEvent()->getSource(); + if ($tweet = $this->twitter->sendTweet($txt)) { + $this->doPrivmsg( + $source, 'Tweeted: ' + . $this->twitter->getUrlOutputStatus($tweet) + ); + } else { + $this->doNotice($nick, 'Tweet failed'); + } + } + + /** + * Formats a Tweet into a message suitable for output + * + * @param object $tweet JSON-decoded tweet object from Twitter + * @param bool $includeUrl whether or not to include the URL in the + * formatted output + * + * @return string + */ + protected function formatTweet(StdClass $tweet, $includeUrl = true) + { + $ts = $this->plugins->time->getCountDown($tweet->created_at); + $out = '<@' . $tweet->user->screen_name .'> '. $tweet->text + . ' - ' . $ts . ' ago'; + if ($includeUrl) { + $out .= ' (' . $this->twitter->getUrlOutputStatus($tweet) . ')'; + } + return $out; + } + + /** + * Renders a URL + * + * @param array $parsed parse_url() output for the URL to render + * + * @return bool + */ + public function renderUrl(array $parsed) + { + if ($parsed['host'] != 'twitter.com' + && $parsed['host'] != 'www.twitter.com' + ) { + // unable to render non-twitter URLs + return false; + } + + $source = $this->getEvent()->getSource(); + + if (preg_match('#^/(.*?)/status(es)?/([0-9]+)$#', $parsed['path'], $matches) + ) { + $tweet = $this->twitter->getTweetByNum($matches[3]); + if ($tweet) { + $this->doPrivmsg($source, $this->formatTweet($tweet, false)); + } + return true; + } + + // if we get this far, we haven't satisfied the URL, so bail: + return false; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/laconica.class.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/laconica.class.php new file mode 100644 index 000000000..e411991db --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/laconica.class.php @@ -0,0 +1,41 @@ +<?php +/** + * Sean's Simple Twitter Library - Laconica extension + * + * Copyright 2008, Sean Coates + * Usage of the works is permitted provided that this instrument is retained + * with the works, so that any entity that uses the works is notified of this + * instrument. + * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + * ( Fair License - http://www.opensource.org/licenses/fair.php ) + * Short license: do whatever you like with this. + * + */ +class Twitter_Laconica extends Twitter { + + /** + * Constructor; sets up configuration. + * + * @param string $user Laconica user name; null for limited read-only access + * @param string $pass Laconica password; null for limited read-only access + * @param string $baseUrl Base URL of Laconica install. Defaults to identi.ca + */ + public function __construct($user=null, $pass=null, $baseUrl = 'http://identi.ca/') { + $this->baseUrl = $baseUrl; + parent::__construct($user, $pass); + } + + /** + * Returns the base API URL + */ + protected function getUrlApi() { + return $this->baseUrlFull . 'api/'; + } + + /** + * Output URL: status + */ + public function getUrlOutputStatus(StdClass $tweet) { + return $this->baseUrl . 'notice/' . urlencode($tweet->id); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/twitter.class.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/twitter.class.php new file mode 100644 index 000000000..31173a6d0 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Twitter/twitter.class.php @@ -0,0 +1,287 @@ +<?php +/** + * Sean's Simple Twitter Library + * + * Probably a little more or a little less than you need. + * + * Copyright 2008, Sean Coates + * Usage of the works is permitted provided that this instrument is retained + * with the works, so that any entity that uses the works is notified of this + * instrument. + * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. + * ( Fair License - http://www.opensource.org/licenses/fair.php ) + * Short license: do whatever you like with this. + * + * komode: le=unix language=php codepage=utf8 tab=4 notabs indent=4 + */ +class Twitter { + + /** + * Base URL for Twitter API + * + * Do not specify user/password in URL + */ + protected $baseUrl = 'http://twitter.com/'; + + /** + * Full base URL (includes user/pass) + * + * (created in Init) + */ + protected $baseUrlFull = null; + + /** + * Twitter API user + */ + protected $user; + + /** + * Twitter API password + */ + protected $pass; + + /** + * Constructor; sets up configuration. + * + * @param string $user Twitter user name; null for limited read-only access + * @param string $pass Twitter password; null for limited read-only access + */ + public function __construct($user=null, $pass=null) { + $this->baseUrlFull = $this->baseUrl; + if (null !== $user) { + // user is defined, so use it in the URL + $this->user = $user; + $this->pass = $pass; + $parsed = parse_url($this->baseUrl); + $this->baseUrlFull = $parsed['scheme'] . '://' . $this->user . ':' . + $this->pass . '@' . $parsed['host']; + // port (optional) + if (isset($parsed['port']) && is_numeric($parsed['port'])) { + $this->baseUrlFull .= ':' . $parsed['port']; + } + // append path (default: /) + if (isset($parsed['path'])) { + $this->baseUrlFull .= $parsed['path']; + } else { + $this->baseUrlFull .= '/'; + } + } + } + + /** + * Fetches a tweet by its number/id + * + * @param int $num the tweet id/number + * @return string (null on failure) + */ + public function getTweetByNum($num) { + if (!is_numeric($num)) { + return; + } + $tweet = json_decode(file_get_contents($this->getUrlStatus($num))); + return $tweet; + } + + /** + * Reads [last] tweet from user + * + * @param string $tweeter the tweeter username + * @param int $num this many tweets ago (1 = current tweet) + * @return string (false on failure) + */ + public function getLastTweet($tweeter, $num = 1) + { + $source = json_decode(file_get_contents($this->getUrlUserTimeline($tweeter))); + if ($num > count($source)) { + return false; + } + $tweet = $source[$num - 1]; + if (!isset($tweet->user->screen_name) || !$tweet->user->screen_name) { + return false; + } + return $tweet; + } + + /** + * fetches mentions for a user + */ + public function getMentions($sinceId=null, $count=20) { + return json_decode(file_get_contents($this->getUrlMentions($sinceId, $count))); + } + + /** + * Fetches followers for a user + */ + public function getFollowers($cursor=-1) { + return json_decode(file_get_contents($this->getUrlFollowers($cursor))); + } + + /** + * Follow a userid + */ + public function follow($userId) { + $params = array( + 'http' => array( + 'method' => 'POST', + 'content' => array(), + 'header' => 'Content-type: application/x-www-form-urlencoded', + ) + ); + $ctx = stream_context_create($params); + $fp = fopen($this->getUrlFollow($userId), 'rb', false, $ctx); + if (!$fp) { + return false; + } + $response = stream_get_contents($fp); + if ($response === false) { + return false; + } + $response = json_decode($response); + return $response; + } + + /** + * fetches DMs for a user + */ + public function getDMs($sinceId=null, $count=20, $page=1) { + return json_decode(file_get_contents($this->getUrlDMs($sinceId, $count, $page))); + } + + /** + * Send DM + */ + public function sendDM($screenName, $text) { + $data = http_build_query(array('screen_name'=>$screenName, 'text'=>$text)); + $params = array( + 'http' => array( + 'method' => 'POST', + 'content' => $data, + 'header' => 'Content-type: application/x-www-form-urlencoded', + ) + ); + $ctx = stream_context_create($params); + $fp = fopen($this->getUrlSendDM(), 'rb', false, $ctx); + if (!$fp) { + return false; + } + $response = stream_get_contents($fp); + if ($response === false) { + return false; + } + $response = json_decode($response); + return $response; + } + + /** + * Sends a tweet + * + * @param string $txt the tweet text to send + * @return string URL of tweet (or false on failure) + */ + public function sendTweet($txt, $limit=true) { + if ($limit) { + $txt = substr($txt, 0, 140); // twitter message size limit + } + $data = 'status=' . urlencode($txt); + $params = array( + 'http' => array( + 'method' => 'POST', + 'content' => $data, + 'header' => 'Content-type: application/x-www-form-urlencoded', + ) + ); + $ctx = stream_context_create($params); + $fp = fopen($this->getUrlTweetPost(), 'rb', false, $ctx); + if (!$fp) { + return false; + } + $response = stream_get_contents($fp); + if ($response === false) { + return false; + } + $response = json_decode($response); + return $response; + } + + /** + * Returns the base API URL + */ + protected function getUrlApi() { + return $this->baseUrlFull; + } + + /** + * Returns the status URL + * + * @param int $num the tweet number + */ + protected function getUrlStatus($num) { + return $this->getUrlApi() . 'statuses/show/'. urlencode($num) .'.json'; + } + + /** + * Returns the user timeline URL + */ + protected function getUrlUserTimeline($user) { + return $this->getUrlApi() . 'statuses/user_timeline/'. urlencode($user) .'.json'; + } + + /** + * Returns the tweet posting URL + */ + protected function getUrlTweetPost() { + return $this->getUrlApi() . 'statuses/update.json'; + } + + /** + * Output URL: status + */ + public function getUrlOutputStatus(StdClass $tweet) { + return $this->baseUrl . urlencode($tweet->user->screen_name) . '/statuses/' . urlencode($tweet->id); + } + + /** + * Return mentions URL + */ + public function getUrlMentions($sinceId=null, $count=20) { + $url = $this->baseUrlFull . 'statuses/mentions.json?count=' . urlencode($count); + if ($sinceId !== null) { + $url .= '&since_id=' . urlencode($sinceId); + } + return $url; + } + + /** + * Returns the followers URL + */ + public function getUrlFollowers($cursor=-1) { + return $this->baseUrlFull . 'statuses/followers.json?cursor=' . ((int)$cursor); + } + + /** + * Returns the follow-user URL + */ + public function getUrlFollow($userid) { + return $this->baseUrlFull . 'friendships/create/' . ((int) $userid) . '.json'; + } + + /** + * Returns the get DMs URL + */ + public function getUrlDMs($sinceId=null, $count=20, $page=1) { + $url = $this->baseUrlFull . 'direct_messages.json?'; + if ($sinceId !== null) { + $url .= 'since_id=' . urlencode($sinceId); + } + $url .= "&page={$page}"; + $url .= "&count={$count}"; + return $url; + } + + /** + * Returns the send DM URL + */ + public function getURLSendDM() { + return $this->baseUrlFull . 'direct_messages/new.json'; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Url.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url.php new file mode 100644 index 000000000..bac115b1b --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url.php @@ -0,0 +1,638 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Url + * @author Phergie Development Team <team@phergie.org> + * @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_Url + */ + +/** + * Monitors incoming messages for instances of URLs and responds with messages + * containing relevant information about detected URLs. + * + * Has an utility method accessible via + * $this->getPlugin('Url')->getTitle('http://foo..'). + * + * @category Phergie + * @package Phergie_Plugin_Url + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Url + * @uses Phergie_Plugin_Encoding pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + * @uses Phergie_Plugin_Tld pear.phergie.org + */ +class Phergie_Plugin_Url extends Phergie_Plugin_Abstract +{ + /** + * Links output format + * + * Can use the variables %nick%, %title% and %link% in it to display + * page titles and links + * + * @var string + */ + protected $baseFormat = '%message%'; + protected $messageFormat = '[ %link% ] %title%'; + + /** + * Flag indicating whether a single response should be sent for a single + * message containing multiple links + * + * @var bool + */ + protected $mergeLinks = true; + + /** + * Max length of the fetched URL title + * + * @var int + */ + protected $titleLength = 40; + + /** + * Url cache to prevent spamming, especially with multiple bots on the + * same channel + * + * @var array + */ + protected $urlCache = array(); + protected $shortCache = array(); + + /** + * Time in seconds to store the cached entries + * + * Setting it to 0 or below disables the cache expiration + * + * @var int + */ + protected $expire = 1800; + + /** + * Number of entries to keep in the cache at one time per channel + * + * Setting it to 0 or below disables the cache limit + * + * @var int + */ + protected $limit = 10; + + /** + * Flag that determines if the plugin will fall back to using an HTTP + * stream when a URL using SSL is detected and OpenSSL support isn't + * available in the PHP installation in use + * + * @var bool + */ + protected $sslFallback = true; + + /** + * Flag that is set to true by the custom error handler if an HTTP error + * code has been received + * + * @var boolean + */ + protected $errorStatus = false; + protected $errorMessage = null; + + /** + * Flag indicating whether or not to display error messages as the title + * if a link posted encounters an error + * + * @var boolean + */ + protected $showErrors = true; + + /** + * Flag indicating whether to detect schemeless URLS (i.e. "example.com") + * + * @var boolean + */ + protected $detectSchemeless = false; + + /** + * Shortener object + */ + protected $shortener; + + /** + * Array of renderers + */ + protected $renderers = array(); + + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Encoding'); + $plugins->getPlugin('Http'); + $plugins->getPlugin('Tld'); + + // make the shortener configurable + $shortener = $this->getConfig('url.shortener', 'Trim'); + $shortener = "Phergie_Plugin_Url_Shorten_{$shortener}"; + $this->shortener = new $shortener($this->plugins->getPlugin('Http')); + + if (!$this->shortener instanceof Phergie_Plugin_Url_Shorten_Abstract) { + $this->fail("Declared shortener class {$shortener} is not of proper ancestry"); + } + + // load config (a bit ugly, but focusing on porting): + foreach ( + array( + 'detect_schemeless' => 'detectSchemeless', + 'base_format' => 'baseFormat', + 'message_format' => 'messageFormat', + 'merge_links' => 'mergeLinks', + 'title_length' => 'titleLength', + 'show_errors' => 'showErrors', + 'expire' => 'expire', + ) as $config => $local) { + if (isset($this->config["url.{$config}"])) { + $this->$local = $this->config["uri.{$config}"]; + } + } + } + + /** + * Checks an incoming message for the presence of a URL and, if one is + * found, responds with its title if it is an HTML document and the + * shortened equivalent of its original URL if it meets length requirements. + * + * @todo Update this to pull configuration settings from $this->config + * rather than caching them as class properties + * @return void + */ + public function onPrivmsg() + { + $this->handleMsg(); + } + + /** + * Checks an incoming message for the presence of a URL and, if one is + * found, responds with its title if it is an HTML document and the + * shortened equivalent of its original URL if it meets length requirements. + * + * @todo Update this to pull configuration settings from $this->config + * rather than caching them as class properties + * @return void + */ + public function onAction() + { + $this->handleMsg(); + } + + /** + * Handles message events and responds with url titles. + * + * @return void + */ + protected function handleMsg() + { + $source = $this->getEvent()->getSource(); + $user = $this->getEvent()->getNick(); + + $responses = array(); + $urls = $this->findUrls($this->getEvent()->getArgument(1)); + + foreach ($urls as $parsed) { + $url = $parsed['glued']; + + // allow out-of-class renderers to handle this URL + foreach ($this->renderers as $renderer) { + if ($renderer->renderUrl($parsed) === true) { + // renderers should return true if they've fully + // rendered the passed URL (they're responsible + // for their own output) + $this->debug('Handled by renderer: ' . get_class($renderer)); + continue 2; + } + } + + // Convert url + $shortenedUrl = $this->shortener->shorten($url); + if (!$shortenedUrl) { + $this->debug('Invalid Url: Unable to shorten. (' . $url . ')'); + $shortenedUrl = $url; + } + + // Prevent spamfest + if ($this->checkUrlCache($url, $shortenedUrl)) { + $this->debug('Invalid Url: URL is in the cache. (' . $url . ')'); + continue; + } + + $title = $this->getTitle($url); + if (!empty($title)) { + $responses[] = str_replace( + array( + '%title%', + '%link%', + '%nick%' + ), array( + $title, + $shortenedUrl, + $user + ), $this->messageFormat + ); + } + + // Update cache + $this->updateUrlCache($url, $shortenedUrl); + unset($title, $shortenedUrl, $title); + } + + // Check to see if there were any URL responses, format them and handle if they + // get merged into one message or not + if (count($responses) > 0) { + if ($this->mergeLinks) { + $message = str_replace( + array( + '%message%', + '%nick%' + ), array( + implode('; ', $responses), + $user + ), $this->baseFormat + ); + $this->doPrivmsg($source, $message); + } else { + foreach ($responses as $response) { + $message = str_replace( + array( + '%message%', + '%nick%' + ), array( + implode('; ', $responses), + $user + ), $this->baseFormat + ); + $this->doPrivmsg($source, $message); + } + } + } + } + + /** + * Detect URLs in a given string. + * + * @param string $message the string to detect urls in + * + * @return array the array of urls found + */ + public function findUrls($message) + { + $pattern = '#'.($this->detectSchemeless ? '' : 'https?://').'(?:([0-9]{1,3}(?:\.[0-9]{1,3}){3})(?![^/]) | (' + .($this->detectSchemeless ? '(?<!http:/|https:/)[@/\\\]' : '').')?(?:(?:[a-z0-9_-]+\.?)+\.[a-z0-9]{1,6}))[^\s]*#xis'; + $urls = array(); + + // URL Match + if (preg_match_all($pattern, $message, $matches, PREG_SET_ORDER)) { + foreach ($matches as $m) { + $url = trim(rtrim($m[0], ', ].?!;')); + + // Check to see if the URL was from an email address, is a directory, etc + if (!empty($m[2])) { + $this->debug('Invalid Url: URL is either an email or a directory path. (' . $url . ')'); + continue; + } + + // Parse the given URL + if (!$parsed = $this->parseUrl($url)) { + $this->debug('Invalid Url: Could not parse the URL. (' . $url . ')'); + continue; + } + + // Check to see if the given IP/Host is valid + if (!empty($m[1]) and !$this->checkValidIP($m[1])) { + $this->debug('Invalid Url: ' . $m[1] . ' is not a valid IP address. (' . $url . ')'); + continue; + } + + // Process TLD if it's not an IP + if (empty($m[1])) { + // Get the TLD from the host + $pos = strrpos($parsed['host'], '.'); + $parsed['tld'] = ($pos !== false ? substr($parsed['host'], ($pos+1)) : ''); + + // Check to see if the URL has a valid TLD + if ($this->plugins->tld->getTld($parsed['tld']) === false) { + $this->debug('Invalid Url: ' . $parsed['tld'] . ' is not a supported TLD. (' . $url . ')'); + continue; + } + } + + // Check to see if the URL is to a secured site or not and handle it accordingly + if ($parsed['scheme'] == 'https' && !extension_loaded('openssl')) { + if (!$this->sslFallback) { + $this->debug('Invalid Url: HTTPS is an invalid scheme, OpenSSL isn\'t available. (' . $url . ')'); + continue; + } else { + $parsed['scheme'] = 'http'; + } + } + + if (!in_array($parsed['scheme'], array('http', 'https'))) { + $this->debug('Invalid Url: ' . $parsed['scheme'] . ' is not a supported scheme. (' . $url . ')'); + continue; + } + + $urls[] = $parsed + array('glued' => $this->glueURL($parsed)); + } + } + + return $urls; + } + + /** + * Checks a given URL (+shortened) against the cache to verify if they were + * previously posted on the channel. + * + * @param string $url The URL to check against + * @param string $shortenedUrl The shortened URL to check against + * + * @return bool + */ + protected function checkUrlCache($url, $shortenedUrl) + { + $source = $this->getEvent()->getSource(); + + /** + * Transform the URL (+shortened) into a HEX CRC32 checksum to prevent potential problems + * and minimize the size of the cache for less cache bloat. + */ + $url = $this->getUrlChecksum($url); + $shortenedUrl = $this->getUrlChecksum($shortenedUrl); + + $cache = array( + 'url' => isset($this->urlCache[$source][$url]) ? $this->urlCache[$source][$url] : null, + 'shortened' => isset($this->shortCache[$source][$shortenedUrl]) ? $this->shortCache[$source][$shortenedUrl] : null + ); + + $expire = $this->expire; + $this->debug("Cache expire: {$expire}"); + /** + * If cache expiration is enabled, check to see if the given url has expired in the cache + * If expire is disabled, simply check to see if the url is listed + */ + if (($expire > 0 && (($cache['url'] + $expire) > time() || ($cache['shortened'] + $expire) > time())) + || ($expire <= 0 && (isset($cache['url']) || isset($cache['shortened']))) + ) { + unset($cache, $url, $shortenedUrl, $expire); + return true; + } + unset($cache, $url, $shortenedUrl, $expire); + return false; + } + + /** + * Updates the cache and adds the given URL (+shortened) to the cache. It + * also handles cleaning the cache of old entries as well. + * + * @param string $url The URL to add to the cache + * @param string $shortenedUrl The shortened to add to the cache + * + * @return bool + */ + protected function updateUrlCache($url, $shortenedUrl) + { + $source = $this->getEvent()->getSource(); + + /** + * Transform the URL (+shortened) into a HEX CRC32 checksum to prevent potential problems + * and minimize the size of the cache for less cache bloat. + */ + $url = $this->getUrlChecksum($url); + $shortenedUrl = $this->getUrlChecksum($shortenedUrl); + $time = time(); + + // Handle the URL cache and remove old entries that surpass the limit if enabled + $this->urlCache[$source][$url] = $time; + if ($this->limit > 0 && count($this->urlCache[$source]) > $this->limit) { + asort($this->urlCache[$source], SORT_NUMERIC); + array_shift($this->urlCache[$source]); + } + + // Handle the shortened cache and remove old entries that surpass the limit if enabled + $this->shortCache[$source][$shortenedUrl] = $time; + if ($this->limit > 0 && count($this->shortCache[$source]) > $this->limit) { + asort($this->shortCache[$source], SORT_NUMERIC); + array_shift($this->shortCache[$source]); + } + unset($url, $shortenedUrl, $time); + } + + /** + * Transliterates a UTF-8 string into corresponding ASCII characters and + * truncates and appends an ellipsis to the string if it exceeds a given + * length. + * + * @param string $str String to decode + * @param int $trim Maximum string length, optional + * + * @return string + */ + protected function decode($str, $trim = null) + { + $out = $this->plugins->encoding->transliterate($str); + if ($trim > 0) { + $out = substr($out, 0, $trim) . (strlen($out) > $trim ? '...' : ''); + } + return $out; + } + + /** + * Takes a url, parses and cleans the URL without of all the junk + * and then return the hex checksum of the url. + * + * @param string $url url to checksum + * + * @return string the hex checksum of the cleaned url + */ + protected function getUrlChecksum($url) + { + $checksum = strtolower(urldecode($this->glueUrl($url, true))); + $checksum = preg_replace('#\s#', '', $this->plugins->encoding->transliterate($checksum)); + return dechex(crc32($checksum)); + } + + /** + * Parses a given URI and procceses the output to remove redundant + * or missing values. + * + * @param string $url the url to parse + * + * @return array the url components + */ + protected function parseUrl($url) + { + if (is_array($url)) return $url; + + $url = trim(ltrim($url, ' /@\\')); + if (!preg_match('&^(?:([a-z][-+.a-z0-9]*):)&xis', $url, $matches)) { + $url = 'http://' . $url; + } + $parsed = parse_url($url); + + if (!isset($parsed['scheme'])) { + $parsed['scheme'] = 'http'; + } + $parsed['scheme'] = strtolower($parsed['scheme']); + + if (isset($parsed['path']) && !isset($parsed['host'])) { + $host = $parsed['path']; + $path = ''; + if (strpos($parsed['path'], '/') !== false) { + list($host, $path) = array_pad(explode('/', $parsed['path'], 2), 2, null); + } + $parsed['host'] = $host; + $parsed['path'] = $path; + } + + return $parsed; + } + + /** + * Parses a given URI and then glues it back together in the proper format. + * If base is set, then it chops off the scheme, user and pass and fragment + * information to return a more unique base URI. + * + * @param string $uri uri to rebuild + * @param string $base set to true to only return the base components + * + * @return string the rebuilt uri + */ + protected function glueUrl($uri, $base = false) + { + $parsed = $uri; + if (!is_array($parsed)) { + $parsed = $this->parseUrl($parsed); + } + + if (is_array($parsed)) { + $uri = ''; + if (!$base) { + $uri .= (!empty($parsed['scheme']) ? $parsed['scheme'] . ':' . + ((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : ''); + $uri .= (!empty($parsed['user']) ? $parsed['user'] . + (!empty($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@' : ''); + } + if ($base && !empty($parsed['host'])) { + $parsed['host'] = trim($parsed['host']); + if (substr($parsed['host'], 0, 4) == 'www.') { + $parsed['host'] = substr($parsed['host'], 4); + } + } + $uri .= (!empty($parsed['host']) ? $parsed['host'] : ''); + if (!empty($parsed['port']) + && (($parsed['scheme'] == 'http' && $parsed['port'] == 80) + || ($parsed['scheme'] == 'https' && $parsed['port'] == 443)) + ) { + unset($parsed['port']); + } + $uri .= (!empty($parsed['port']) ? ':' . $parsed['port'] : ''); + if (!empty($parsed['path']) && (!$base || $base && $parsed['path'] != '/')) { + $uri .= (substr($parsed['path'], 0, 1) == '/') ? $parsed['path'] : ('/' . $parsed['path']); + } + $uri .= (!empty($parsed['query']) ? '?' . $parsed['query'] : ''); + if (!$base) { + $uri .= (!empty($parsed['fragment']) ? '#' . $parsed['fragment'] : ''); + } + } + return $uri; + } + + /** + * Checks the given string to see if its a valid IP4 address + * + * @param string $ip the ip to validate + * + * @return bool + */ + protected function checkValidIP($ip) + { + return long2ip(ip2long($ip)) === $ip; + } + + /** + * Returns the title of the given page + * + * @param string $url url to the page + * + * @return string title + */ + public function getTitle($url) + { + $http = $this->plugins->getPlugin('Http'); + $options = array( + 'timeout' => 3.5, + 'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12' + ); + + $response = $http->head($url, array(), $options); + $header = $response->getHeaders('Content-Type'); + + if (!preg_match('#^(text/x?html|application/xhtml+xml)(?:;.*)?$#', $header)) { + $title = $header; + } else { + $response = $http->get($url, array(), $options); + $content = $response->getContent(); + if (preg_match('#<title[^>]*>(.*?)</title>#is', $content, $match)) { + $title = preg_replace('/[\s\v]+/', ' ', trim($match[1])); + } + } + $encoding = $this->plugins->getPlugin('Encoding'); + $title = $encoding->decodeEntities($title); + + if (empty($title)) { + if ($response->isError()) { + $title = $response->getCodeAsString(); + } else { + $title = 'No Title'; + } + } + + return $title; + } + + /** + * Output a debug message + * + * @param string $msg the message to output + * + * @return void + */ + protected function debug($msg) + { + echo "(DEBUG:Url) $msg\n"; + } + + /** + * Add a renderer to the stack + * + * @param object $obj the renderer to add + * + * @return void + */ + public function registerRenderer($obj) + { + $this->renderers[spl_object_hash($obj)] = $obj; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Abstract.php new file mode 100644 index 000000000..607d1654c --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Abstract.php @@ -0,0 +1,105 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @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_Php + */ + +/** + * URL shortener abstract class + * + * @category Phergie + * @package Phergie_Plugin_Url + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Url + * @uses Phergie_Plugin_Http pear.phergie.org + */ +abstract class Phergie_Plugin_Url_Shorten_Abstract +{ + protected $http; + + /** + * Constructor + * + * @param Phergie_Plugin_Http $http instance of the http plugin + */ + public function __construct(Phergie_Plugin_Http $http) + { + $this->http = $http; + } + + /** + * Returns an array of request parameters given a url to shorten. The + * following keys are valid request parameters: + * + * * 'uri': the URI for the request (required) + * * 'query': an array of key-value pairs sent in a GET request + * * 'post': an array of key-value pairs sent in a POST request + * * 'callback': to be called after the request is finished. Should accept + * a Phergie_Plugin_Http_Response object and return either the shortened + * url or false if an error has occured. + * + * If the 'post' key is present a POST request shall be made; otherwise + * a GET request will be made. The 'post' key can be an empty array and + * a post request will still be made. + * + * If no callback is provided the contents of the response will be returned. + * + * @param string $url the url to shorten + * + * @return array the request parameters + */ + protected abstract function getRequestParams($url); + + /** + * Shortens a given url. + * + * @param string $url the url to shorten + * + * @return string the shortened url or false on a failure + */ + public function shorten($url) + { + $defaults = array('get' => array(), 'post' => array(), 'callback' => null); + $options = array('timeout' => 2); + $params = $this->getRequestParams($url) + $defaults; + + // Should some kind of notice be thrown? Maybe just if getRequestParams does not return an array? + if (!is_array($params) || empty($params['uri'])) { + return $url; + } + + if (!empty($params['post'])) { + $response = $this->http->post($params['uri'], $params['get'], $params['post'], $options); + } else { + $response = $this->http->get($params['uri'], $params['get'], $options); + } + + if (is_callable($params['callback'])) { + return call_user_func($params['callback'], $response); + } + + $code = $response->getCode(); + $content = trim($response->getContent); + if ($code < 200 || $code >= 300 || empty($content)) { + return false; + } + + return $response->getContent(); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Trim.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Trim.php new file mode 100644 index 000000000..af7e8a514 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Url/Shorten/Trim.php @@ -0,0 +1,64 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Php + * @author Phergie Development Team <team@phergie.org> + * @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_Php + */ + +/** + * Shortens urls via the tr.im service + * + * @category Phergie + * @package Phergie_Plugin_Url + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Url + */ +class Phergie_Plugin_Url_Shorten_Trim extends Phergie_Plugin_Url_Shorten_Abstract +{ + /** + * Returns an array of request parameters given a url to shorten. The + * following keys are valid request parameters: + * + * @param string $url the url to shorten + * + * @return array the request parameters + */ + protected function getRequestParams($url) + { + return array( + 'uri' => 'http://api.tr.im/v1/trim_simple?url=' . rawurlencode($url), + 'callback' => array($this, 'onComplete') + ); + } + + /** + * Callback for when the URL has been shortened. Checks for error messages. + * + * @param Phergie_Plugin_Http_Response $response the response object + * + * @return string|bool the shortened url or false on failure + */ + protected function onComplete($response) + { + if (strpos($response->getContent(), 'Error: ') === 0) { + return false; + } + + return $response->getContent(); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/UserInfo.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/UserInfo.php new file mode 100644 index 000000000..9437073d8 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/UserInfo.php @@ -0,0 +1,413 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_UserInfo + * @author Phergie Development Team <team@phergie.org> + * @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 <team@phergie.org> + * @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; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Weather.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Weather.php new file mode 100644 index 000000000..7d4f85a19 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Weather.php @@ -0,0 +1,149 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Weather + * @author Phergie Development Team <team@phergie.org> + * @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_Weather + */ + +/** + * Detects and responds to requests for current weather conditions in a + * particular location using data from a web service. Requires registering + * with weather.com to obtain authentication credentials, which must be + * stored in the configuration settings weather.partner_id and + * weather.license_key for the plugin to function. + * + * @category Phergie + * @package Phergie_Plugin_Weather + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Weather + * @link http://www.weather.com/services/xmloap.html + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + * @uses Phergie_Plugin_Temperature pear.phergie.org + * @uses extension SimpleXML + */ +class Phergie_Plugin_Weather extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $plugins->getPlugin('Http'); + $plugins->getPlugin('Temperature'); + + if (empty($this->config['weather.partner_id']) + || empty($this->config['weather.license_key'])) { + $this->fail('weather.partner_id and weather.license_key must be specified'); + } + } + + /** + * Returns a weather report for a specified location. + * + * @param string $location Zip code or city/state/country specification + * + * @return void + */ + public function onCommandWeather($location) + { + $response = $this->plugins->http->get( + 'http://xoap.weather.com/search/search', + array('where' => $location) + ); + + if ($response->isError()) { + $this->doNotice( + $this->event->getNick(), + 'ERROR: ' . $response->getCode() . ' ' . $response->getMessage() + ); + return; + } + + $nick = $this->event->getNick(); + + $xml = $response->getContent(); + if (count($xml->loc) == 0) { + $this->doNotice($nick, 'No results for that location.'); + return; + } + + $where = (string) $xml->loc[0]['id']; + $response = $this->plugins->http->get( + 'http://xoap.weather.com/weather/local/' . $where, + array( + 'cc' => '*', + 'link' => 'xoap', + 'prod' => 'xoap', + 'par' => $this->config['weather.partner_id'], + 'key' => $this->config['weather.license_key'], + ) + ); + + if ($response->isError()) { + $this->doNotice( + $this->event->getNick(), + 'ERROR: ' . $response->getCode() . ' ' . $response->getMessage() + ); + return; + } + + $temperature = $this->plugins->getPlugin('Temperature'); + $xml = $response->getContent(); + $weather = 'Weather for ' . (string) $xml->loc->dnam . ' - '; + switch ($xml->head->ut) { + case 'F': + $tempF = $xml->cc->tmp; + $tempC = $temperature->convertFahrenheitToCelsius($tempF); + break; + case 'C': + $tempC = $xml->cc->tmp; + $tempF = $temperature->convertCelsiusToFahrenheit($tempC); + break; + default: + $this->doNotice( + $this->event->getNick(), + 'ERROR: No scale information given.'); + break; + } + $r = $xml->cc->hmid; + $hiF = $temperature->getHeatIndex($tempF, $r); + $hiC = $temperature->convertFahrenheitToCelsius($hiF); + $weather .= 'Temperature: ' . $tempF . 'F/' . $tempC . 'C'; + $weather .= ', Humidity: ' . (string) $xml->cc->hmid . '%'; + if ($hiF > $tempF || $hiC > $tempC) { + $weather .= ', Heat Index: ' . $hiF . 'F/' . $hiC . 'C'; + } + $weather .= + ', Conditions: ' . (string) $xml->cc->t . + ', Updated: ' . (string) $xml->cc->lsup . + ' [ http://weather.com/weather/today/' . + str_replace( + array('(', ')', ',', ' '), + array('', '', '', '+'), + (string) $xml->loc->dnam + ) . + ' ]'; + + $this->doPrivmsg($this->event->getSource(), $nick . ': ' . $weather); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine.php new file mode 100644 index 000000000..9aa0845a6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine.php @@ -0,0 +1,69 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Wine + * @author Phergie Development Team <team@phergie.org> + * @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_Wine + */ + +/** + * Processes requests to serve users wine. + * + * @category Phergie + * @package Phergie_Plugin_Wine + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Wine + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Serve pear.phergie.org + */ +class Phergie_Plugin_Wine extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->plugins; + $plugins->getPlugin('Command'); + $plugins->getPlugin('Serve'); + } + + /** + * Processes requests to serve a user a wine. + * + * @param string $request Request including the target and an optional + * suggestion of what wine to serve + * + * @return void + */ + public function onCommandWine($request) + { + $format = $this->getConfig( + 'wine.format', + 'serves %target% a glass of %item%.' + ); + + $this->plugins->getPlugin('Serve')->serve( + dirname(__FILE__) . '/Wine/wine.db', + 'wine', + $format, + $request + ); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine/db.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine/db.php new file mode 100644 index 000000000..ce01c2d98 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Wine/db.php @@ -0,0 +1,53 @@ +<?php + +// Create database schema +echo 'Creating database', PHP_EOL; +$file = __DIR__ . '/wine.db'; +if (file_exists($file)) { + unlink($file); +} +$db = new PDO('sqlite:' . $file); +$db->exec('CREATE TABLE wine (name VARCHAR(255), link VARCHAR(255))'); +$db->exec('CREATE UNIQUE INDEX wine_name ON wine (name)'); +$insert = $db->prepare('INSERT INTO wine (name, link) VALUES (:name, :link)'); + +// Get and decompress lcboapi.com data set +$outer = __DIR__ . '/current.zip'; +if (!file_exists($outer)) { + echo 'Downloading lcboapi.com data set', PHP_EOL; + copy('http://lcboapi.com/download/current.zip', $outer); +} + +echo 'Decompressing lcboapi.com data set', PHP_EOL; +$zip = new ZipArchive; +$zip->open($outer); +$stat = $zip->statIndex(0); +$inner = __DIR__ . '/' . $stat['name']; +$zip->extractTo(__DIR__); +$zip->close(); +$zip = new ZipArchive; +$zip->open($inner); +$stat = $zip->statIndex(0); +$file = __DIR__ . '/' . $stat['name']; +$zip->extractTo(__DIR__); +$zip->close(); + +// Aggregate data set into the database +$lcbo = new PDO('sqlite:' . $file); +$result = $lcbo->query('SELECT product_no, name FROM products WHERE primary_category = "Wine"'); +$wines = $result->fetchAll(); +echo 'Processing lcboapi.com data - ', number_format(count($wines), 0), ' records', PHP_EOL; +$db->beginTransaction(); +foreach ($wines as $wine) { + $name = $wine['name']; + $link = 'http://lcboapi.com/products/' . $wine['product_no']; + $insert->execute(array($name, $link)); +} +$db->commit(); + +// Clean up +echo 'Cleaning up', PHP_EOL; +unset($lcbo); +unlink($outer); +unlink($inner); +unlink($file); diff --git a/plugins/Irc/extlib/phergie/Phergie/Plugin/Youtube.php b/plugins/Irc/extlib/phergie/Phergie/Plugin/Youtube.php new file mode 100644 index 000000000..e06ea5640 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Plugin/Youtube.php @@ -0,0 +1,168 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie_Plugin_Youtube + * @author Phergie Development Team <team@phergie.org> + * @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_Youtube + */ + +/** + * Provides commands used to access several services offered by Google + * including search, translation, weather, maps, and currency and general + * value unit conversion. + * + * @category Phergie + * @package Phergie_Plugin_Youtube + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie_Plugin_Youtube + * @uses Phergie_Plugin_Command pear.phergie.org + * @uses Phergie_Plugin_Http pear.phergie.org + */ +class Phergie_Plugin_Youtube extends Phergie_Plugin_Abstract +{ + /** + * Checks for dependencies. + * + * @return void + */ + public function onLoad() + { + $plugins = $this->getPluginHandler(); + $plugins->getPlugin('Command'); + $plugins->getPlugin('Http'); + if ($url = $plugins->getPlugin('Url')) { + $url->registerRenderer($this); + } + } + + /** + * Queries the YouTube video search web service, processes the first + * result, and sends a message back to the current event source. + * + * @param string $query Search term + * + * @return object YouTube result object + */ + protected function queryYoutube($query) + { + $url = 'http://gdata.youtube.com/feeds/api/videos'; + $params = array( + 'max-results' => '1', + 'alt' => 'json', + 'q' => $query + ); + $http = $this->plugins->getPlugin('Http'); + $response = $http->get($url, $params); + $json = $response->getContent(); + + $entries = $json->feed->entry; + if (!$entries) { + $this->doNotice($this->event->getNick(), 'Query returned no results'); + return; + } + $entry = reset($entries); + + $nick = $this->event->getNick(); + $link = $entry->link[0]->href; + $title = $entry->title->{'$t'}; + $author = $entry->author[0]->name->{'$t'}; + $seconds = $entry->{'media$group'}->{'yt$duration'}->seconds; + $published = $entry->published->{'$t'}; + $views = $entry->{'yt$statistics'}->viewCount; + $rating = $entry->{'gd$rating'}->average; + + $minutes = floor($seconds / 60); + $seconds = str_pad($seconds % 60, 2, '0', STR_PAD_LEFT); + $parsed_link = parse_url($link); + parse_str($parsed_link['query'], $parsed_query); + $link = 'http://youtu.be/' . $parsed_query['v']; + $published = date('n/j/y g:i A', strtotime($published)); + $views = number_format($views, 0); + $rating = round($rating, 2); + + $format = $this->getConfig('youtube.format'); + if (!$format) { + $format = '%nick%:' + . ' [ %link% ]' + . ' "%title%" by %author%,' + . ' Length %minutes%:%seconds%,' + . ' Published %published%,' + . ' Views %views%,' + . ' Rating %rating%'; + } + + $replacements = array( + 'nick' => $nick, + 'link' => $link, + 'title' => $title, + 'author' => $author, + 'minutes' => $minutes, + 'seconds' => $seconds, + 'published' => $published, + 'views' => $views, + 'rating' => $rating + ); + + $msg = $format; + foreach ($replacements as $from => $to) { + $msg = str_replace('%' . $from . '%', $to, $msg); + } + $this->doPrivmsg($this->event->getSource(), $msg); + } + + /** + * Returns the first result of a YouTube search. + * + * @param string $query Search query + * + * @return void + */ + public function onCommandYoutube($query) + { + $this->queryYoutube($query); + } + + /** + * Renders YouTube URLs. + * + * @param array $parsed parse_url() output for the URL to render + * + * @return boolean TRUE if the URL was rendered successfully, FALSE + * otherwise + */ + public function renderUrl(array $parsed) + { + switch ($parsed['host']) { + case 'youtu.be': + $v = ltrim($parsed['path'], '/'); + break; + case 'youtube.com': + case 'www.youtube.com': + parse_str($parsed['query'], $parsed_query); + if (!empty($parsed_query['v'])) { + $v = $parsed_query['v']; + break; + } + default: + return false; + } + + $this->queryYoutube($v); + + return true; + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Process/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Process/Abstract.php new file mode 100755 index 000000000..0ec3569fe --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Process/Abstract.php @@ -0,0 +1,130 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for obtaining and processing incoming events. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +abstract class Phergie_Process_Abstract +{ + /** + * Current driver instance + * + * @var Phergie_Driver_Abstract + */ + protected $driver; + + /** + * Current connection handler instance + * + * @var Phergie_Connection_Handler + */ + protected $connections; + + /** + * Current plugin handler instance + * + * @var Phergie_Plugin_Handler + */ + protected $plugins; + + /** + * Current event handler instance + * + * @var Phergie_Event_Handler + */ + protected $events; + + /** + * Current end-user interface instance + * + * @var Phergie_Ui_Abstract + */ + protected $ui; + + /** + * List of arguments for use within the instance + * + * @var array + */ + protected $options = array(); + + /** + * Gets the required class refences from Phergie_Bot. + * + * @param Phergie_Bot $bot Current bot instance in use + * @param array $options Optional processor arguments + * + * @return void + */ + public function __construct(Phergie_Bot $bot, array $options = array()) + { + $this->driver = $bot->getDriver(); + $this->plugins = $bot->getPluginHandler(); + $this->connections = $bot->getConnectionHandler(); + $this->events = $bot->getEventHandler(); + $this->ui = $bot->getUi(); + $this->options = $options; + } + + /** + * Sends resulting outgoing events from ealier processing in handleEvents. + * + * @param Phergie_Connection $connection Active connection + * + * @return void + */ + protected function processEvents(Phergie_Connection $connection) + { + $this->plugins->preDispatch(); + if (count($this->events)) { + foreach ($this->events as $event) { + $this->ui->onCommand($event, $connection); + + $method = 'do' . ucfirst(strtolower($event->getType())); + call_user_func_array( + array($this->driver, $method), + $event->getArguments() + ); + } + } + $this->plugins->postDispatch(); + + if ($this->events->hasEventOfType(Phergie_Event_Request::TYPE_QUIT)) { + $this->ui->onQuit($connection); + $this->connections->removeConnection($connection); + } + + $this->events->clearEvents(); + } + + /** + * Obtains and processes incoming events. + * + * @return void + */ + public abstract function handleEvents(); +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Process/Async.php b/plugins/Irc/extlib/phergie/Phergie/Process/Async.php new file mode 100644 index 000000000..78d595914 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Process/Async.php @@ -0,0 +1,157 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Connection data processor which polls to handle input in an + * asynchronous manner. Will also cause the application tick at + * the user-defined wait time. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Process_Async extends Phergie_Process_Abstract +{ + /** + * Length of time to poll for stream activity (seconds) + * + * @var int + */ + protected $sec = 0; + + /** + * Length of time to poll for stream activity (microseconds) + * + * @var int + */ + protected $usec = 200000; + + /** + * Length of time to wait between ticks. + * + * @var int + */ + protected $wait = 0; + + /** + * Records when the application last performed a tick + * + * @var int + */ + protected $lastTick = 0; + + /** + * Overrides the parent class to set the poll time. + * + * @param Phergie_Bot $bot Main bot class + * @param array $options Processor arguments + * + * @return void + */ + public function __construct(Phergie_Bot $bot, array $options) + { + if (!$bot->getDriver() instanceof Phergie_Driver_Streams) { + throw new Phergie_Process_Exception( + 'The Async event processor requires the Streams driver' + ); + } + + foreach (array('sec', 'usec') as $var) { + if (isset($options[$var])) { + if (!is_int($options[$var])) { + throw new Phergie_Process_Exception( + 'Processor option "' . $var . '" must be an integer' + ); + } + $this->$var = $options[$var]; + } + } + + if (!isset($this->sec) && !isset($this->usec)) { + throw new Phergie_Process_Exception( + 'One of the processor options "sec" or "usec" must be specified' + ); + } + + parent::__construct($bot, $options); + } + + /** + * Waits for stream activity and performs event processing on + * connections with data to read. + * + * @return void + */ + protected function handleEventsAsync() + { + $hostmasks = $this->driver->getActiveReadSockets($this->sec, $this->usec); + if (!$hostmasks) { + return; + } + $connections = $this->connections->getConnections($hostmasks); + foreach ($connections as $connection) { + $this->driver->setConnection($connection); + $this->plugins->setConnection($connection); + $this->plugins->onTick(); + + if ($event = $this->driver->getEvent()) { + $this->ui->onEvent($event, $connection); + $this->plugins->setEvent($event); + $this->plugins->preEvent(); + $this->plugins->{'on' . ucfirst($event->getType())}(); + } + + $this->processEvents($connection); + } + } + + /** + * Perform application tick event on all plugins and connections. + * + * @return void + */ + protected function doTick() + { + foreach ($this->connections as $connection) { + $this->plugins->setConnection($connection); + $this->plugins->onTick(); + $this->processEvents($connection); + } + } + + /** + * Obtains and processes incoming events, then sends resulting outgoing + * events. + * + * @return void + */ + public function handleEvents() + { + $time = time(); + if ($this->lastTick == 0 || ($this->lastTick + $this->wait <= $time)) { + $this->doTick(); + $this->lastTick = $time; + } + $this->handleEventsAsync(); + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Process/Exception.php b/plugins/Irc/extlib/phergie/Phergie/Process/Exception.php new file mode 100755 index 000000000..f964443c6 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Process/Exception.php @@ -0,0 +1,33 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Exception related to event processor operations. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Process_Exception extends Phergie_Exception +{ +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Process/Standard.php b/plugins/Irc/extlib/phergie/Phergie/Process/Standard.php new file mode 100644 index 000000000..24adbaf99 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Process/Standard.php @@ -0,0 +1,57 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Connection data processor which reads all connections looking + * for a response. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Process_Standard extends Phergie_Process_Abstract +{ + /** + * Obtains and processes incoming events, then sends resulting outgoing + * events. + * + * @return void + */ + public function handleEvents() + { + foreach ($this->connections as $connection) { + $this->driver->setConnection($connection); + $this->plugins->setConnection($connection); + $this->plugins->onTick(); + + if ($event = $this->driver->getEvent()) { + $this->ui->onEvent($event, $connection); + $this->plugins->setEvent($event); + $this->plugins->preEvent(); + $this->plugins->{'on' . ucfirst($event->getType())}(); + } + + $this->processEvents($connection); + } + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php b/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php new file mode 100644 index 000000000..9c1b3a6f1 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/StatusnetBot.php @@ -0,0 +1,98 @@ +<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Extends the bot class (Phergie_Bot) to allow connection and access to
+ * sockets and to allow StatusNet to 'drive' the bot
+ *
+ * @category Phergie
+ * @package Phergie_StatusnetBot
+ * @author Luke Fitzgerald <lw.fitzgerald@googlemail.com>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+class Phergie_StatusnetBot extends Phergie_Bot {
+ /**
+ * Set up bot and connect to servers
+ *
+ * @return void
+ */
+ public function connect() {
+ $ui = $this->getUi();
+ $ui->setEnabled($this->getConfig('ui.enabled'));
+
+ $this->loadPlugins();
+ $this->loadConnections();
+ }
+
+ /**
+ * Transmit raw command to server using driver
+ *
+ * Handles construction of command strings and their transmission to the
+ * server.
+ *
+ * @param string $command Command to send
+ * @param string|array $args Optional string or array of sequential
+ * arguments
+ *
+ * @return string Command string that was sent
+ * @throws Phergie_Driver_Exception
+ */
+ public function send($command, $args = '') {
+ return $this->getDriver()->send($command, $args);
+ }
+
+ /**
+ * Handle incoming data on the socket using the handleEvents
+ * method of the Processor
+ *
+ * @return void
+ */
+ public function handleEvents() {
+ $this->getProcessor()->handleEvents();
+ }
+
+ /**
+ * Close the current connection and reconnect to the server
+ *
+ * @return void
+ */
+ public function reconnect() {
+ $driver = $this->getDriver();
+ $sockets = $driver->getSockets();
+
+ // Close any existing connections
+ try {
+ $driver->forceQuit();
+ } catch (Phergie_Driver_Exception $e){}
+ try {
+ $driver->doConnect();
+ } catch (Phergie_Driver_Exception $e){
+ $driver->forceQuit();
+ throw $e;
+ }
+ }
+
+ /**
+ * Get the sockets used by the bot
+ *
+ * @return array Array of socket resources
+ */
+ public function getSockets() {
+ return $this->getDriver()->getSockets();
+ }
+}
\ No newline at end of file diff --git a/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/INSTALL b/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/INSTALL new file mode 100755 index 000000000..dfc985756 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/INSTALL @@ -0,0 +1,24 @@ +----------------------------------------------------------------------------- +-- About LogViewer +----------------------------------------------------------------------------- + +The purpose of this tool is to make an HTML display of the Logs made by the +Logging plugin for Phergie stored in SQLite database. + +----------------------------------------------------------------------------- +-- Installation +----------------------------------------------------------------------------- + +To install this, simply copy the contents of this directory into whatever +directory you want this to run under. + +----------------------------------------------------------------------------- +-- Configuration +----------------------------------------------------------------------------- + +The only configuration needed at this point in time is to edit the $database +variable in the top of the index.php to point to the appropriate Phergie +log file. + +Stuff may get more complicated in the future ;) But this is meant to be +simple for now.
\ No newline at end of file diff --git a/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/index.php b/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/index.php new file mode 100755 index 000000000..fc27fa294 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Tools/LogViewer/index.php @@ -0,0 +1,368 @@ +<?php +// Phergie Log Viewer ... Currently designed as a single PHP file in order to make it easy to +// 'install' this. Just drop the index.php (or whatever name you wish to rename it to) wherever +// you wish, and it will simply work. Sure, it would be nice to structure some of this stuff into +// various include files/etc. But right now this is simple enough of a quick log viewer, that it's +// just one file. + + +/********** SETUP **********/ + +// (Change any of these if/as needed for your setup) +ini_set('default_charset', 'UTF-8'); +date_default_timezone_set('UTC'); +$log = "/PATH/AND/FILENAME/TO/YOUR/LOGFILE/PLEASE.db"; + + +/********** PREPARATION **********/ + +$db = new PDO('sqlite:' . $log); +if (!is_object($db)) { + // Failure, can't access Phergie Log. Bail with an error message, not pretty, but works: + echo "ERROR: Cannot access Phergie Log File, please check the configuration & access privileges"; + exit(); +} + + +/********** DETECTION **********/ + +// Determine the mode of the application and call the appropriate handler function +$mode = empty($_GET['m']) ? '' : $_GET['m']; +switch ($mode) { + case 'channel': + show_days($db); + break; + case 'day': + show_log($db); + break; + default: + show_channels($db); +} + +// Exit not really needed here, but reminds us that everything below is support functions: +exit(); + + +/********** MODES **********/ + +/** + * show_channels + * + * Provide a list of all channel's that we are logging information for: + * + * @param PDO A PDO object referring to the database + * @return void + * @author Eli White <eli@eliw.com> + **/ +function show_channels(PDO $db) { + // Begin the HTML page: + template_header('Channels'); + echo "\nChannels:\n<ul>\n"; + + // Loop through the database reading in each channel, and echoing out a <li> for it. + // only grab actual channels that start with # ... also pre-lowercase everything. + // this allows us to 'deal' with variable caps in how the channels were logged. + $channels = $db->query(" + select distinct lower(chan) as c + from logs + where chan like '#%' + "); + foreach ($channels as $row) { + $html = utf8specialchars($row['c']); + $url = urlencode($row['c']); + echo "<li><a href=\"?m=channel&w={$url}\">{$html}</a></li>\n"; + } + + // Finish off the page: + echo "\n</ul>\n"; + template_footer(); +} + +/** + * show_days + * + * Create a calendar view of all days available for this particular channel + * + * NOTE: May get unwieldy if large log files. Perhaps consider in the future + * making a paginated version of this? by year? Or a separate 'which year' page + * before this? Not to worry about now. + * + * @param PDO A PDO object referring to the database + * @return void + * @author Eli White <eli@eliw.com> + **/ +function show_days(PDO $db) { + $channel = $_GET['w']; + $url = urlencode($channel); + + // Begin the HTML page: + template_header('Daily Logs for Channel: ' . utf8specialchars($channel)); + echo "\n<ul>\n"; + + // Query the database to discover all days that are available for this channel: + $data = array(); + $prepared = $db->prepare(" + select distinct date(tstamp) as day + from logs + where lower(chan) = :chan + "); + $prepared->execute(array(':chan' => $channel)); + foreach ($prepared as $row) { + list($y, $m, $d) = explode('-', $row['day']); + $data[$y][$m][$d] = "{$y}-{$m}-{$d}"; + } + + // For now, just loop over them all and provide a list: + ksort($data); + foreach ($data as $year => $months) { + ksort($months); + foreach ($months as $month => $days) { + // Figure out a few facts about this month: + $stamp = mktime(0, 0, 0, $month, 1, $year); + $first_weekday = idate('w', $stamp); + $days_in_month = idate('t', $stamp); + $name = date('F', $stamp); + + // We have a month ... start a new table: + echo <<<EOTABLE +<div class="month"> + <table> + <caption>{$name} {$year}</caption> + <tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th></tr> +EOTABLE; + // Now we need to start looping through the days in this month: + echo '<tr>'; + $rowmod = 0; + // Loop through all day entries, no matter how many blanks we need: + for ($d = (-$first_weekday + 1); $d < $days_in_month + 1; $d++) { + if (!($rowmod++ % 7)) { + // Stop/start a new row: + echo '</tr><tr>'; + } + echo '<td>'; + // If this day is pre or post actual month days, make it blank: + if (($d < 1) || ($d > $days_in_month)) { + echo ' '; + } elseif (isset($days[$d])) { + // Make a link to the day's log: + echo "<a href=\"?m=day&w={$url}&d={$days[$d]}\">{$d}</a>"; + } else { + // Just a dead number: + echo $d; + } + echo '</td>'; + } + // Finish off any blanks needed for a complete table row: + while ($rowmod++ % 7) { + echo '<td> </td>'; + } + echo "</tr></table></div>\n"; + } + } + + // Finish off the page: + echo "\n</ul>\n"; + template_footer(); +} + +/** + * show_log + * + * Actually show the log for this specific day + * + * @param PDO A PDO object referring to the database + * @return void + * @author Eli White <eli@eliw.com> + **/ +function show_log(PDO $db) { + $channel = $_GET['w']; + $day = $_GET['d']; + $parts = explode('-', $day); + $formatted_date = "{$parts[0]}-{$parts[1]}-{$parts[2]}"; + + // Begin the HTML page: + template_header('Date: ' . utf8specialchars($formatted_date) . + ' - Channel: ' . utf8specialchars($channel)); + + // Query the database to get all log lines for this date: + $prepared = $db->prepare(" + select time(tstamp) as t, type, nick, message + from logs + where lower(chan) = :chan and date(tstamp) = :day + order by tstamp asc + "); + $prepared->execute(array( + ':chan' => $channel, + ':day' => $day, + )); + + // Loop through each line, + foreach ($prepared as $row) { + // Prepare some basic details for output: + $color = nick_color($row['nick']); + $time = utf8specialchars($row['t']); + $msg = utf8specialchars($row['message']); + $nick = utf8specialchars($row['nick']); + $type = false; + + // Now change the format of the line based upon the type: + switch ($row['type']) { + case 4: // PRIVMSG (A Regular Message) + echo "[$time] <span style=\"color:#{$color};\"><{$nick}></span> {$msg}<br />\n"; + break; + case 5: // ACTION (emote) + echo "[$time] <span style=\"color:#{$color};\">*{$nick} {$msg}</span><br />\n"; + break; + case 1: // JOIN + echo "[$time] -> {$nick} joined the room.<br />\n"; + break; + case 2: // PART (leaves channel) + echo "[$time] -> {$nick} left the room: {$msg}<br />\n"; + break; + case 3: // QUIT (quits the server) + echo "[$time] -> {$nick} left the server: {$msg}<br />\n"; + break; + case 6: // NICK (changes their nickname) + echo "[$time] -> {$nick} is now known as: {$msg}<br />\n"; + break; + case 7: // KICK (booted) + echo "[$time] -> {$nick} boots {$msg} from the room.<br />\n"; + break; + case 8: // MODE (changed their mode) + $type = 'MODE'; + case 9: // TOPIC (changed the topic) + $type = $type ? $type : 'TOPIC'; + echo "[$time] -> {$nick}: :{$type}: {$msg}<br />\n"; + } + } + + // Finish up the page: + template_footer(); +} + +/** + * nick_color + * + * Uses a silly little algorithm to pick a consistent but unique(ish) color for + * any given username. NOTE: Augment this in the future to make it not generate + * 'close to white' ones, also maybe to ensure uniqueness? (Not allow two to have + * colors that are close to each other?) + * + * @return string A CSS valid hex color string + * @author Eli White <eli@eliw.com> + **/ +function nick_color($user) { + static $colors = array(); + + if (!isset($colors[$user])) { + $colors[$user] = substr(md5($user), 0, 6); + } + + return $colors[$user]; +} + +/** + * utf8specialchars + * + * Just a quick wrapper around htmlspecialchars + * + * @param string The UTF8 string to escape + * @return string An escaped and ready for HTML use string + * @author Eli White <eli@eliw.com> + **/ +function utf8specialchars($string) { + return htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); +} + + +/********** TEMPLATES **********/ + +/** + * template_header + * + * Echo out the header for each HTML page + * + * @param $title string The title to be used for this page. + * @return void + * @author Eli White <eli@eliw.com> + **/ +function template_header($title) { + $css = template_css(); + echo <<<EOHTML +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>Phergie LogViewer - {$title}</title> + <style type="text/css" media="all">{$css}</style> + </head> + <body> + <h2>Phergie LogViewer - {$title}</h2> +EOHTML; +} + +/** + * template_footer + * + * Echo out the bottom of each HTML page + * + * @return void + * @author Eli White <eli@eliw.com> + **/ +function template_footer() { + echo <<<EOHTML + </body> +</html> +EOHTML; +} + +/** + * template_css + * + * Generate the CSS used by these HTML pages & return it. + * + * @return string The CSS in question: + * @author Eli White <eli@eliw.com> + **/ +function template_css() { + return <<<EOCSS + div.month { + float: left; + height: 15em; + } + + div.month table { + border-collapse: collapse; + border: 2px solid black; + margin-right: 2em; + } + + div.month td, div.month th { + text-align: center; + vertical-align: bottom; + border: 1px solid gray; + width: 2em; + height: 1.7em; + padding: 1px; + margin: 0px; + } + + div.month th { + text-decoration: bold; + border: 2px solid black; + } + + div.month a { + text-decoration: none; + } + + a:visited, a:link { + color: blue; + } + + a:active, a:hover { + color: red; + } +EOCSS; +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Tools/README b/plugins/Irc/extlib/phergie/Phergie/Tools/README new file mode 100755 index 000000000..ed8fe2946 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Tools/README @@ -0,0 +1,6 @@ +This directory holds any assorted tools that work with Phergie but that are +not part of Phergie itself. Such as tools to examine data that Phergie is +capturing/logging. + +Each should live inside of it's own directory, and be completely self-contained +with instructions inside on how to use it. diff --git a/plugins/Irc/extlib/phergie/Phergie/Ui/Abstract.php b/plugins/Irc/extlib/phergie/Phergie/Ui/Abstract.php new file mode 100644 index 000000000..e2e2ce2f1 --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Ui/Abstract.php @@ -0,0 +1,116 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * Base class for end-user interfaces. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +abstract class Phergie_Ui_Abstract +{ + /** + * Handler for when a server connection is attempted. + * + * @param string $host Server hostname + * + * @return void + */ + public function onConnect($host) + { + } + + /** + * Handler for when an attempt is made to load a plugin. + * + * @param string $plugin Short name of the plugin + * + * @return void + */ + public function onPluginLoad($plugin) + { + } + + /** + * Handler for when a plugin fails to load. + * + * @param string $plugin Short name of the plugin + * @param string $message Message describing the reason for the failure + * + * @return void + */ + public function onPluginFailure($plugin, $message) + { + } + + /** + * Handler for when the bot receives an IRC event. + * + * @param Phergie_Event_Abstract $event Received event + * @param Phergie_Connection $connection Connection on which the event + * was received + * + * @return void + */ + public function onEvent(Phergie_Event_Abstract $event, + Phergie_Connection $connection + ) { + } + + /** + * Handler for when the bot sends a command to a server. + * + * @param Phergie_Event_Command $event Event representing the command + * being sent + * @param Phergie_Connection $connection Connection on which the command + * is being sent + * + * @return void + */ + public function onCommand(Phergie_Event_Command $event, + Phergie_Connection $connection + ) { + } + + /** + * Handler for when the bot terminates a connection to a server. + * + * @param Phergie_Connection $connection Terminated connection + * + * @return void + */ + public function onQuit(Phergie_Connection $connection) + { + } + + /** + * Handler for when the bot shuts down after terminating all server + * connections. + * + * @return void + */ + public function onShutdown() + { + } +} diff --git a/plugins/Irc/extlib/phergie/Phergie/Ui/Console.php b/plugins/Irc/extlib/phergie/Phergie/Ui/Console.php new file mode 100644 index 000000000..a0a528b3f --- /dev/null +++ b/plugins/Irc/extlib/phergie/Phergie/Ui/Console.php @@ -0,0 +1,223 @@ +<?php +/** + * Phergie + * + * PHP version 5 + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE. + * It is also available through the world-wide-web at this URL: + * http://phergie.org/license + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @copyright 2008-2010 Phergie Development Team (http://phergie.org) + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ + +/** + * End-user interface that produces console output when running the bot from + * a shell. + * + * @category Phergie + * @package Phergie + * @author Phergie Development Team <team@phergie.org> + * @license http://phergie.org/license New BSD License + * @link http://pear.phergie.org/package/Phergie + */ +class Phergie_Ui_Console extends Phergie_Ui_Abstract +{ + /** + * Flag that toggles all console output + * + * @var bool + */ + protected $enabled; + + /** + * Format for timestamps included in console output + * + * @var string + * @link http://php.net/date + */ + protected $format; + + /** + * Constructor to initialize object properties. + * + * @return void + */ + public function __construct() + { + $this->enabled = true; + $this->format = 'H:i:s'; + } + + /** + * Outputs a timestamped line to the console if console output is enabled. + * + * @param string $line Line to output + * + * @return void + */ + protected function console($line) + { + if ($this->enabled) { + echo date($this->format), ' ', $line, PHP_EOL; + } + } + + /** + * Returns whether console output is enabled. + * + * @return bool TRUE if console output is enabled, FALSE otherwise + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * Sets whether console output is enabled. + * + * @param bool $enabled TRUE to enable console output, FALSE otherwise, + * defaults to TRUE + * + * @return Phergie_Ui_Console Provides a fluent interface + */ + public function setEnabled($enabled = true) + { + $this->enabled = (bool) $enabled; + return $this; + } + + /** + * Returns the format used for timestamps in console output. + * + * @return string + * @link http://php.net/date + */ + public function getFormat() + { + return $this->format; + } + + /** + * Sets the format used for timestamps in console output, overwriting + * any previous format used. + * + * @param string $format Timestamp format + * + * @return Phergie_Ui_Console Provides a fluent interface + * @link http://php.net/date + */ + public function setFormat($format) + { + $this->format = (string) $format; + return $this; + } + + /** + * Outputs a prompt when a server connection is attempted. + * + * @param string $host Server hostname + * + * @return void + */ + public function onConnect($host) + { + $this->console('Connecting to ' . $host); + } + + /** + * Outputs a prompt when a plugin is loaded successfully. + * + * @param string $plugin Short name of the plugin + * + * @return void + */ + public function onPluginLoad($plugin) + { + $this->console('Loaded plugin ' . $plugin); + } + + /** + * Outputs a prompt when a plugin fails to load. + * + * @param string $plugin Short name of the plugin + * @param string $message Message describing the reason for the failure + * + * @return void + */ + public function onPluginFailure($plugin, $message) + { + $this->console('Unable to load plugin ' . $plugin . ' - ' . $message); + } + + /** + * Outputs a prompt when the bot receives an IRC event. + * + * @param Phergie_Event_Abstract $event Received event + * @param Phergie_Connection $connection Connection on which the + * event was received + * + * @return void + */ + public function onEvent(Phergie_Event_Abstract $event, + Phergie_Connection $connection + ) { + $host = $connection->getHostmask()->getHost(); + $this->console($host . ' <- ' . $event->getRawData()); + } + + /** + * Outputs a prompt when the bot sends a command to a server. + * + * @param Phergie_Event_Command $event Event representing the + * command being sent + * @param Phergie_Connection $connection Connection on which the + * command is being sent + * + * @return void + */ + public function onCommand(Phergie_Event_Command $event, + Phergie_Connection $connection + ) { + $plugin = $event->getPlugin()->getName(); + $host = $connection->getHostmask()->getHost(); + $type = strtoupper($event->getType()); + $args = implode(' ', $event->getArguments()); + $this->console( + $plugin . ' plugin: ' . + $host . ' -> ' . $type . ' ' . $args + ); + } + + /** + * Outputs a prompt when the bot terminates a connection to a server. + * + * @param Phergie_Connection $connection Terminated connection + * + * @return void + */ + public function onQuit(Phergie_Connection $connection) + { + $host = $connection->getHostmask()->getHost(); + $this->console('Disconnecting from ' . $host); + } + + /** + * Outputs a prompt when the bot shuts down after terminating all server + * connections. + * + * @return void + */ + public function onShutdown() + { + $this->console('Shutting down'); + } +} |