]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php
Merged in Phergie changes
[quix0rs-gnu-social.git] / plugins / Irc / extlib / phergie / Phergie / Plugin / Karma.php
index b7ce48685a330f4b4cb3879cea68b6fccb3efed0..f55f8d6edf78fb9297ea0048c558ded6105b79af 100644 (file)
 
 /**
  * Handles requests for incrementation or decrementation of a maintained list
- * of counters for specified terms and antithrottling to prevent extreme
- * inflation or depression of counters by any single individual.
+ * 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
+ * @uses     Phergie_Plugin_Message pear.phergie.org
  */
 class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
 {
     /**
-     * Stores the SQLite object
+     * SQLite object
      *
      * @var resource
      */
     protected $db = null;
 
     /**
-     * Retains the last garbage collection date
+     * Prepared statement to add a new karma record
      *
-     * @var array
+     * @var PDOStatement
      */
-    protected $lastGc = null;
+    protected $insertKarma;
 
     /**
-     * Logs the karma usages and limits users to one karma change per word
-     * and per day
+     * Prepared statement to update an existing karma record
      *
-     * @return void
+     * @var PDOStatement
      */
-    protected $log = array();
+    protected $updateKarma;
 
     /**
-     * Some fixed karma values, keys must be lowercase
+     * Retrieves an existing karma record
      *
-     * @var array
+     * @var PDOStatement
      */
-    protected $fixedKarma;
+    protected $fetchKarma;
 
     /**
-     * A list of blacklisted values
+     * Retrieves an existing fixed karma record
      *
-     * @var array
-     */
-    protected $karmaBlacklist;
-
-    /**
-     * Answers for correct assertions
+     * @var PDOStatement
      */
-    protected $positiveAnswers;
+    protected $fetchFixedKarma;
 
     /**
-     * Answers for incorrect assertions
+     * Retrieves a positive answer for a karma comparison
+     *
+     * @var PDOStatement
      */
-    protected $negativeAnswers;
+    protected $fetchPositiveAnswer;
 
     /**
-     * Prepared PDO statements
+     * Retrieves a negative answer for a karma comparison
      *
      * @var PDOStatement
      */
-    protected $insertKarma;
-    protected $updateKarma;
-    protected $fetchKarma;
-    protected $insertComment;
+    protected $fetchNegativeAnswer;
 
     /**
-     * Connects to the database containing karma ratings and initializes
-     * class properties.
+     * Check for dependencies and initializes a database connection and
+     * prepared statements.
      *
      * @return void
      */
     public function onLoad()
     {
-        $this->db = null;
-        $this->lastGc = null;
-        $this->log = array();
+        $plugins = $this->getPluginHandler();
+        $plugins->getPlugin('Command');
+        $plugins->getPlugin('Message');
 
-        if(!defined('M_EULER')) {
-            define('M_EULER', '0.57721566490153286061');
-        }
+        $file = dirname(__FILE__) . '/Karma/karma.db';
+        $this->db = new PDO('sqlite:' . $file);
 
-        $this->fixedKarma = array(
-            'phergie'      => '%s has karma of awesome',
-            'pi'           => '%s has karma of ' . M_PI,
-            'Π'            => '%s has karma of ' . M_PI,
-            'Ï€'            => '%s has karma of ' . M_PI,
-            'chucknorris'  => '%s has karma of Warning: Integer out of range',
-            'chuck norris' => '%s has karma of Warning: Integer out of range',
-            'c'            => '%s has karma of 299 792 458 m/s',
-            'e'            => '%s has karma of ' . M_E,
-            'euler'        => '%s has karma of ' . M_EULER,
-            'mole'         => '%s has karma of 6.02214e23 molecules',
-            'avogadro'     => '%s has karma of 6.02214e23 molecules',
-            'spoon'        => '%s has no karma. There is no spoon',
-            'mc^2'         => '%s has karma of E',
-            'mc2'          => '%s has karma of E',
-            'mc²'          => '%s has karma of E',
-            'i'            => '%s haz big karma',
-            'karma' => 'The karma law says that all living creatures are responsible for their karma - their actions and the effects of their actions. You should watch yours.'
-        );
-
-        $this->karmaBlacklist = array(
-            '*',
-            'all',
-            'everything'
-        );
-
-        $this->positiveAnswers = array(
-            'No kidding, %owner% totally kicks %owned%\'s ass !',
-            'True that.',
-            'I concur.',
-            'Yay, %owner% ftw !',
-            '%owner% is made of WIN!',
-            'Nothing can beat %owner%!',
-        );
-
-        $this->negativeAnswers = array(
-            'No sir, not at all.',
-            'You\'re wrong dude, %owner% wins.',
-            'I\'d say %owner% is better than %owned%.',
-            'You must be joking, %owner% ftw!',
-            '%owned% is made of LOSE!',
-            '%owned% = Epic Fail',
-        );
-
-        // Load or initialize the database
-        $class = new ReflectionClass(get_class($this));
-        $dir = dirname($class->getFileName() . '/' . $this->name);
-        $this->db = new PDO('sqlite:' . $dir . 'karma.db');
-
-        // Check to see if the table exists
-        $table = $this->db->query('
-            SELECT COUNT(*)
-            FROM sqlite_master
-            WHERE name = ' . $this->db->quote('karmas')
-        )->fetchColumn();
-
-        // Create database tables if necessary
-        if (!$table) {
-            $this->db->query('
-                CREATE TABLE karmas ( word VARCHAR ( 255 ), karma MEDIUMINT );
-                CREATE UNIQUE INDEX word ON karmas ( word );
-                CREATE INDEX karmaIndex ON karmas ( karma );
-                CREATE TABLE comments ( wordid INT , comment VARCHAR ( 255 ) );
-                CREATE INDEX wordidIndex ON comments ( wordid );
-                CREATE UNIQUE INDEX commentUnique ON comments ( comment );
-            ');
-        }
+        $this->fetchKarma = $this->db->prepare('
+            SELECT karma
+            FROM karmas
+            WHERE term = :term
+            LIMIT 1
+        ');
 
         $this->insertKarma = $this->db->prepare('
-            INSERT INTO karmas (
-                word,
-                karma
-            )
-            VALUES (
-                :word,
-                :karma
-            )
+            INSERT INTO karmas (term, karma)
+            VALUES (:term, :karma)
         ');
 
-        $this->insertComment = $this->db->prepare('
-            INSERT INTO comments (
-                wordid,
-                comment
-            )
-            VALUES (
-                :wordid,
-                :comment
-            )
+        $this->updateKarma = $this->db->prepare('
+            UPDATE karmas
+            SET karma = :karma
+            WHERE term = :term
         ');
 
-        $this->fetchKarma = $this->db->prepare('
-            SELECT karma, ROWID id FROM karmas WHERE LOWER(word) = LOWER(:word) LIMIT 1
+        $this->fetchFixedKarma = $this->db->prepare('
+            SELECT karma
+            FROM fixed_karmas
+            WHERE term = :term
+            LIMIT 1
         ');
 
-        $this->updateKarma = $this->db->prepare('
-            UPDATE karmas SET karma = :karma WHERE LOWER(word) = LOWER(:word)
+        $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
         ');
     }
 
     /**
-     * Checks for dependencies.
+     * Get the canonical form of a given term.
      *
-     * @return void
+     * 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
      */
-    public static function onLoad()
+    protected function getCanonicalTerm($term)
     {
-       if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
-            $this->fail('PDO and pdo_sqlite extensions must be installed');
-       }
+        $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;
     }
 
     /**
-     * Handles requests for incrementation, decrementation, or lookup of karma
-     * ratings sent via messages from users.
+     * Intercepts a message and processes any contained recognized commands.
      *
      * @return void
      */
     public function onPrivmsg()
     {
-        $source = $this->event->getSource();
-        $message = $this->event->getArgument(1);
-        $target = $this->event->getNick();
-
-        // Command prefix check
-        $prefix = preg_quote(trim($this->getConfig('command.prefix')));
-        $bot = preg_quote($this->getConfig('connections.nick'));
-        $exp = '(?:(?:' . $bot . '\s*[:,>]?\s+(?:' . $prefix . ')?)|(?:' . $prefix . '))';
-
-        // Karma status request
-        if (preg_match('#^' . $exp . 'karma\s+(.+)$#i', $message, $m)) {
-            // Return user's value if "me" is requested
-            if (strtolower($m[1]) === 'me') {
-                $m[1] = $target;
-            }
-            // Clean the term
-            $term = $this->doCleanWord($m[1]);
-
-            // Check the blacklist
-            if (is_array($this->karmaBlacklist) && in_array($term, $this->karmaBlacklist)) {
-                $this->doNotice($target, $term . ' is blacklisted');
-                return;
-            }
-
-            // Return fixed value if set
-            if (isset($this->fixedKarma[$term])) {
-                $this->doPrivmsg($source, $target . ': ' . sprintf($this->fixedKarma[$term], $m[1]) . '.');
-                return;
-            }
-
-            // Return current karma or neutral if not set yet
-            $this->fetchKarma->execute(array(':word'=>$term));
-            $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-
-            // Sanity check if someone if someone prefixed their conversation with karma
-            if (!$res && substr_count($term, ' ') > 1 && !(substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')')) {
-                return;
-            }
-
-            // Clean the raw term if it was contained within brackets
-            if (substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')') {
-                $m[1] = substr($m[1], 1, -1);
-            }
-
-            if ($res && $res['karma'] != 0) {
-                $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has karma of ' . $res['karma'] . '.');
-            } else {
-                $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has neutral karma.');
-            }
-        // Incrementation/decrementation request
-        } elseif (preg_match('{^' . $exp . '?(?:(\+{2,2}|-{2,2})(\S+?|\(.+?\)+)|(\S+?|\(.+?\)+)(\+{2,2}|-{2,2}))(?:\s+(.*))?$}ix', $message, $m)) {
-            if (!empty($m[4])) {
-                $m[1] = $m[4]; // Increment/Decrement
-                $m[2] = $m[3]; // Word
-            }
-            $m[3] = (isset($m[5]) ? $m[5] : null); // Comment
-            unset($m[4], $m[5]);
-            list(, $sign, $word, $comment) = array_pad($m, 4, null);
-
-            // Clean the word
-            $word = strtolower($this->doCleanWord($word));
-            if (empty($word)) {
-                return;
-            }
-
-            // Do nothing if the karma is fixed or blacklisted
-            if (isset($this->fixedKarma[$word]) ||
-                is_array($this->karmaBlacklist) && in_array($word, $this->karmaBlacklist)) {
-                return;
-            }
-
-            // Force a decrementation if someone tries to update his own karma
-            if ($word == strtolower($target) && $sign != '--' && !$this->fromAdmin(true)) {
-                $this->doNotice($target, 'Bad ' . $target . '! You can not modify your own Karma. Shame on you!');
-                $sign = '--';
-            }
-
-            // Antithrottling check
-            $host = $this->event->getHost();
-            $limit = $this->getConfig('karma.limit');
-            // This is waiting on the Acl plugin from Elazar, being bypassed for now
-            //if ($limit > 0 && !$this->fromAdmin()) {
-            if ($limit > 0) {
-                if (isset($this->log[$host][$word]) && $this->log[$host][$word] >= $limit) {
-                    // Three strikes, you're out, so lets decrement their karma for spammage
-                    if ($this->log[$host][$word] == ($limit+3)) {
-                        $this->doNotice($target, 'Bad ' . $target . '! Didn\'t I tell you that you reached your limit already?');
-                        $this->log[$host][$word] = $limit;
-                        $word = $target;
-                        $sign = '--';
-                    // Toss a notice to the user if they reached their limit
-                    } else {
-                        $this->doNotice($target, 'You have currently reached your limit in modifying ' . $word . ' for this day, please wait a bit.');
-                        $this->log[$host][$word]++;
-                        return;
-                    }
-                } else {
-                    if (isset($this->log[$host][$word])) {
-                        $this->log[$host][$word]++;
-                    } else {
-                        $this->log[$host][$word] = 1;
-                    }
-                }
-            }
-
-            // Get the current value then update or create entry
-            $this->fetchKarma->execute(array(':word'=>$word));
-            $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-            if ($res) {
-                $karma = ($res['karma'] + ($sign == '++' ? 1 : -1));
-                $args = array(
-                    ':word' => $word,
-                    ':karma' => $karma
-                );
-                $this->updateKarma->execute($args);
-            } else {
-                $karma = ($sign == '++' ? '1' : '-1');
-                $args = array(
-                    ':word' => $word,
-                    ':karma' => $karma
-                );
-                $this->insertKarma->execute($args);
-                $this->fetchKarma->execute(array(':word'=>$word));
-                $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-            }
-            $id = $res['id'];
-            // Add comment
-            $comment = preg_replace('{(?:^//(.*)|^#(.*)|^/\*(.*?)\*/$)}', '$1$2$3', $comment);
-            if (!empty($comment)) {
-                $this->insertComment->execute(array(':wordid' => $id, ':comment' => $comment));
-            }
-            // Perform garbage collection on the antithrottling log if needed
-            if (date('d') !== $this->lastGc) {
-                $this->doGc();
-            }
-        // Assertion request
-        } elseif (preg_match('#^' . $exp . '?([^><]+)(<|>)([^><]+)$#', $message, $m)) {
-            // Trim words
-            $word1 = strtolower($this->doCleanWord($m[1]));
-            $word2 = strtolower($this->doCleanWord($m[3]));
-            $operator = $m[2];
-
-            // Do nothing if the karma is fixed
-            if (isset($this->fixedKarma[$word1]) || isset($this->fixedKarma[$word2]) ||
-                empty($word1) || empty($word2)) {
-                return;
-            }
-
-            // Fetch first word
-            if ($word1 === '*' || $word1 === 'all' || $word1 === 'everything') {
-                $res = array('karma' => 0);
-                $word1 = 'everything';
-            } else {
-                $this->fetchKarma->execute(array(':word'=>$word1));
-                $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-            }
-            // If it exists, fetch second word
-            if ($res) {
-                if ($word2 === '*' || $word2 === 'all' || $word2 === 'everything') {
-                    $res2 = array('karma' => 0);
-                    $word2 = 'everything';
-                } else {
-                    $this->fetchKarma->execute(array(':word'=>$word2));
-                    $res2 = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-                }
-                // If it exists, compare and return value
-                if ($res2 && $res['karma'] != $res2['karma']) {
-                    $assertion = ($operator === '<' && $res['karma'] < $res2['karma']) || ($operator === '>' && $res['karma'] > $res2['karma']);
-                    // Switch arguments if they are in the wrong order
-                    if ($operator === '<') {
-                        $tmp = $word2;
-                        $word2 = $word1;
-                        $word1 = $tmp;
-                    }
-                    $this->doPrivmsg($source, $assertion ? $this->fetchPositiveAnswer($word1, $word2) : $this->fetchNegativeAnswer($word1, $word2));
-                    // If someone asserts that something is greater or lesser than everything, we increment/decrement that something at the same time
-                    if ($word2 === 'everything') {
-                        $this->event = clone$this->event;
-                        $this->event->setArguments(array($this->event->getArgument(0), '++'.$word1));
-                        $this->onPrivmsg();
-                    } elseif ($word1 === 'everything') {
-                        $this->event = clone$this->event;
-                        $this->event->setArguments(array($this->event->getArgument(0), '--'.$word2));
-                        $this->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);
         }
     }
 
-    protected function fetchPositiveAnswer($owner, $owned)
+    /**
+     * 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)
     {
-        return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->positiveAnswers[array_rand($this->positiveAnswers,1)]);
+        $source = $this->getEvent()->getSource();
+        $nick = $this->getEvent()->getNick();
+
+        if (empty($term)) {
+            return;
+        }
+
+        $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);
     }
 
-    protected function fetchNegativeAnswer($owned, $owner)
+    /**
+     * 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)
     {
-        return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->negativeAnswers[array_rand($this->negativeAnswers,1)]);
+        $data = array(
+            ':term' => $term,
+            ':karma' => 0
+        );
+        $this->updateKarma->execute($data);
     }
 
-    protected function doCleanWord($word)
+    /**
+     * 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)
     {
-        $word = trim($word);
-        if (substr($word, 0, 1) === '(' && substr($word, -1) === ')') {
-            $word = trim(substr($word, 1, -1));
+        $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
+            || empty($canonicalTerm0)
+            || empty($canonicalTerm1)
+        ) {
+            return;
+        }
+
+        if ($canonicalTerm0 == 'everything') {
+            $change = $method == '<' ? '++' : '--';
+            $this->modifyKarma($canonicalTerm1, $change);
+            $karma0 = 0;
+            $karma1 = $this->fetchKarma($canonicalTerm1);
+        } elseif ($canonicalTerm1 == 'everything') {
+            $change = $method == '<' ? '--' : '++';
+            $this->modifyKarma($canonicalTerm0, $change);
+            $karma0 = $this->fetchKarma($canonicalTerm1);
+            $karma1 = 0;
+        } else {
+            $karma0 = $this->fetchKarma($canonicalTerm0);
+            $karma1 = $this->fetchKarma($canonicalTerm1);
         }
-        $word = preg_replace('#\s+#', ' ', strtolower(trim($word)));
-        return $word;
+
+        if (($method == '<'
+            && $karma0 < $karma1)
+            || ($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);
     }
 
     /**
-     * Performs garbage collection on the antithrottling log.
+     * Modifes a term's karma.
+     *
+     * @param string $term   Term to modify
+     * @param string $action Karma action (either ++ or --)
      *
      * @return void
      */
-    public function doGc()
+    protected function modifyKarma($term, $action)
     {
-        unset($this->log);
-        $this->log = array();
-        $this->lastGc = date('d');
+        if (empty($term)) {
+            return;
+        }
+
+        $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);
+    }
+
+    /**
+     * 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'];
     }
 }