2010-07-26 19:13:00 +01:00
< ? php
/**
* Phergie
*
* PHP version 5
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE .
* It is also available through the world - wide - web at this URL :
* http :// phergie . org / license
*
* @ category Phergie
* @ package Phergie_Plugin_Karma
* @ author Phergie Development Team < team @ phergie . org >
* @ 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_Karma
*/
/**
* Handles requests for incrementation or decrementation of a maintained list
2010-08-03 19:45:55 +01:00
* of counters for specified terms and antithrottling to prevent extreme
* inflation or depression of counters by any single individual .
2010-07-26 19:13:00 +01:00
*
* @ 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
*/
class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
{
/**
2010-08-03 19:45:55 +01:00
* Stores the SQLite object
2010-07-26 19:13:00 +01:00
*
* @ var resource
*/
protected $db = null ;
/**
2010-08-03 19:45:55 +01:00
* Retains the last garbage collection date
2010-07-26 19:13:00 +01:00
*
2010-08-03 19:45:55 +01:00
* @ var array
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
protected $lastGc = null ;
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* Logs the karma usages and limits users to one karma change per word
* and per day
2010-07-26 19:13:00 +01:00
*
2010-08-03 19:45:55 +01:00
* @ return void
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
protected $log = array ();
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* Some fixed karma values , keys must be lowercase
2010-07-26 19:13:00 +01:00
*
2010-08-03 19:45:55 +01:00
* @ var array
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
protected $fixedKarma ;
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* A list of blacklisted values
2010-07-26 19:13:00 +01:00
*
2010-08-03 19:45:55 +01:00
* @ var array
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
protected $karmaBlacklist ;
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* Answers for correct assertions
*/
protected $positiveAnswers ;
/**
* Answers for incorrect assertions
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
protected $negativeAnswers ;
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* Prepared PDO statements
2010-07-26 19:13:00 +01:00
*
* @ var PDOStatement
*/
2010-08-03 19:45:55 +01:00
protected $insertKarma ;
protected $updateKarma ;
protected $fetchKarma ;
protected $insertComment ;
2010-07-26 19:13:00 +01:00
/**
2010-08-03 19:45:55 +01:00
* Connects to the database containing karma ratings and initializes
* class properties .
2010-07-26 19:13:00 +01:00
*
* @ return void
*/
public function onLoad ()
{
2010-08-03 19:45:55 +01:00
$this -> db = null ;
$this -> lastGc = null ;
$this -> log = array ();
2010-07-26 19:13:00 +01:00
2010-08-03 19:45:55 +01:00
if ( ! defined ( 'M_EULER' )) {
define ( 'M_EULER' , '0.57721566490153286061' );
}
2010-07-26 19:13:00 +01:00
2010-08-03 19:45:55 +01:00
$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.'
);
2010-07-26 19:13:00 +01:00
2010-08-03 19:45:55 +01:00
$this -> karmaBlacklist = array (
'*' ,
'all' ,
'everything'
);
2010-07-26 19:13:00 +01:00
2010-08-03 19:45:55 +01:00
$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 -> insertKarma = $this -> db -> prepare ( '
INSERT INTO karmas (
word ,
karma
)
VALUES (
: word ,
: karma
)
2010-07-26 19:13:00 +01:00
' );
2010-08-03 19:45:55 +01:00
$this -> insertComment = $this -> db -> prepare ( '
INSERT INTO comments (
wordid ,
comment
)
VALUES (
: wordid ,
: comment
)
2010-07-26 19:13:00 +01:00
' );
2010-08-03 19:45:55 +01:00
$this -> fetchKarma = $this -> db -> prepare ( '
SELECT karma , ROWID id FROM karmas WHERE LOWER ( word ) = LOWER ( : word ) LIMIT 1
2010-08-03 17:42:02 +01:00
' );
2010-08-03 19:45:55 +01:00
$this -> updateKarma = $this -> db -> prepare ( '
UPDATE karmas SET karma = : karma WHERE LOWER ( word ) = LOWER ( : word )
2010-07-26 19:13:00 +01:00
' );
}
/**
2010-08-03 19:45:55 +01:00
* Checks for dependencies .
2010-07-26 19:13:00 +01:00
*
2010-08-03 19:45:55 +01:00
* @ return void
2010-07-26 19:13:00 +01:00
*/
2010-08-03 19:45:55 +01:00
public static function onLoad ()
2010-07-26 19:13:00 +01:00
{
2010-08-03 19:45:55 +01:00
if ( ! extension_loaded ( 'PDO' ) || ! extension_loaded ( 'pdo_sqlite' )) {
$this -> fail ( 'PDO and pdo_sqlite extensions must be installed' );
}
2010-07-26 19:13:00 +01:00
}
/**
2010-08-03 19:45:55 +01:00
* Handles requests for incrementation , decrementation , or lookup of karma
* ratings sent via messages from users .
2010-07-26 19:13:00 +01:00
*
* @ return void
*/
public function onPrivmsg ()
{
2010-08-03 19:45:55 +01:00
$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 ();
}
}
}
2010-07-26 19:13:00 +01:00
}
}
2010-08-03 19:45:55 +01:00
protected function fetchPositiveAnswer ( $owner , $owned )
2010-07-26 19:13:00 +01:00
{
2010-08-03 19:45:55 +01:00
return str_replace ( array ( '%owner%' , '%owned%' ), array ( $owner , $owned ), $this -> positiveAnswers [ array_rand ( $this -> positiveAnswers , 1 )]);
2010-07-26 19:13:00 +01:00
}
2010-08-03 19:45:55 +01:00
protected function fetchNegativeAnswer ( $owned , $owner )
2010-07-26 19:13:00 +01:00
{
2010-08-03 19:45:55 +01:00
return str_replace ( array ( '%owner%' , '%owned%' ), array ( $owner , $owned ), $this -> negativeAnswers [ array_rand ( $this -> negativeAnswers , 1 )]);
2010-07-26 19:13:00 +01:00
}
2010-08-03 19:45:55 +01:00
protected function doCleanWord ( $word )
2010-07-26 19:13:00 +01:00
{
2010-08-03 19:45:55 +01:00
$word = trim ( $word );
if ( substr ( $word , 0 , 1 ) === '(' && substr ( $word , - 1 ) === ')' ) {
$word = trim ( substr ( $word , 1 , - 1 ));
2010-07-26 19:13:00 +01:00
}
2010-08-03 19:45:55 +01:00
$word = preg_replace ( '#\s+#' , ' ' , strtolower ( trim ( $word )));
return $word ;
2010-07-26 19:13:00 +01:00
}
/**
2010-08-03 19:45:55 +01:00
* Performs garbage collection on the antithrottling log .
2010-07-26 19:13:00 +01:00
*
* @ return void
*/
2010-08-03 19:45:55 +01:00
public function doGc ()
2010-07-26 19:13:00 +01:00
{
2010-08-03 19:45:55 +01:00
unset ( $this -> log );
$this -> log = array ();
$this -> lastGc = date ( 'd' );
2010-07-26 19:13:00 +01:00
}
}