From 0ef7c5559f6fabd4325c9c892a2b2f5fcbddb9b3 Mon Sep 17 00:00:00 2001 From: Luke Fitzgerald Date: Sun, 4 Jul 2010 09:51:44 -0700 Subject: [PATCH] Initial IRC plugin work --- plugins/Irc/Fake_Irc.php | 58 +++++++++++ plugins/Irc/IrcPlugin.php | 155 +++++++++++++++++++++++++++++ plugins/Irc/ircmanager.php | 195 +++++++++++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 plugins/Irc/Fake_Irc.php create mode 100644 plugins/Irc/IrcPlugin.php create mode 100644 plugins/Irc/ircmanager.php diff --git a/plugins/Irc/Fake_Irc.php b/plugins/Irc/Fake_Irc.php new file mode 100644 index 0000000000..eb302aea14 --- /dev/null +++ b/plugins/Irc/Fake_Irc.php @@ -0,0 +1,58 @@ +. + * + * @category Network + * @package StatusNet + * @author Luke Fitzgerald + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +class Fake_Irc extends Phergie_Driver_Streams { + public $would_be_sent = null; + + private function send($command, $args = '') { + // 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; + } + + $this->would_be_sent = $buffer . "\r\n"; + } +} diff --git a/plugins/Irc/IrcPlugin.php b/plugins/Irc/IrcPlugin.php new file mode 100644 index 0000000000..3a188f667a --- /dev/null +++ b/plugins/Irc/IrcPlugin.php @@ -0,0 +1,155 @@ +. + * + * @category IM + * @package StatusNet + * @author Luke Fitzgerald + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} +// We bundle the Phergie library... +set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib/phergie'); +require 'Phergie/Autoload.php'; +Phergie_Autoload::registerAutoloader(); + +/** + * Plugin for IRC + * + * @category Plugin + * @package StatusNet + * @author Luke Fitzgerald + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class IrcPlugin extends ImPlugin { + public $user = null; + public $password = null; + public $publicFeed = array(); + + public $transport = 'irc'; + + function getDisplayName() { + return _m('IRC'); + } + + function normalize($screenname) { + $screenname = str_replace(" ","", $screenname); + return strtolower($screenname); + } + + function daemon_screenname() { + return $this->user; + } + + function validate($screenname) { + if (preg_match('/^[a-z]\w{2,15}$/i', $screenname)) { + return true; + } else { + return false; + } + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + */ + function onAutoload($cls) { + $dir = dirname(__FILE__); + + switch ($cls) { + case 'IrcManager': + include_once $dir . '/'.strtolower($cls).'.php'; + return false; + case 'Fake_Irc': + include_once $dir . '/'. $cls .'.php'; + return false; + default: + return true; + } + } + + function onStartImDaemonIoManagers(&$classes) { + parent::onStartImDaemonIoManagers(&$classes); + $classes[] = new IrcManager($this); // handles sending/receiving + return true; + } + + function microiduri($screenname) { + return 'irc:' . $screenname; + } + + function send_message($screenname, $body) { + $this->fake_irc->sendIm($screenname, $body); + $this->enqueue_outgoing_raw($this->fake_irc->would_be_sent); + return true; + } + + /** + * Accept a queued input message. + * + * @return true if processing completed, false if message should be reprocessed + */ + function receive_raw_message($message) { + $info=Aim::getMessageInfo($message); + $from = $info['from']; + $user = $this->get_user($from); + $notice_text = $info['message']; + + $this->handle_incoming($from, $notice_text); + + return true; + } + + function initialize() { + if (!isset($this->user)) { + throw new Exception("must specify a user"); + } + if (!isset($this->password)) { + throw new Exception("must specify a password"); + } + + $this->fake_irc = new Fake_Irc($this->user, $this->password, 4); + return true; + } + + function onPluginVersion(&$versions) { + $versions[] = array('name' => 'IRC', + 'version' => STATUSNET_VERSION, + 'author' => 'Luke Fitzgerald', + 'homepage' => 'http://status.net/wiki/Plugin:IRC', + 'rawdescription' => + _m('The IRC plugin allows users to send and receive notices over an IRC network.')); + return true; + } +} diff --git a/plugins/Irc/ircmanager.php b/plugins/Irc/ircmanager.php new file mode 100644 index 0000000000..6a86e34fd3 --- /dev/null +++ b/plugins/Irc/ircmanager.php @@ -0,0 +1,195 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +/** + * IRC background connection manager for IRC-using queue handlers, + * allowing them to send outgoing messages on the right connection. + * + * Input is handled during socket select loop, keepalive pings during idle. + * Any incoming messages will be handled. + * + * In a multi-site queuedaemon.php run, one connection will be instantiated + * for each site being handled by the current process that has IRC enabled. + */ + +class IrcManager extends ImManager { + + public $conn = null; + /** + * Initialize connection to server. + * @return boolean true on success + */ + public function start($master) { + if (parent::start($master)) { + $this->connect(); + return true; + } else { + return false; + } + } + + public function getSockets() { + $this->connect(); + if ($this->conn) { + return array($this->conn->myConnection); + } else { + return array(); + } + } + + /** + * Process IRC events that have come in over the wire. + * @param resource $socket + */ + public function handleInput($socket) { + common_log(LOG_DEBUG, "Servicing the IRC queue."); + $this->stats('irc_process'); + $this->conn->receive(); + } + + function connect() { + if (!$this->conn) { + $this->conn = new Phergie_Bot; + + $password = isset($this->plugin->password) ? $this->plugin->password : NULL; + $transport = isset($this->plugin->transport) ? $this->plugin->transport : 'tcp'; + $encoding = isset($this->plugin->encoding) ? $this->plugin->encoding : 'ISO-8859-1'; + + $config = new Phergie_Extended_Config; + $config->readArray( + array( + // One array per connection, pretty self-explanatory + 'connections' => array( + // Ex: All connection info for the Freenode network + array( + 'host' => $this->plugin->host, + 'port' => $this->plugin->port, + 'username' => $this->plugin->username, + 'realname' => $this->plugin->realname, + 'nick' => $this->plugin->nickname, + 'password' => $password, + 'transport' => $transport, + 'encoding' => $encoding + ) + ), + + 'processor' => 'async', + 'processor.options' => array('usec' => 200000), + // Time zone. See: http://www.php.net/manual/en/timezones.php + 'timezone' => 'UTC', + + // Whitelist of plugins to load + 'plugins' => array( + // To enable a plugin, simply add a string to this array containing + // the short name of the plugin as shown below. + + // 'ShortPluginName', + + // Below is an example of enabling the AutoJoin plugin, for which + // the corresponding PEAR package is Phergie_Plugin_AutoJoin. This + // plugin allows you to set a list of channels in this configuration + // file that the bot will automatically join when it connects to a + // server. If you'd like to enable this plugin, simply install it, + // uncomment the line below, and set a value for the setting + // autojoin.channels (examples for which are located further down in + // this file). + + // 'AutoJoin', + + // A few other recommended plugins: + + // Servers randomly send PING events to clients to ensure that + // they're still connected and will eventually terminate the + + // connection if a PONG response is not received. The Pong plugin + // handles sending these responses. + + // 'Pong', + + // It's sometimes difficult to distinguish between a lack of + // activity on a server and the client not receiving data even + // though a connection remains open. The Ping plugin performs a self + // CTCP PING sporadically to ensure that its connection is still + // functioning and, if not, terminates the bot. + + // 'Ping', + + // Sometimes it's desirable to have the bot disconnect gracefully + // when issued a command to do so via a PRIVMSG event. The Quit + // plugin implements this using the Command plugin to intercept the + // command. + + // 'Quit', + ), + + // If set to true, this allows any plugin dependencies for plugins + // listed in the 'plugins' option to be loaded even if they are not + // explicitly included in that list + 'plugins.autoload' => true, + + // Enables shell output describing bot events via Phergie_Ui_Console + 'ui.enabled' => true, + + // Examples of supported values for autojoins.channel: + // 'autojoin.channels' => '#channel1,#channel2', + // 'autojoin.channels' => array('#channel1', '#channel2'), + // 'autojoin.channels' => array( + // 'host1' => '#channel1,#channel2', + // 'host2' => array('#channel3', '#channel4') + // ), + + // Examples of setting values for Ping plugin settings + + // This is the amount of time in seconds that the Ping plugin will wait + // to receive an event from the server before it initiates a self-ping + + // 'ping.event' => 300, // 5 minutes + + // This is the amount of time in seconds that the Ping plugin will wait + // following a self-ping attempt before it assumes that a response will + // never be received and terminates the connection + + // 'ping.ping' => 10, // 10 seconds + +)); + $this->conn=new Aim($this->plugin->user,$this->plugin->password,4); + $this->conn->registerHandler("IMIn",array($this,"handle_aim_message")); + $this->conn->myServer="toc.oscar.aol.com"; + $this->conn->signon(); + $this->conn->setProfile(_m('Send me a message to post a notice'), false); + } + return $this->conn; + } + + function handle_irc_message($data) { + $this->plugin->enqueue_incoming_raw($data); + return true; + } + + function send_raw_message($data) { + $this->connect(); + if (!$this->conn) { + return false; + } + $this->conn->sflapSend($data[0],$data[1],$data[2],$data[3]); + return true; + } +}