* @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 * @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 */ 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'); } /** * Creates the database if it does not already exist. * * @return void */ public function onConnect() { $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']; } } }