9 * This source file is subject to the new BSD license that is bundled
10 * with this package in the file LICENSE.
11 * It is also available through the world-wide-web at this URL:
12 * http://phergie.org/license
15 * @package Phergie_Plugin_Karma
16 * @author Phergie Development Team <team@phergie.org>
17 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
18 * @license http://phergie.org/license New BSD License
19 * @link http://pear.phergie.org/package/Phergie_Plugin_Karma
23 * Handles requests for incrementation or decrementation of a maintained list
24 * of counters for specified terms.
27 * @package Phergie_Plugin_Karma
28 * @author Phergie Development Team <team@phergie.org>
29 * @license http://phergie.org/license New BSD License
30 * @link http://pear.phergie.org/package/Phergie_Plugin_Karma
32 * @uses extension pdo_sqlite
33 * @uses Phergie_Plugin_Command pear.phergie.org
35 class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
45 * Prepared statement to add a new karma record
49 protected $insertKarma;
52 * Prepared statement to update an existing karma record
56 protected $updateKarma;
59 * Retrieves an existing karma record
63 protected $fetchKarma;
66 * Retrieves an existing fixed karma record
70 protected $fetchFixedKarma;
73 * Retrieves a positive answer for a karma comparison
77 protected $fetchPositiveAnswer;
80 * Retrieves a negative answer for a karma comparison
84 protected $fetchNegativeAnswer;
87 * Check for dependencies and initializes a database connection and
88 * prepared statements.
92 public function onLoad()
94 $plugins = $this->getPluginHandler();
95 $plugins->getPlugin('Command');
100 * Initializes prepared statements used by the plugin.
104 protected function initializePreparedStatements()
106 $this->fetchKarma = $this->db->prepare('
113 $this->insertKarma = $this->db->prepare('
114 INSERT INTO karmas (term, karma)
115 VALUES (:term, :karma)
118 $this->updateKarma = $this->db->prepare('
124 $this->fetchFixedKarma = $this->db->prepare('
131 $this->fetchPositiveAnswer = $this->db->prepare('
133 FROM positive_answers
138 $this->fetchNegativeAnswer = $this->db->prepare('
140 FROM negative_answers
147 * Returns a connection to the plugin database, initializing one if none
150 * @return PDO Database connection
152 public function getDb()
154 if (empty($this->db)) {
155 $this->db = new PDO('sqlite:' . dirname(__FILE__) . '/Karma/karma.db');
156 $this->initializePreparedStatements();
162 * Sets the connection to the plugin database, mainly intended for unit
165 * @param PDO $db Database connection
167 * @return Phergie_Plugin_Karma Provides a fluent interface
169 public function setDb(PDO $db)
172 $this->initializePreparedStatements();
177 * Get the canonical form of a given term.
179 * In the canonical form all sequences of whitespace
180 * are replaced by a single space and all characters
183 * @param string $term Term for which a canonical form is required
185 * @return string Canonical term
187 protected function getCanonicalTerm($term)
189 $canonicalTerm = strtolower(preg_replace('|\s+|', ' ', trim($term, '()')));
190 switch ($canonicalTerm) {
192 $canonicalTerm = strtolower($this->event->getNick());
197 $canonicalTerm = 'everything';
200 return $canonicalTerm;
204 * Intercepts a message and processes any contained recognized commands.
208 public function onPrivmsg()
210 $message = $this->getEvent()->getText();
212 $termPattern = '\S+?|\([^<>]+?\)+';
213 $actionPattern = '(?P<action>\+\+|--)';
215 $modifyPattern = <<<REGEX
217 (?J) # allow overwriting capture names
218 \s* # ignore leading whitespace
220 (?: # start with ++ or -- before the term
222 (?P<term>$termPattern)
223 | # follow the term with ++ or --
224 (?P<term>$termPattern)
225 $actionPattern # allow no whitespace between the term and the action
230 $versusPattern = <<<REGEX
232 (?P<term0>$termPattern)
233 \s+(?P<method><|>)\s+
234 (?P<term1>$termPattern)$#
240 if (preg_match($modifyPattern, $message, $match)) {
241 $action = $match['action'];
242 $term = $this->getCanonicalTerm($match['term']);
243 $this->modifyKarma($term, $action);
244 } elseif (preg_match($versusPattern, $message, $match)) {
245 $term0 = trim($match['term0']);
246 $term1 = trim($match['term1']);
247 $method = $match['method'];
248 $this->compareKarma($term0, $term1, $method);
253 * Get the karma rating for a given term.
255 * @param string $term Term for which the karma rating needs to be
260 public function onCommandKarma($term)
262 $source = $this->getEvent()->getSource();
263 $nick = $this->getEvent()->getNick();
265 $canonicalTerm = $this->getCanonicalTerm($term);
267 $fixedKarma = $this->fetchFixedKarma($canonicalTerm);
269 $message = $nick . ': ' . $term . ' ' . $fixedKarma;
270 $this->doPrivmsg($source, $message);
274 $karma = $this->fetchKarma($canonicalTerm);
276 $message = $nick . ': ';
279 $message .= 'You have';
281 $message .= $term . ' has';
287 $message .= 'karma of ' . $karma;
289 $message .= 'neutral karma';
294 $this->doPrivmsg($source, $message);
298 * Resets the karma for a term to 0.
300 * @param string $term Term for which to reset the karma rating
304 public function onCommandReincarnate($term)
310 $this->updateKarma->execute($data);
314 * Compares the karma between two terms. Optionally increases/decreases
315 * the karma of either term.
317 * @param string $term0 First term
318 * @param string $term1 Second term
319 * @param string $method Comparison method (< or >)
323 protected function compareKarma($term0, $term1, $method)
325 $event = $this->getEvent();
326 $nick = $event->getNick();
327 $source = $event->getSource();
329 $canonicalTerm0 = $this->getCanonicalTerm($term0);
330 $canonicalTerm1 = $this->getCanonicalTerm($term1);
332 $fixedKarma0 = $this->fetchFixedKarma($canonicalTerm0);
333 $fixedKarma1 = $this->fetchFixedKarma($canonicalTerm1);
335 if ($fixedKarma0 || $fixedKarma1) {
339 if ($canonicalTerm0 == 'everything') {
340 $change = $method == '<' ? '++' : '--';
342 $karma1 = $this->modifyKarma($canonicalTerm1, $change);
343 } elseif ($canonicalTerm1 == 'everything') {
344 $change = $method == '<' ? '--' : '++';
345 $karma0 = $this->modifyKarma($canonicalTerm0, $change);
348 $karma0 = $this->fetchKarma($canonicalTerm0);
349 $karma1 = $this->fetchKarma($canonicalTerm1);
352 // Combining the first and second branches here causes an odd
353 // single-line lapse in code coverage, but the lapse disappears if
355 if ($method == '<' && $karma0 < $karma1) {
356 $replies = $this->fetchPositiveAnswer;
357 } elseif ($method == '>' && $karma0 > $karma1) {
358 $replies = $this->fetchPositiveAnswer;
360 $replies = $this->fetchNegativeAnswer;
363 $reply = $replies->fetchColumn();
365 if (max($karma0, $karma1) == $karma1) {
366 list($canonicalTerm0, $canonicalTerm1) =
367 array($canonicalTerm1, $canonicalTerm0);
370 $message = str_replace(
371 array('%owner%','%owned%'),
372 array($canonicalTerm0, $canonicalTerm1),
376 $this->doPrivmsg($source, $message);
380 * Modifes a term's karma.
382 * @param string $term Term to modify
383 * @param string $action Karma action (either ++ or --)
385 * @return int Modified karma rating
387 protected function modifyKarma($term, $action)
389 $karma = $this->fetchKarma($term);
390 if ($karma !== false) {
391 $statement = $this->updateKarma;
393 $statement = $this->insertKarma;
396 $karma += ($action == '++') ? 1 : -1;
402 $statement->execute($args);
408 * Returns the karma rating for a specified term for which the karma
409 * rating can be modified.
411 * @param string $term Term for which to fetch the corresponding karma
414 * @return integer|boolean Integer value denoting the term's karma or
415 * FALSE if there is the specified term has no associated karma
418 protected function fetchKarma($term)
420 $this->fetchKarma->execute(array(':term' => $term));
421 $result = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
423 if ($result === false) {
427 return (int) $result['karma'];
431 * Returns a phrase describing the karma rating for a specified term for
432 * which the karma rating is fixed.
434 * @param string $term Term for which to fetch the corresponding karma
437 * @return string Phrase describing the karma rating, which may be append
438 * to the term to form a complete response
440 protected function fetchFixedKarma($term)
442 $this->fetchFixedKarma->execute(array(':term' => $term));
443 $result = $this->fetchFixedKarma->fetch(PDO::FETCH_ASSOC);
445 if ($result === false) {
449 return $result['karma'];