]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php
Revert "Merged in Phergie changes"
[quix0rs-gnu-social.git] / plugins / Irc / extlib / phergie / Phergie / Plugin / Karma.php
1 <?php
2 /**
3  * Phergie
4  *
5  * PHP version 5
6  *
7  * LICENSE
8  *
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
13  *
14  * @category  Phergie
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
20  */
21
22 /**
23  * Handles requests for incrementation or decrementation of a maintained list
24  * of counters for specified terms and antithrottling to prevent extreme
25  * inflation or depression of counters by any single individual.
26  *
27  * @category Phergie
28  * @package  Phergie_Plugin_Karma
29  * @author   Phergie Development Team <team@phergie.org>
30  * @license  http://phergie.org/license New BSD License
31  * @link     http://pear.phergie.org/package/Phergie_Plugin_Karma
32  */
33 class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
34 {
35     /**
36      * Stores the SQLite object
37      *
38      * @var resource
39      */
40     protected $db = null;
41
42     /**
43      * Retains the last garbage collection date
44      *
45      * @var array
46      */
47     protected $lastGc = null;
48
49     /**
50      * Logs the karma usages and limits users to one karma change per word
51      * and per day
52      *
53      * @return void
54      */
55     protected $log = array();
56
57     /**
58      * Some fixed karma values, keys must be lowercase
59      *
60      * @var array
61      */
62     protected $fixedKarma;
63
64     /**
65      * A list of blacklisted values
66      *
67      * @var array
68      */
69     protected $karmaBlacklist;
70
71     /**
72      * Answers for correct assertions
73      */
74     protected $positiveAnswers;
75
76     /**
77      * Answers for incorrect assertions
78      */
79     protected $negativeAnswers;
80
81     /**
82      * Prepared PDO statements
83      *
84      * @var PDOStatement
85      */
86     protected $insertKarma;
87     protected $updateKarma;
88     protected $fetchKarma;
89     protected $insertComment;
90
91     /**
92      * Connects to the database containing karma ratings and initializes
93      * class properties.
94      *
95      * @return void
96      */
97     public function onLoad()
98     {
99         $this->db = null;
100         $this->lastGc = null;
101         $this->log = array();
102
103         if(!defined('M_EULER')) {
104             define('M_EULER', '0.57721566490153286061');
105         }
106
107         $this->fixedKarma = array(
108             'phergie'      => '%s has karma of awesome',
109             'pi'           => '%s has karma of ' . M_PI,
110             'Π'            => '%s has karma of ' . M_PI,
111             'Ï€'            => '%s has karma of ' . M_PI,
112             'chucknorris'  => '%s has karma of Warning: Integer out of range',
113             'chuck norris' => '%s has karma of Warning: Integer out of range',
114             'c'            => '%s has karma of 299 792 458 m/s',
115             'e'            => '%s has karma of ' . M_E,
116             'euler'        => '%s has karma of ' . M_EULER,
117             'mole'         => '%s has karma of 6.02214e23 molecules',
118             'avogadro'     => '%s has karma of 6.02214e23 molecules',
119             'spoon'        => '%s has no karma. There is no spoon',
120             'mc^2'         => '%s has karma of E',
121             'mc2'          => '%s has karma of E',
122             'mc²'          => '%s has karma of E',
123             'i'            => '%s haz big karma',
124             '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.'
125         );
126
127         $this->karmaBlacklist = array(
128             '*',
129             'all',
130             'everything'
131         );
132
133         $this->positiveAnswers = array(
134             'No kidding, %owner% totally kicks %owned%\'s ass !',
135             'True that.',
136             'I concur.',
137             'Yay, %owner% ftw !',
138             '%owner% is made of WIN!',
139             'Nothing can beat %owner%!',
140         );
141
142         $this->negativeAnswers = array(
143             'No sir, not at all.',
144             'You\'re wrong dude, %owner% wins.',
145             'I\'d say %owner% is better than %owned%.',
146             'You must be joking, %owner% ftw!',
147             '%owned% is made of LOSE!',
148             '%owned% = Epic Fail',
149         );
150
151         // Load or initialize the database
152         $class = new ReflectionClass(get_class($this));
153         $dir = dirname($class->getFileName() . '/' . $this->name);
154         $this->db = new PDO('sqlite:' . $dir . 'karma.db');
155
156         // Check to see if the table exists
157         $table = $this->db->query('
158             SELECT COUNT(*)
159             FROM sqlite_master
160             WHERE name = ' . $this->db->quote('karmas')
161         )->fetchColumn();
162
163         // Create database tables if necessary
164         if (!$table) {
165             $this->db->query('
166                 CREATE TABLE karmas ( word VARCHAR ( 255 ), karma MEDIUMINT );
167                 CREATE UNIQUE INDEX word ON karmas ( word );
168                 CREATE INDEX karmaIndex ON karmas ( karma );
169                 CREATE TABLE comments ( wordid INT , comment VARCHAR ( 255 ) );
170                 CREATE INDEX wordidIndex ON comments ( wordid );
171                 CREATE UNIQUE INDEX commentUnique ON comments ( comment );
172             ');
173         }
174
175         $this->insertKarma = $this->db->prepare('
176             INSERT INTO karmas (
177                 word,
178                 karma
179             )
180             VALUES (
181                 :word,
182                 :karma
183             )
184         ');
185
186         $this->insertComment = $this->db->prepare('
187             INSERT INTO comments (
188                 wordid,
189                 comment
190             )
191             VALUES (
192                 :wordid,
193                 :comment
194             )
195         ');
196
197         $this->fetchKarma = $this->db->prepare('
198             SELECT karma, ROWID id FROM karmas WHERE LOWER(word) = LOWER(:word) LIMIT 1
199         ');
200
201         $this->updateKarma = $this->db->prepare('
202             UPDATE karmas SET karma = :karma WHERE LOWER(word) = LOWER(:word)
203         ');
204     }
205
206     /**
207      * Checks for dependencies.
208      *
209      * @return void
210      */
211     public static function onLoad()
212     {
213         if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
214             $this->fail('PDO and pdo_sqlite extensions must be installed');
215         }
216     }
217
218     /**
219      * Handles requests for incrementation, decrementation, or lookup of karma
220      * ratings sent via messages from users.
221      *
222      * @return void
223      */
224     public function onPrivmsg()
225     {
226         $source = $this->event->getSource();
227         $message = $this->event->getArgument(1);
228         $target = $this->event->getNick();
229
230         // Command prefix check
231         $prefix = preg_quote(trim($this->getConfig('command.prefix')));
232         $bot = preg_quote($this->getConfig('connections.nick'));
233         $exp = '(?:(?:' . $bot . '\s*[:,>]?\s+(?:' . $prefix . ')?)|(?:' . $prefix . '))';
234
235         // Karma status request
236         if (preg_match('#^' . $exp . 'karma\s+(.+)$#i', $message, $m)) {
237             // Return user's value if "me" is requested
238             if (strtolower($m[1]) === 'me') {
239                 $m[1] = $target;
240             }
241             // Clean the term
242             $term = $this->doCleanWord($m[1]);
243
244             // Check the blacklist
245             if (is_array($this->karmaBlacklist) && in_array($term, $this->karmaBlacklist)) {
246                 $this->doNotice($target, $term . ' is blacklisted');
247                 return;
248             }
249
250             // Return fixed value if set
251             if (isset($this->fixedKarma[$term])) {
252                 $this->doPrivmsg($source, $target . ': ' . sprintf($this->fixedKarma[$term], $m[1]) . '.');
253                 return;
254             }
255
256             // Return current karma or neutral if not set yet
257             $this->fetchKarma->execute(array(':word'=>$term));
258             $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
259
260             // Sanity check if someone if someone prefixed their conversation with karma
261             if (!$res && substr_count($term, ' ') > 1 && !(substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')')) {
262                 return;
263             }
264
265             // Clean the raw term if it was contained within brackets
266             if (substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')') {
267                 $m[1] = substr($m[1], 1, -1);
268             }
269
270             if ($res && $res['karma'] != 0) {
271                 $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has karma of ' . $res['karma'] . '.');
272             } else {
273                 $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has neutral karma.');
274             }
275         // Incrementation/decrementation request
276         } elseif (preg_match('{^' . $exp . '?(?:(\+{2,2}|-{2,2})(\S+?|\(.+?\)+)|(\S+?|\(.+?\)+)(\+{2,2}|-{2,2}))(?:\s+(.*))?$}ix', $message, $m)) {
277             if (!empty($m[4])) {
278                 $m[1] = $m[4]; // Increment/Decrement
279                 $m[2] = $m[3]; // Word
280             }
281             $m[3] = (isset($m[5]) ? $m[5] : null); // Comment
282             unset($m[4], $m[5]);
283             list(, $sign, $word, $comment) = array_pad($m, 4, null);
284
285             // Clean the word
286             $word = strtolower($this->doCleanWord($word));
287             if (empty($word)) {
288                 return;
289             }
290
291             // Do nothing if the karma is fixed or blacklisted
292             if (isset($this->fixedKarma[$word]) ||
293                 is_array($this->karmaBlacklist) && in_array($word, $this->karmaBlacklist)) {
294                 return;
295             }
296
297             // Force a decrementation if someone tries to update his own karma
298             if ($word == strtolower($target) && $sign != '--' && !$this->fromAdmin(true)) {
299                 $this->doNotice($target, 'Bad ' . $target . '! You can not modify your own Karma. Shame on you!');
300                 $sign = '--';
301             }
302
303             // Antithrottling check
304             $host = $this->event->getHost();
305             $limit = $this->getConfig('karma.limit');
306             // This is waiting on the Acl plugin from Elazar, being bypassed for now
307             //if ($limit > 0 && !$this->fromAdmin()) {
308             if ($limit > 0) {
309                 if (isset($this->log[$host][$word]) && $this->log[$host][$word] >= $limit) {
310                     // Three strikes, you're out, so lets decrement their karma for spammage
311                     if ($this->log[$host][$word] == ($limit+3)) {
312                         $this->doNotice($target, 'Bad ' . $target . '! Didn\'t I tell you that you reached your limit already?');
313                         $this->log[$host][$word] = $limit;
314                         $word = $target;
315                         $sign = '--';
316                     // Toss a notice to the user if they reached their limit
317                     } else {
318                         $this->doNotice($target, 'You have currently reached your limit in modifying ' . $word . ' for this day, please wait a bit.');
319                         $this->log[$host][$word]++;
320                         return;
321                     }
322                 } else {
323                     if (isset($this->log[$host][$word])) {
324                         $this->log[$host][$word]++;
325                     } else {
326                         $this->log[$host][$word] = 1;
327                     }
328                 }
329             }
330
331             // Get the current value then update or create entry
332             $this->fetchKarma->execute(array(':word'=>$word));
333             $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
334             if ($res) {
335                 $karma = ($res['karma'] + ($sign == '++' ? 1 : -1));
336                 $args = array(
337                     ':word' => $word,
338                     ':karma' => $karma
339                 );
340                 $this->updateKarma->execute($args);
341             } else {
342                 $karma = ($sign == '++' ? '1' : '-1');
343                 $args = array(
344                     ':word' => $word,
345                     ':karma' => $karma
346                 );
347                 $this->insertKarma->execute($args);
348                 $this->fetchKarma->execute(array(':word'=>$word));
349                 $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
350             }
351             $id = $res['id'];
352             // Add comment
353             $comment = preg_replace('{(?:^//(.*)|^#(.*)|^/\*(.*?)\*/$)}', '$1$2$3', $comment);
354             if (!empty($comment)) {
355                 $this->insertComment->execute(array(':wordid' => $id, ':comment' => $comment));
356             }
357             // Perform garbage collection on the antithrottling log if needed
358             if (date('d') !== $this->lastGc) {
359                 $this->doGc();
360             }
361         // Assertion request
362         } elseif (preg_match('#^' . $exp . '?([^><]+)(<|>)([^><]+)$#', $message, $m)) {
363             // Trim words
364             $word1 = strtolower($this->doCleanWord($m[1]));
365             $word2 = strtolower($this->doCleanWord($m[3]));
366             $operator = $m[2];
367
368             // Do nothing if the karma is fixed
369             if (isset($this->fixedKarma[$word1]) || isset($this->fixedKarma[$word2]) ||
370                 empty($word1) || empty($word2)) {
371                 return;
372             }
373
374             // Fetch first word
375             if ($word1 === '*' || $word1 === 'all' || $word1 === 'everything') {
376                 $res = array('karma' => 0);
377                 $word1 = 'everything';
378             } else {
379                 $this->fetchKarma->execute(array(':word'=>$word1));
380                 $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
381             }
382             // If it exists, fetch second word
383             if ($res) {
384                 if ($word2 === '*' || $word2 === 'all' || $word2 === 'everything') {
385                     $res2 = array('karma' => 0);
386                     $word2 = 'everything';
387                 } else {
388                     $this->fetchKarma->execute(array(':word'=>$word2));
389                     $res2 = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
390                 }
391                 // If it exists, compare and return value
392                 if ($res2 && $res['karma'] != $res2['karma']) {
393                     $assertion = ($operator === '<' && $res['karma'] < $res2['karma']) || ($operator === '>' && $res['karma'] > $res2['karma']);
394                     // Switch arguments if they are in the wrong order
395                     if ($operator === '<') {
396                         $tmp = $word2;
397                         $word2 = $word1;
398                         $word1 = $tmp;
399                     }
400                     $this->doPrivmsg($source, $assertion ? $this->fetchPositiveAnswer($word1, $word2) : $this->fetchNegativeAnswer($word1, $word2));
401                     // If someone asserts that something is greater or lesser than everything, we increment/decrement that something at the same time
402                     if ($word2 === 'everything') {
403                         $this->event = clone$this->event;
404                         $this->event->setArguments(array($this->event->getArgument(0), '++'.$word1));
405                         $this->onPrivmsg();
406                     } elseif ($word1 === 'everything') {
407                         $this->event = clone$this->event;
408                         $this->event->setArguments(array($this->event->getArgument(0), '--'.$word2));
409                         $this->onPrivmsg();
410                     }
411                 }
412             }
413         }
414     }
415
416     protected function fetchPositiveAnswer($owner, $owned)
417     {
418         return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->positiveAnswers[array_rand($this->positiveAnswers,1)]);
419     }
420
421     protected function fetchNegativeAnswer($owned, $owner)
422     {
423         return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->negativeAnswers[array_rand($this->negativeAnswers,1)]);
424     }
425
426     protected function doCleanWord($word)
427     {
428         $word = trim($word);
429         if (substr($word, 0, 1) === '(' && substr($word, -1) === ')') {
430             $word = trim(substr($word, 1, -1));
431         }
432         $word = preg_replace('#\s+#', ' ', strtolower(trim($word)));
433         return $word;
434     }
435
436     /**
437      * Performs garbage collection on the antithrottling log.
438      *
439      * @return void
440      */
441     public function doGc()
442     {
443         unset($this->log);
444         $this->log = array();
445         $this->lastGc = date('d');
446     }
447 }