root/trunk/Phergie/Plugin/Remind.php @ 289

Revision 289, 9.1 KB (checked in by tobias382, 5 years ago)

Fixes #62 Committing submitted patch for nick case bug

Line 
1<?php
2
3/**
4 * Parses and logs commands messages that should be relayed to other users the
5 * next time the recipient is active on the same channel
6 *
7 * (adapted from the Drink plugin)
8 */
9class Phergie_Plugin_Remind extends Phergie_Plugin_Abstract_Command
10{
11    /**
12     * Indicates that a local directory is required for this plugin
13     *
14     * @var bool
15     */
16    protected $needsDir = true;
17
18    /**
19     * PDO resource for a SQLite database containing the reminders
20     *
21     * @var resource
22     */
23    protected $db = null;
24
25    /**
26     * Flag that indicates whether or not to use and in-memory reminder list
27     *
28     * @var bool
29     */
30    protected $keepListInMemory = true;
31
32    /**
33     * In-memory store for pending reminders
34     *
35     * Form: $msgStore[channel][recipient] set if a pending reminder exists
36     */
37    protected $msgStore = array();
38
39    /**
40     * Connects to the database and populates tables where needed.
41     *
42     * @return void
43     */
44    public function onInit()
45    {
46        // Initialize the database connection
47        $this->db = new PDO('sqlite:' . $this->dir . 'remind.db');
48        if (!$this->db) {
49            return;
50        }
51        $this->createTables();
52        $this->populateMemory();
53    }
54
55    /**
56     * Returns whether or not the plugin's dependencies are met.
57     *
58     * @param Phergie_Driver_Abstract $client Client instance
59     * @param array $plugins List of short names for plugins that the
60     *                       bootstrap file intends to instantiate
61     * @see Phergie_Plugin_Abstract_Base::checkDependencies()
62     * @return bool TRUE if dependencies are met, FALSE otherwise
63     */
64    public static function checkDependencies(Phergie_Driver_Abstract $client, array $plugins)
65    {
66        $errors = array();
67
68        if (!extension_loaded('PDO')) {
69            $errors[] = 'PDO php extension is required';
70        }
71        if (!extension_loaded('pdo_sqlite')) {
72            $errors[] = 'pdo_sqlite php extension is required';
73        }
74        return empty($errors) ? true : $errors;
75    }
76
77    /**
78     * Determines if a table exists
79     *
80     * @param string $name Table name
81     * @return bool
82     */
83    protected function haveTable($name)
84    {
85        return (bool) $this->db->query(
86            'SELECT COUNT(*) FROM sqlite_master WHERE name = ' . $this->db->quote($name)
87            )->fetchColumn();
88    }
89
90    /**
91     * Creates the database table(s) (if they don't exist)
92     *
93     * @return void
94     */
95    protected function createTables()
96    {
97        if (!$this->haveTable('remind')) {
98            $this->debug('Creating the database schema for: remind');
99            $this->db->exec('
100                CREATE TABLE
101                    remind
102                    (
103                        time INTEGER,
104                        channel TEXT,
105                        recipient TEXT,
106                        sender TEXT,
107                        message TEXT
108                    )
109            ');
110        }
111    }
112
113    /**
114     * Populates the in-memory cache of pending reminders
115     *
116     * @return void
117     */
118    protected function populateMemory()
119    {
120        if (!$this->keepListInMemory) {
121            return;
122        }
123        $storeCounter = 0;
124        foreach ($this->fetchMessages() as $msg) {
125            $this->msgStore[$msg['channel']][$msg['recipient']] = $msg['rowid'];
126            ++$storeCounter;
127        }
128        $this->debug("Found {$storeCounter} messages", true);
129    }
130
131    /**
132     * Gets pending messages (for a specific channel/recipient)
133     *
134     * @param string $channel   channel on which to check pending messages
135     * @param string $recipient user for which to check pending messages
136     * @return array of records
137     */
138    protected function fetchMessages($channel = null, $recipient = null)
139    {
140        if ($channel) {
141            $qClause = 'WHERE channel = :channel AND recipient = :recipient';
142            $params = array(
143                'channel' => $channel,
144                'recipient' => strtolower($recipient)
145            );
146        } else {
147            $qClause = '';
148            $params = array();
149        }
150        $q = $this->db->prepare(
151            'SELECT rowid, channel, sender, recipient, time, message
152            FROM remind ' . $qClause
153        );
154        $q->execute($params);
155        return $q->fetchAll();
156    }
157
158    /**
159     * Deletes a delivered message
160     *
161     * @param int    $rowid     ID of the message to delete
162     * @param string $channel   message's channel
163     * @param string $recipient message's recipient
164     * @return void
165     */
166    protected function deleteMessage($rowid, $channel, $nick)
167    {
168        $q = $this->db->prepare('DELETE FROM remind WHERE rowid = :rowid');
169        $q->execute(array('rowid' => $rowid));
170
171        if ($this->keepListInMemory) {
172            if (isset($this->msgStore[$channel][$nick])
173                && $this->msgStore[$channel][$nick] == $rowid) {
174                unset($this->msgStore[$channel][$nick]);
175            }
176        }
177    }
178
179    /**
180     * Get data for a specific message
181     *
182     * @param int $rowid row ID of the message to get
183     */
184    protected function getMessage($rowid)
185    {
186        $q = $this->db->prepare('
187            SELECT rowid, time, channel, recipient, sender, message
188            FROM remind
189            WHERE rowid = :rowid
190        ');
191        $q->execute(array('rowid' => $rowid));
192        return $q->fetch(PDO::FETCH_ASSOC);
193    }
194
195    /**
196     * Handles the tell/remind command (stores the message)
197     *
198     * @param string $recipient name of the recipient
199     * @param string $essage    message to store
200     * @return void
201     */
202    protected function handleRemind($recipient, $message)
203    {
204        $source = $this->event->getSource();
205        $nick = $this->event->getNick();
206        if (!$this->event->isInChannel()) {
207            $this->doPrivmsg($source, 'Reminders must be requested in-channel.');
208            return;
209        }
210        $q = $this->db->prepare('
211            INSERT INTO remind
212                (
213                    time,
214                    channel,
215                    recipient,
216                    sender,
217                    message
218                )
219            VALUES
220                (
221                    :time,
222                    :channel,
223                    :recipient,
224                    :sender,
225                    :message
226                )
227        ');
228        try {
229            $q->execute(array(
230                'time' => date(DATE_RFC822),
231                'channel' => $source,
232                'recipient' => strtolower($recipient),
233                'sender' => strtolower($nick),
234                'message' => $message
235            ));
236        } catch (PDOException $e) { }
237
238        if ($rowid = $this->db->lastInsertId()) {
239            $this->doPrivmsg($source, "ok, $nick, message stored");
240        } else {
241            $this->doPrivmsg($source, "$nick: bad things happened. Message not saved.");
242            return;
243        }
244
245        if ($this->keepListInMemory) {
246            $this->msgStore[$source][$recipient] = $rowid;
247        }
248    }
249
250    /**
251     * Determines if the user has pending reminders, and if so, delivers them
252     *
253     * @param string $channel   channel to check
254     * @param string $recipient recipient to check
255     * @return Bool
256     */
257    protected function deliverReminders($channel, $nick)
258    {
259        if ($channel[0] != '#') {
260            // private message, not a channel, so don't check
261            return;
262        }
263
264        // short circuit if there's no message in memory (if allowed)
265        if ($this->keepListInMemory
266            && !isset($this->msgStore[$channel][strtolower($nick)])) {
267            return;
268        }
269       
270        // fetch and deliver messages
271        foreach ($this->fetchMessages($channel, $nick) as $msg) {
272            $this->doPrivmsg(
273                $channel,
274                "{$nick}: (from {$msg['sender']}, {$msg['time']}) " .
275                    $msg['message']
276            );
277            $this->deleteMessage($msg['rowid'], $channel, $nick);
278        }
279    }
280
281    /**
282     * Intercepts a message and processes any contained recognized commands.
283     *
284     * Overrides parent to force bot nick prefix (3rd param of processCommand())
285     * Also checks to see if the message's nick has any pending message and
286     * delivers them if so
287     *
288     * @return void
289     */
290    public function onPrivmsg()
291    {
292        $this->deliverReminders($this->event->getSource(), $this->event->getNick());
293        $this->processCommand($this->event->getArgument(1), false, true);
294    }
295
296    /**
297     * Handles reminder requests
298     *
299     * @param string $message
300     * @return void
301     */
302    public function onDoTell($recipient, $message)
303    {
304        $this->handleRemind($recipient, $message);
305    }
306   
307    /**
308     * Handles reminder requests
309     *
310     * @param string $message
311     * @return void
312     */
313    public function onDoAsk($recipient, $message)
314    {
315        $this->handleRemind($recipient, $message);
316    }
317
318    /**
319     * Handles reminder requests
320     *
321     * @param string $message
322     * @return void
323     */
324    public function onDoRemind($recipient, $message)
325    {
326        $this->handleRemind($recipient, $message);
327    }
328}
Note: See TracBrowser for help on using the browser.