Merged in Phergie changes

This commit is contained in:
Luke Fitzgerald 2010-08-03 09:42:02 -07:00
parent 9b9a36e492
commit a3df3b6367
30 changed files with 1546 additions and 719 deletions

View File

@ -0,0 +1,81 @@
<?php
if (!defined('__DIR__')) {
define('__DIR__', dirname(__FILE__));
}
// Create database schema
echo 'Creating database', PHP_EOL;
$file = __DIR__ . '/beer.db';
if (file_exists($file)) {
unlink($file);
}
$db = new PDO('sqlite:' . $file);
$db->exec('CREATE TABLE beer (name VARCHAR(255), link VARCHAR(255))');
$db->exec('CREATE UNIQUE INDEX beer_name ON beer (name)');
$insert = $db->prepare('INSERT INTO beer (name, link) VALUES (:name, :link)');
// Get raw beerme.com data set
echo 'Downloading beerme.com data set', PHP_EOL;
$file = __DIR__ . '/beerlist.txt';
if (!file_exists($file)) {
copy('http://beerme.com/beerlist.php', $file);
}
$contents = file_get_contents($file);
// Extract data from data set
echo 'Processing beerme.com data', PHP_EOL;
$contents = tidy_repair_string($contents);
libxml_use_internal_errors(true);
$doc = new DOMDocument;
$doc->loadHTML($contents);
libxml_clear_errors();
$xpath = new DOMXPath($doc);
$beers = $xpath->query('//table[@class="beerlist"]/tr/td[1]');
$db->beginTransaction();
foreach ($beers as $beer) {
$name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $beer->textContent);
$name = preg_replace('/\h*\v+\h*/', '', $name);
$link = 'http://beerme.com' . $beer->childNodes->item(1)->getAttribute('href');
$insert->execute(array($name, $link));
}
$db->commit();
// Clean up
echo 'Cleaning up', PHP_EOL;
unlink($file);
// Get and decompress openbeerdb.com data set
$archive = __DIR__ . '/beers.zip';
if (!file_exists($archive)) {
echo 'Downloading openbeerdb.com data set', PHP_EOL;
copy('http://openbeerdb.googlecode.com/files/beers.zip', $archive);
}
echo 'Decompressing openbeerdb.com data set', PHP_EOL;
$zip = new ZipArchive;
$zip->open($archive);
$zip->extractTo(__DIR__, 'beers/beers.csv');
$zip->close();
$file = __DIR__ . '/beers/beers.csv';
// Extract data from data set
echo 'Processing openbeerdb.com data', PHP_EOL;
$fp = fopen($file, 'r');
$columns = fgetcsv($fp, 0, '|');
$db->beginTransaction();
while ($line = fgetcsv($fp, 0, '|')) {
$line = array_combine($columns, $line);
$name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $line['name']);
$name = preg_replace('/\h*\v+\h*/', '', $name);
$link = null;
$insert->execute(array($name, $link));
}
$db->commit();
fclose($fp);
// Clean up
echo 'Cleaning up', PHP_EOL;
unlink($file);
unlink($archive);
rmdir(__DIR__ . '/beers');

View File

@ -254,7 +254,7 @@ class Phergie_Connection
if (!in_array($this->encoding, mb_list_encodings())) { if (!in_array($this->encoding, mb_list_encodings())) {
throw new Phergie_Connection_Exception( throw new Phergie_Connection_Exception(
'Encoding ' . $this->encoding . ' is not supported', 'Encoding ' . $this->encoding . ' is not supported',
Phergie_Connection_Exception::ENCODING_NOT_SUPPORTED Phergie_Connection_Exception::ERR_ENCODING_NOT_SUPPORTED
); );
} }

View File

@ -41,4 +41,10 @@ class Phergie_Connection_Exception extends Phergie_Exception
* but that transport is not supported by the current PHP installation * but that transport is not supported by the current PHP installation
*/ */
const ERR_TRANSPORT_NOT_SUPPORTED = 2; const ERR_TRANSPORT_NOT_SUPPORTED = 2;
/**
* Error indicating that a connection is configured to use an encoding,
* but that encoding is not supported by the current PHP installation
*/
const ERR_ENCODING_NOT_SUPPORTED = 3;
} }

View File

@ -243,6 +243,7 @@ class Phergie_Driver_Streams extends Phergie_Driver_Abstract
// Parse the command and arguments // Parse the command and arguments
list($cmd, $args) = array_pad(explode(' ', $buffer, 2), 2, null); list($cmd, $args) = array_pad(explode(' ', $buffer, 2), 2, null);
$hostmask = new Phergie_Hostmask(null, null, $this->connection->getHost());
} else { } else {
// If the event could be from the server or a user... // If the event could be from the server or a user...

View File

@ -23,11 +23,45 @@
* Provides an access control system to limit reponses to events based on * Provides an access control system to limit reponses to events based on
* the users who originate them. * the users who originate them.
* *
* Configuration settings:
* acl.whitelist - mapping of user hostmask patterns (optionally by host) to
* plugins and methods where those plugins and methods will
* only be accessible to those users (i.e. and inaccessible
* to other users)
* acl.blacklist - mapping of user hostmasks (optionally by host) to plugins
* and methods where where those plugins and methods will be
* inaccessible to those users but accessible to other users
* acl.ops - TRUE to automatically give access to whitelisted plugins
* and methods to users with ops for events they initiate in
* channels where they have ops
*
* The whitelist and blacklist settings are formatted like so:
* <code>
* 'acl.whitelist' => array(
* 'hostname1' => array(
* 'pattern1' => array(
* 'plugins' => array(
* 'ShortPluginName'
* ),
* 'methods' => array(
* 'methodName'
* )
* ),
* )
* ),
* </code>
*
* The hostname array dimension is optional; if not used, rules will be
* applied across all connections. The pattern is a user hostmask pattern
* where asterisks (*) are used for wildcards. Plugins and methods do not
* need to be set to empty arrays if they are not used; simply exclude them.
*
* @category Phergie * @category Phergie
* @package Phergie_Plugin_Acl * @package Phergie_Plugin_Acl
* @author Phergie Development Team <team@phergie.org> * @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License * @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Acl * @link http://pear.phergie.org/package/Phergie_Plugin_Acl
* @uses Phergie_Plugin_UserInfo pear.phergie.org
*/ */
class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract
{ {
@ -38,6 +72,8 @@ class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract
*/ */
public function onLoad() public function onLoad()
{ {
$this->plugins->getPlugin('UserInfo');
if (!$this->getConfig('acl.blacklist') if (!$this->getConfig('acl.blacklist')
&& !$this->getConfig('acl.whitelist') && !$this->getConfig('acl.whitelist')
) { ) {
@ -45,48 +81,106 @@ class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract
} }
} }
/**
* Applies a set of rules to a plugin handler iterator.
*
* @param Phergie_Plugin_Iterator $iterator Iterator to receive rules
* @param array $rules Associate array containing
* either a 'plugins' key pointing to an array containing plugin
* short names to filter, a 'methods' key pointing to an array
* containing method names to filter, or both
*
* @return void
*/
protected function applyRules(Phergie_Plugin_Iterator $iterator, array $rules)
{
if (!empty($rules['plugins'])) {
$iterator->addPluginFilter($rules['plugins']);
}
if (!empty($rules['methods'])) {
$iterator->addMethodFilter($rules['methods']);
}
}
/** /**
* Checks permission settings and short-circuits event processing for * Checks permission settings and short-circuits event processing for
* blacklisted users. * blacklisted users.
* *
* @return bool FALSE to short-circuit event processing if the user is * @return void
* blacklisted, TRUE otherwise
*/ */
public function preEvent() public function preEvent()
{ {
// Ignore server responses // Ignore server responses
if ($this->event instanceof Phergie_Event_Response) { if ($this->event instanceof Phergie_Event_Response) {
return true; return;
} }
// Ignore server-initiated events // Ignore server-initiated events
if (!$this->event->isFromUser()) { if (!$this->event->isFromUser()) {
return true; return;
} }
// Determine whether a whitelist or blacklist is being used // Get the iterator used to filter plugins when processing events
$list = $this->getConfig('acl.whitelist'); $iterator = $this->plugins->getIterator();
$matches = true;
if (!$list) { // Get configuration setting values
$list = $this->getConfig('acl.blacklist'); $whitelist = $this->getConfig('acl.whitelist', array());
$matches = false; $blacklist = $this->getConfig('acl.blacklist', array());
} $ops = $this->getConfig('acl.ops', false);
// Support host-specific lists // Support host-specific lists
$host = $this->connection->getHost(); $host = $this->connection->getHost();
if (isset($list[$host])) { foreach (array('whitelist', 'blacklist') as $var) {
$list = $list[$host]; foreach ($$var as $pattern => $rules) {
$regex = '/^' . str_replace('*', '.*', $pattern) . '$/i';
if (preg_match($regex, $host)) {
${$var} = ${$var}[$pattern];
break;
}
}
} }
// Short-circuit event processing if appropriate // Get information on the user initiating the current event
$hostmask = $this->event->getHostmask(); $hostmask = $this->event->getHostmask();
foreach ($list as $pattern) { $isOp = $ops
&& $this->event->isInChannel()
&& $this->plugins->userInfo->isOp(
$this->event->getNick(),
$this->event->getSource()
);
// Filter whitelisted commands if the user is not on the whitelist
if (!$isOp) {
$whitelisted = false;
foreach ($whitelist as $pattern => $rules) {
if ($hostmask->matches($pattern)) { if ($hostmask->matches($pattern)) {
return $matches; $whitelisted = true;
}
}
if (!$whitelisted) {
foreach ($whitelist as $pattern => $rules) {
$this->applyRules($iterator, $rules);
}
} }
} }
// Allow event processing if appropriate // Filter blacklisted commands if the user is on the blacklist
return !$matches; $blacklisted = false;
foreach ($blacklist as $pattern => $rules) {
if ($hostmask->matches($pattern)) {
$this->applyRules($iterator, $rules);
break;
}
}
}
/**
* Clears filters on the plugin handler iterator.
*
* @return void
*/
public function postDispatch()
{
$this->plugins->getIterator()->clearFilters();
} }
} }

View File

@ -0,0 +1,69 @@
<?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_Caffeine
* @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_Caffeine
*/
/**
* Processes requests to serve users caffeinated beverages.
*
* @category Phergie
* @package Phergie_Plugin_Caffeine
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Caffeine
* @uses Phergie_Plugin_Command pear.phergie.org
* @uses Phergie_Plugin_Serve pear.phergie.org
*/
class Phergie_Plugin_Caffeine extends Phergie_Plugin_Abstract
{
/**
* Checks for dependencies.
*
* @return void
*/
public function onLoad()
{
$plugins = $this->plugins;
$plugins->getPlugin('Command');
$plugins->getPlugin('Serve');
}
/**
* Processes requests to serve a user a caffeinated beverage.
*
* @param string $request Request including the target and an optional
* suggestion of what caffeinated beverage to serve
*
* @return void
*/
public function onCommandCaffeine($request)
{
$format = $this->getConfig(
'beer.format',
'throws %target% %article% %item%.'
);
$this->plugins->getPlugin('Serve')->serve(
dirname(__FILE__) . '/Caffeine/caffeine.db',
'caffeine',
$format,
$request
);
}
}

View File

@ -0,0 +1,51 @@
<?php
if (!defined('__DIR__')) {
define('__DIR__', dirname(__FILE__));
}
// Create database schema
echo 'Creating database', PHP_EOL;
$file = __DIR__ . '/caffeine.db';
if (file_exists($file)) {
unlink($file);
}
$db = new PDO('sqlite:' . $file);
$db->exec('CREATE TABLE caffeine (name VARCHAR(255), link VARCHAR(255))');
$db->exec('CREATE UNIQUE INDEX caffeine_name ON caffeine (name)');
$insert = $db->prepare('INSERT INTO caffeine (name, link) VALUES (:name, :link)');
// Get raw energyfiend.com data set
echo 'Downloading energyfiend.com data set', PHP_EOL;
$file = __DIR__ . '/the-caffeine-database.html';
if (!file_exists($file)) {
copy('http://www.energyfiend.com/the-caffeine-database', $file);
}
$contents = file_get_contents($file);
// Extract data from data set
echo 'Processing energyfiend.com data', PHP_EOL;
$contents = tidy_repair_string($contents);
libxml_use_internal_errors(true);
$doc = new DOMDocument;
$doc->loadHTML($contents);
libxml_clear_errors();
$xpath = new DOMXPath($doc);
$caffeine = $xpath->query('//table[@id="caffeinedb"]//tr/td[1]');
$db->beginTransaction();
foreach ($caffeine as $drink) {
$name = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $drink->textContent);
$name = preg_replace('/\s*\v+\s*/', ' ', $name);
if ($drink->firstChild->nodeName == 'a') {
$link = 'http://energyfiend.com'
. $drink->firstChild->getAttribute('href');
} else {
$link = null;
}
$insert->execute(array($name, $link));
}
$db->commit();
// Clean up
echo 'Cleaning up', PHP_EOL;
unlink($file);

View File

@ -29,9 +29,17 @@
* @license http://phergie.org/license New BSD License * @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Command * @link http://pear.phergie.org/package/Phergie_Plugin_Command
* @uses extension reflection * @uses extension reflection
* @uses Phergie_Plugin_Message pear.phergie.org
*/ */
class Phergie_Plugin_Command extends Phergie_Plugin_Abstract class Phergie_Plugin_Command extends Phergie_Plugin_Abstract
{ {
/**
* Prefix for command method names
*
* @var string
*/
const METHOD_PREFIX = 'onCommand';
/** /**
* Cache for command lookups used to confirm that methods exist and * Cache for command lookups used to confirm that methods exist and
* parameter counts match * parameter counts match
@ -41,24 +49,28 @@ class Phergie_Plugin_Command extends Phergie_Plugin_Abstract
protected $methods = array(); protected $methods = array();
/** /**
* Prefix for command method names * Load the Message plugin
* *
* @var string * @return void
*/ */
protected $methodPrefix = 'onCommand'; public function onLoad()
{
$plugins = $this->getPluginHandler();
$plugins->getPlugin('Message');
}
/** /**
* Populates the methods cache. * Populates the methods cache.
* *
* @return void * @return void
*/ */
protected function populateMethodCache() public function populateMethodCache()
{ {
foreach ($this->getPluginHandler() as $plugin) { foreach ($this->getPluginHandler() as $plugin) {
$reflector = new ReflectionClass($plugin); $reflector = new ReflectionClass($plugin);
foreach ($reflector->getMethods() as $method) { foreach ($reflector->getMethods() as $method) {
$name = $method->getName(); $name = $method->getName();
if (strpos($name, $this->methodPrefix) === 0 if (strpos($name, self::METHOD_PREFIX) === 0
&& !isset($this->methods[$name]) && !isset($this->methods[$name])
) { ) {
$this->methods[$name] = array( $this->methods[$name] = array(
@ -84,26 +96,26 @@ class Phergie_Plugin_Command extends Phergie_Plugin_Abstract
$this->populateMethodCache(); $this->populateMethodCache();
} }
// Get the content of the message // Check for a prefixed message
$event = $this->getEvent(); $msg = $this->plugins->message->getMessage();
$msg = trim($event->getText()); if ($msg === false) {
$prefix = $this->getConfig('command.prefix');
// Check for the command prefix if one is set and needed
if ($prefix && $event->isInChannel()) {
if (strpos($msg, $prefix) !== 0) {
return; return;
} else {
$msg = substr($msg, strlen($prefix));
}
} }
// Separate the command and arguments // Separate the command and arguments
$parsed = preg_split('/\s+/', $msg, 2); $parsed = preg_split('/\s+/', $msg, 2);
$method = $this->methodPrefix . ucfirst(strtolower(array_shift($parsed))); $command = strtolower(array_shift($parsed));
$args = count($parsed) ? array_shift($parsed) : ''; $args = count($parsed) ? array_shift($parsed) : '';
// Resolve aliases to their corresponding commands
$aliases = $this->getConfig('command.aliases', array());
$result = preg_grep('/^' . $command . '$/i', array_keys($aliases));
if ($result) {
$command = $aliases[array_shift($result)];
}
// Check to ensure the command exists // Check to ensure the command exists
$method = self::METHOD_PREFIX . ucfirst($command);
if (empty($this->methods[$method])) { if (empty($this->methods[$method])) {
return; return;
} }

View File

@ -36,9 +36,6 @@ $cookies = $xpath->query('//table[@width="90%"]/tr/td[1]/a');
foreach ($cookies as $cookie) { foreach ($cookies as $cookie) {
$name = $cookie->textContent; $name = $cookie->textContent;
foreach (range(0, mb_strlen($name) - 1) as $index) {
echo mb_strcut($name, $index, 1), PHP_EOL;
}
$name = str_replace( $name = str_replace(
array('(',')',"\n", 'cookies'), array('(',')',"\n", 'cookies'),
array('','', ' ', 'cookie'), array('','', ' ', 'cookie'),

View File

@ -49,11 +49,7 @@ class Phergie_Plugin_Daddy extends Phergie_Plugin_Abstract
= '/' . preg_quote($prefix) . = '/' . preg_quote($prefix) .
'\s*?who\'?s y(?:our|a) ([^?]+)\??/iAD'; '\s*?who\'?s y(?:our|a) ([^?]+)\??/iAD';
if (preg_match($pattern, $text, $m)) { if (preg_match($pattern, $text, $m)) {
if ($config['daddy.curses'] && mt_rand(0, 5) === 5) {
$msg = $target . ': I am your ' . $m[1] . ', bitch!';
} else {
$msg = 'You\'re my ' . $m[1] . ', ' . $target . '!'; $msg = 'You\'re my ' . $m[1] . ', ' . $target . '!';
}
$this->doPrivmsg($source, $msg); $this->doPrivmsg($source, $msg);
} }
} }

View File

@ -32,24 +32,9 @@
* @uses Phergie_Plugin_Command pear.phergie.org * @uses Phergie_Plugin_Command pear.phergie.org
* @uses Phergie_Plugin_Http pear.phergie.org * @uses Phergie_Plugin_Http pear.phergie.org
* @uses Phergie_Plugin_Temperature pear.phergie.org * @uses Phergie_Plugin_Temperature pear.phergie.org
*
* @pluginDesc Provide access to some Google services
*/ */
class Phergie_Plugin_Google extends Phergie_Plugin_Abstract class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
{ {
/**
* HTTP plugin
*
* @var Phergie_Plugin_Http
*/
protected $http;
/**
* Language for Google Services
*/
protected $lang;
/** /**
* Checks for dependencies. * Checks for dependencies.
* *
@ -59,11 +44,8 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
{ {
$plugins = $this->getPluginHandler(); $plugins = $this->getPluginHandler();
$plugins->getPlugin('Command'); $plugins->getPlugin('Command');
$this->http = $plugins->getPlugin('Http'); $plugins->getPlugin('Http');
$plugins->getPlugin('Help')->register($this);
$plugins->getPlugin('Weather'); $plugins->getPlugin('Weather');
$this->lang = $this->getConfig('google.lang', 'en');
} }
/** /**
@ -73,8 +55,6 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* *
* @return void * @return void
* @todo Implement use of URL shortening here * @todo Implement use of URL shortening here
*
* @pluginCmd [query] do a search on google
*/ */
public function onCommandG($query) public function onCommandG($query)
{ {
@ -83,7 +63,7 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
'v' => '1.0', 'v' => '1.0',
'q' => $query 'q' => $query
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$json = $response->getContent()->responseData; $json = $response->getContent()->responseData;
$event = $this->getEvent(); $event = $this->getEvent();
$source = $event->getSource(); $source = $event->getSource();
@ -110,8 +90,6 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* @param string $query Search term * @param string $query Search term
* *
* @return void * @return void
*
* @pluginCmd [query] Do a search on Google and count the results
*/ */
public function onCommandGc($query) public function onCommandGc($query)
{ {
@ -120,7 +98,7 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
'v' => '1.0', 'v' => '1.0',
'q' => $query 'q' => $query
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$json = $response->getContent()->responseData->cursor; $json = $response->getContent()->responseData->cursor;
$count = $json->estimatedResultCount; $count = $json->estimatedResultCount;
$event = $this->getEvent(); $event = $this->getEvent();
@ -146,8 +124,6 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* @param string $query Term to translate * @param string $query Term to translate
* *
* @return void * @return void
*
* @pluginCmd [from language] [to language] [text to translate] Do a translation on Google
*/ */
public function onCommandGt($from, $to, $query) public function onCommandGt($from, $to, $query)
{ {
@ -157,7 +133,7 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
'q' => $query, 'q' => $query,
'langpair' => $from . '|' . $to 'langpair' => $from . '|' . $to
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$json = $response->getContent(); $json = $response->getContent();
$event = $this->getEvent(); $event = $this->getEvent();
$source = $event->getSource(); $source = $event->getSource();
@ -180,18 +156,16 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* between 0 and 3 to get the forecast * between 0 and 3 to get the forecast
* *
* @return void * @return void
*
* @pluginCmd [location] Show the weather for the specified location
*/ */
public function onCommandGw($location, $offset = null) public function onCommandGw($location, $offset = null)
{ {
$url = 'http://www.google.com/ig/api'; $url = 'http://www.google.com/ig/api';
$params = array( $params = array(
'weather' => $location, 'weather' => $location,
'hl' => $this->lang, 'hl' => $this->getConfig('google.lang', 'en'),
'oe' => 'UTF-8' 'oe' => 'UTF-8'
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$xml = $response->getContent()->weather; $xml = $response->getContent()->weather;
$event = $this->getEvent(); $event = $this->getEvent();
@ -280,8 +254,6 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* @param string $location Location to search for * @param string $location Location to search for
* *
* @return void * @return void
*
* @pluginCmd [location] Get the location from Google Maps to the location specified
*/ */
public function onCommandGmap($location) public function onCommandGmap($location)
{ {
@ -294,13 +266,13 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
$params = array( $params = array(
'q' => $location, 'q' => $location,
'output' => 'json', 'output' => 'json',
'gl' => $this->lang, 'gl' => $this->getConfig('google.lang', 'en'),
'sensor' => 'false', 'sensor' => 'false',
'oe' => 'utf8', 'oe' => 'utf8',
'mrt' => 'all', 'mrt' => 'all',
'key' => $this->getConfig('google.key') 'key' => $this->getConfig('google.key')
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$json = $response->getContent(); $json = $response->getContent();
if (!empty($json)) { if (!empty($json)) {
$qtd = count($json->Placemark); $qtd = count($json->Placemark);
@ -345,8 +317,6 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* @param string $to Destination metric * @param string $to Destination metric
* *
* @return void * @return void
*
* @pluginCmd [value] [currency from] [currency to] Converts a monetary value from one currency to another
*/ */
public function onCommandGconvert($value, $from, $to) public function onCommandGconvert($value, $from, $to)
{ {
@ -356,30 +326,22 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
'from' => $from, 'from' => $from,
'to' => $to 'to' => $to
); );
$response = $this->http->get($url, $params); $response = $this->plugins->http->get($url, $params);
$contents = $response->getContent(); $contents = $response->getContent();
$event = $this->getEvent(); $event = $this->getEvent();
$source = $event->getSource(); $source = $event->getSource();
$nick = $event->getNick(); $nick = $event->getNick();
if ($contents) { if ($contents) {
preg_match( libxml_use_internal_errors(true);
'#<span class=bld>.*? ' . $to . '</span>#im', $doc = new DOMDocument;
$contents, $doc->loadHTML($contents);
$matches libxml_clear_errors();
); $xpath = new DOMXPath($doc);
if (!$matches[0]) { $result = $xpath->query('//div[@id="currency_converter_result"]');
$this->doPrivmsg($source, $nick . ', I can\'t do that.'); $div = $result->item(0);
} else { $text = rtrim($div->textContent);
$str = str_replace('<span class=bld>', '', $matches[0]);
$str = str_replace($to . '</span>', '', $str);
$text
= number_format($value, 2, ',', '.') . ' ' . $from .
' => ' . number_format($str, 2, ',', '.') . ' ' . $to;
$this->doPrivmsg($source, $text); $this->doPrivmsg($source, $text);
} }
} else {
$this->doPrivmsg($source, $nick . ', we had a problem.');
}
} }
/** /**
@ -395,7 +357,7 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
public function onCommandConvert($query) public function onCommandConvert($query)
{ {
$url = 'http://www.google.com/search?q=' . urlencode($query); $url = 'http://www.google.com/search?q=' . urlencode($query);
$response = $this->http->get($url); $response = $this->plugins->http->get($url);
$contents = $response->getContent(); $contents = $response->getContent();
$event = $this->getEvent(); $event = $this->getEvent();
$source = $event->getSource(); $source = $event->getSource();
@ -434,48 +396,60 @@ class Phergie_Plugin_Google extends Phergie_Plugin_Abstract
* *
* @return void * @return void
* @todo Implement use of URL shortening here * @todo Implement use of URL shortening here
*
* @pluginCmd [query] do a search of a definition on Google Dictionary
*/ */
public function onCommandDefine($query) public function onCommandDefine($query)
{ {
$query = urlencode($query); $lang = $this->getConfig('google.lang', 'en');
$url = 'http://www.google.com/dictionary/json?callback=result'. $url = 'http://www.google.com/dictionary/json';
'&q='.$query.'&sl='.$this->lang.'&tl='.$this->lang. $params = array(
'&restrict=pr,de'; 'callback' => 'result',
$json = file_get_contents($url); 'q' => $query,
'sl' => $lang,
'tl' => $lang,
'restrict' => 'pr,de'
);
$response = $this->plugins->http->get($url, $params);
$json = $response->getContent();
//Remove some garbage from the json // Remove some garbage from the JSON and decode it
$json = str_replace(array("result(", ",200,null)"), "", $json); $json = str_replace(array('result(', ',200,null)'), '', $json);
//Awesome workaround to remove a lot of slashes from json
$json = str_replace('"', '¿?¿', $json); $json = str_replace('"', '¿?¿', $json);
$json = strip_tags(stripcslashes($json)); $json = strip_tags(stripcslashes($json));
$json = str_replace('"', "'", $json); $json = str_replace('"', "'", $json);
$json = str_replace('¿?¿', '"', $json); $json = str_replace('¿?¿', '"', $json);
$json = json_decode($json); $json = json_decode($json);
$event = $this->getEvent(); $event = $this->getEvent();
$source = $event->getSource(); $source = $event->getSource();
$nick = $event->getNick(); $nick = $event->getNick();
if (!empty($json->webDefinitions)) { if (!empty($json->webDefinitions)) {
$results = count($json->webDefinitions[0]->entries); $results = 0;
$more = $results > 1 ? ($results-1).' ' : NULL; foreach ($json->primaries[0]->entries as $entry) {
$lang_code = substr($this->lang, 0, 2); if ($entry->type == 'meaning') {
$msg = $results++;
$nick . ': ' . if (empty($text)) {
$json->webDefinitions[0]->entries[0]->terms[0]->text . foreach ($entry->terms as $term) {
' - You can find more '.$more.'results at '. if ($term->type == 'text') {
'http://www.google.com/dictionary?aq=f&langpair='. $text = trim($term->text);
$lang_code.'%7C'.$lang_code.'&q='.$query.'&hl='.$lang_code; }
}
}
}
}
$more = $results > 1 ? ($results - 1) . ' ' : '';
$lang_code = substr($lang, 0, 2);
$msg = $nick . ': ' . $text
. ' - You can find ' . $more . 'more results at '
. 'http://www.google.com/dictionary'
. '?aq=f'
. '&langpair=' . $lang_code . '%7C' . $lang_code
. '&q=' . $query
. '&hl=' . $lang_code;
$this->doPrivmsg($source, $msg); $this->doPrivmsg($source, $msg);
} else { } else {
if ($this->lang != 'en'){ if ($lang != 'en'){
$temp = $this->lang; $lang = 'en';
$this->lang = 'en';
$this->onCommandDefine($query); $this->onCommandDefine($query);
$this->lang = $temp;
} else { } else {
$msg = $nick . ': No results for this query.'; $msg = $nick . ': No results for this query.';
$this->doPrivmsg($source, $msg); $this->doPrivmsg($source, $msg);

View File

@ -68,6 +68,14 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
*/ */
protected $events; protected $events;
/**
* Iterator used for selectively proxying method calls to contained
* plugins
*
* @var Iterator
*/
protected $iterator;
/** /**
* Constructor to initialize class properties and add the path for core * Constructor to initialize class properties and add the path for core
* plugins. * plugins.
@ -416,26 +424,39 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
*/ */
public function getIterator() public function getIterator()
{ {
return new ArrayIterator($this->plugins); if (empty($this->iterator)) {
$this->iterator = new Phergie_Plugin_Iterator(
new ArrayIterator($this->plugins)
);
}
return $this->iterator;
} }
/** /**
* Proxies method calls to all plugins containing the called method. An * Sets the iterator for all currently loaded plugin instances.
* individual plugin may short-circuit this process by explicitly *
* returning FALSE. * @param Iterator $iterator Plugin iterator
*
* @return Phergie_Plugin_Handler Provides a fluent interface
*/
public function setIterator(Iterator $iterator)
{
$this->iterator = $iterator;
return $this;
}
/**
* Proxies method calls to all plugins containing the called method.
* *
* @param string $name Name of the method called * @param string $name Name of the method called
* @param array $args Arguments passed in the method call * @param array $args Arguments passed in the method call
* *
* @return bool FALSE if a plugin short-circuits processing by returning * @return void
* FALSE, TRUE otherwise
*/ */
public function __call($name, array $args) public function __call($name, array $args)
{ {
foreach ($this->plugins as $plugin) { foreach ($this->getIterator() as $plugin) {
if (call_user_func_array(array($plugin, $name), $args) === false) { call_user_func_array(array($plugin, $name), $args);
return false;
}
} }
return true; return true;
} }

View File

@ -28,26 +28,16 @@
* @license http://phergie.org/license New BSD License * @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Help * @link http://pear.phergie.org/package/Phergie_Plugin_Help
* @uses Phergie_Plugin_Command pear.phergie.org * @uses Phergie_Plugin_Command pear.phergie.org
*
* @pluginDesc Provides access to plugin help information
*/ */
class Phergie_Plugin_Help extends Phergie_Plugin_Abstract class Phergie_Plugin_Help extends Phergie_Plugin_Abstract
{ {
/** /**
* Holds the registry of help data indexed by plugin name * Registry of help data indexed by plugin name
* *
* @var array * @var array
*/ */
protected $registry; protected $registry;
/**
* Whether the registry has been alpha sorted
*
* @var bool
*/
protected $registry_sorted = false;
/** /**
* Checks for dependencies. * Checks for dependencies.
* *
@ -56,143 +46,175 @@ class Phergie_Plugin_Help extends Phergie_Plugin_Abstract
public function onLoad() public function onLoad()
{ {
$this->getPluginHandler()->getPlugin('Command'); $this->getPluginHandler()->getPlugin('Command');
$this->register($this); }
/**
* Creates a registry of plugin metadata on connect.
*
* @return void
*/
public function onConnect()
{
$this->populateRegistry();
}
/**
* Creates a registry of plugin metadata.
*
* @return void
*/
public function populateRegistry()
{
$this->registry = array();
foreach ($this->plugins as $plugin) {
$class = new ReflectionClass($plugin);
$pluginName = strtolower($plugin->getName());
// Parse the plugin description
$docblock = $class->getDocComment();
$annotations = $this->getAnnotations($docblock);
if (isset($annotations['pluginDesc'])) {
$pluginDesc = implode(' ', $annotations['pluginDesc']);
} else {
$pluginDesc = $this->parseShortDescription($docblock);
}
$this->registry[$pluginName] = array(
'desc' => $pluginDesc,
'cmds' => array()
);
// Parse command method descriptions
$methodPrefix = Phergie_Plugin_Command::METHOD_PREFIX;
$methodPrefixLength = strlen($methodPrefix);
foreach ($class->getMethods() as $method) {
if (strpos($method->getName(), $methodPrefix) !== 0) {
continue;
}
$cmd = strtolower(substr($method->getName(), $methodPrefixLength));
$docblock = $method->getDocComment();
$annotations = $this->getAnnotations($docblock);
if (isset($annotations['pluginCmd'])) {
$cmdDesc = implode(' ', $annotations['pluginCmd']);
} else {
$cmdDesc = $this->parseShortDescription($docblock);
}
$cmdParams = array();
if (!empty($annotations['param'])) {
foreach ($annotations['param'] as $param) {
$match = null;
if (preg_match('/\h+\$([^\h]+)\h+/', $param, $match)) {
$cmdParams[] = $match[1];
}
}
}
$this->registry[$pluginName]['cmds'][$cmd] = array(
'desc' => $cmdDesc,
'params' => $cmdParams
);
}
if (empty($this->registry[$pluginName]['cmds'])) {
unset($this->registry[$pluginName]);
}
}
} }
/** /**
* Displays a list of plugins with help information available or * Displays a list of plugins with help information available or
* commands available for a specific plugin. * commands available for a specific plugin.
* *
* @param string $plugin Short name of the plugin for which commands * @param string $query Optional short name of a plugin for which commands
* should be returned, else a list of plugins with help * should be returned or a command; if unspecified, a list of
* information available is returned * plugins with help information available is returned
* *
* @return void * @return void
*
* @pluginCmd Show all active plugins with help available
* @pluginCmd [plugin] Shows commands line for a specific plugin
*/ */
public function onCommandHelp($plugin = null) public function onCommandHelp($query = null)
{ {
if ($query == 'refresh') {
$this->populateRegistry();
}
$nick = $this->getEvent()->getNick(); $nick = $this->getEvent()->getNick();
$delay = $this->getConfig('help.delay', 2);
if (!$plugin) { // Handle requests for a plugin list
// protect from sorting the registry each time help is called if (!$query) {
if (!$this->registry_sorted) { $msg = 'These plugins have help information available: '
asort($this->registry); . implode(', ', array_keys($this->registry));
$this->registry_sorted = true; $this->doPrivmsg($nick, $msg);
return;
} }
$msg = 'These plugins below have help information available.'; // Handle requests for plugin information
$this->doPrivMsg($nick, $msg); $query = strtolower($query);
if (isset($this->registry[$query])
&& empty($this->registry[$query]['cmds'][$query])) {
$msg = $query . ' - ' . $this->registry[$query]['desc'];
$this->doPrivmsg($nick, $msg);
$msg = 'Available commands - '
. implode(', ', array_keys($this->registry[$query]['cmds']));
$this->doPrivmsg($nick, $msg);
foreach ($this->registry as $plugin => $data) {
$this->doPrivMsg($nick, "{$plugin} - {$data['desc']}");
}
} else {
if (isset($this->getPluginHandler()->{$plugin})
&& isset($this->registry[strtolower($plugin)]['cmd'])
) {
$msg
= 'The ' .
$plugin .
' plugin exposes the commands shown below.';
$this->doPrivMsg($nick, $msg);
if ($this->getConfig('command.prefix')) { if ($this->getConfig('command.prefix')) {
$msg $msg
= 'Note that these commands must be prefixed with "' . = 'Note that these commands must be prefixed with "'
$this->getConfig('command.prefix') . . $this->getConfig('command.prefix')
'" (without quotes) when issued in a public channel.'; . '" (without quotes) when issued in a public channel.';
$this->doPrivMsg($nick, $msg); $this->doPrivmsg($nick, $msg);
} }
foreach ($this->registry[strtolower($plugin)]['cmd'] return;
as $cmd => $descs
) {
foreach ($descs as $desc) {
$this->doPrivMsg($nick, "{$cmd} {$desc}");
}
} }
} else { // Handle requests for command information
$this->doPrivMsg($nick, 'That plugin is not loaded.'); foreach ($this->registry as $plugin => $data) {
if (empty($data['cmds'])) {
continue;
} }
$result = preg_grep('/^' . $query . '$/i', array_keys($data['cmds']));
if (!$result) {
continue;
}
$cmd = $data['cmds'][array_shift($result)];
$msg = $query;
if (!empty($cmd['params'])) {
$msg .= ' [' . implode('] [', $cmd['params']) . ']';
}
$msg .= ' - ' . $cmd['desc'];
$this->doPrivmsg($nick, $msg);
} }
} }
/** /**
* Sets the description for the plugin instance * Parses and returns the short description from a docblock.
* *
* @param Phergie_Plugin_Abstract $plugin plugin instance * @param string $docblock Docblock comment code
* @param string $description plugin description
* *
* @return void * @return string Short description (i.e. content from the start of the
* docblock up to the first double-newline)
*/ */
public function setPluginDescription( protected function parseShortDescription($docblock)
Phergie_Plugin_Abstract $plugin,
$description
) {
$this->registry[strtolower($plugin->getName())]
['desc'] = $description;
}
/**
* Sets the description for the command on the plugin instance
*
* @param Phergie_Plugin_Abstract $plugin plugin instance
* @param string $command from onCommand method
* @param string $description command description
*
* @return void
*/
public function setCommandDescription(
Phergie_Plugin_Abstract $plugin,
$command,
array $description
) {
$this->registry[strtolower($plugin->getName())]
['cmd'][$command] = $description;
}
/**
* registers the plugin with the help plugin. this will parse the docblocks
* for specific annotations that this plugin will respond with when
* queried.
*
* @param Phergie_Plugin_Abstract $plugin plugin instance
*
* @return void
*/
public function register(Phergie_Plugin_Abstract $plugin)
{ {
$class = new ReflectionClass($plugin); $desc = preg_replace(
array('#^\h*\*\h*#m', '#^/\*\*\h*\v+\h*#', '#(?:\r?\n){2,}.*#s', '#\s*\v+\s*#'),
$annotations = self::parseAnnotations($class->getDocComment()); array('', '', '', ' '),
if (isset($annotations['pluginDesc'])) { $docblock
$this->setPluginDescription(
$plugin,
join(' ', $annotations['pluginDesc'])
); );
} return $desc;
foreach ($class->getMethods() as $method) {
if (strpos($method->getName(), 'onCommand') !== false) {
$annotations = self::parseAnnotations($method->getDocComment());
if (isset($annotations['pluginCmd'])) {
$cmd = strtolower(substr($method->getName(), 9));
$this->setCommandDescription(
$plugin,
$cmd,
$annotations['pluginCmd']
);
}
}
}
} }
/** /**
* Taken from PHPUnit/Util/Test.php:243 and modified to fix an issue * Taken from PHPUnit/Util/Test.php and modified to fix an issue with
* with tag content spanning multiple lines. * tag content spanning multiple lines.
* *
* PHPUnit * PHPUnit
* *
@ -232,7 +254,7 @@ class Phergie_Plugin_Help extends Phergie_Plugin_Abstract
* *
* @return array * @return array
*/ */
protected static function parseAnnotations($docblock) protected function getAnnotations($docblock)
{ {
$annotations = array(); $annotations = array();

View File

@ -184,7 +184,10 @@ class Phergie_Plugin_Http extends Phergie_Plugin_Abstract
$type = $this->response->getHeaders('content-type'); $type = $this->response->getHeaders('content-type');
foreach ($this->handlers as $expr => $handler) { foreach ($this->handlers as $expr => $handler) {
if (preg_match('#^' . $expr . '$#i', $type)) { if (preg_match('#^' . $expr . '$#i', $type)) {
$body = call_user_func($handler, $body); $handled = call_user_func($handler, $body);
if (!empty($handled)) {
$body = $handled;
}
} }
} }
@ -256,7 +259,7 @@ class Phergie_Plugin_Http extends Phergie_Plugin_Abstract
public function post($url, array $query = array(), public function post($url, array $query = array(),
array $post = array(), array $context = array() array $post = array(), array $context = array()
) { ) {
if (!empty($params)) { if (!empty($query)) {
$url .= '?' . http_build_query($query); $url .= '?' . http_build_query($query);
} }

View File

@ -0,0 +1,180 @@
<?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_Ideone
* @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_Ideone
*/
/**
* Interfaces with ideone.com to execute code and return the result.
*
* @category Phergie
* @package Phergie_Plugin_Ideone
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Ideone
* @uses Phergie_Plugin_Command pear.phergie.org
*/
class Phergie_Plugin_Ideone extends Phergie_Plugin_Abstract
{
/**
* Checks for dependencies.
*
* @return void
*/
public function onLoad()
{
$this->plugins->getPlugin('Command');
}
/**
* Checks a service response for an error, sends a notice to the event
* source if an error has occurred, and returns whether an error was found.
*
* @param array $result Associative array representing the service response
*
* @return boolean TRUE if an error is found, FALSE otherwise
*/
protected function isError($result)
{
if ($result['error'] != 'OK') {
$this->doNotice($this->event->getNick(), 'ideone error: ' . $result['error']);
return true;
}
return false;
}
/**
* Executes a source code sequence in a specified language and returns
* the result.
*
* @param string $language Programming language the source code is in
* @param string $code Source code to execute
*
* @return void
*/
public function onCommandIdeone($language, $code)
{
$source = $this->event->getSource();
$nick = $this->event->getNick();
// Get authentication credentials
$user = $this->getConfig('ideone.user', 'test');
$pass = $this->getConfig('ideone.pass', 'test');
// Normalize the command parameters
$language = strtolower($language);
// Massage PHP code to allow for convenient shorthand
if ($language == 'php') {
if (!preg_match('/^<\?(?:php)?/', $code)) {
$code = '<?php ' . $code;
}
switch (substr($code, -1)) {
case '}':
case ';':
break;
default:
$code .= ';';
break;
}
}
// Identify the language to use
$client = new SoapClient('http://ideone.com/api/1/service.wsdl');
$response = $client->getLanguages($user, $pass);
if ($this->isError($response)) {
return;
}
$languageLength = strlen($language);
foreach ($response['languages'] as $languageId => $languageName) {
if (strncasecmp($language, $languageName, $languageLength) == 0) {
break;
}
}
// Send the paste data
$response = $client->createSubmission(
$user,
$pass,
$code,
$languageId,
null, // string input - data from stdin
true, // boolean run - TRUE to execute the code
false // boolean private - FALSE to make the paste public
);
if ($this->isError($response)) {
return;
}
$link = $response['link'];
// Wait until the paste data is processed or the service fails
$attempts = $this->getConfig('ideone.attempts', 10);
foreach (range(1, $attempts) as $attempt) {
$response = $client->getSubmissionStatus($user, $pass, $link);
if ($this->isError($response)) {
return;
}
if ($response['status'] == 0) {
$result = $response['result'];
break;
} else {
$result = null;
sleep(1);
}
}
if ($result == null) {
$this->doNotice($nick, 'ideone error: Timed out');
return;
}
if ($result != 15) {
$this->doNotice($nick, 'ideone error: Status code ' . $result);
return;
}
// Get details for the created paste
$response = $client->getSubmissionDetails(
$user,
$pass,
$link,
false, // boolean withSource - FALSE to not return the source code
false, // boolean withInput - FALSE to not return stdin data
true, // boolean withOutput - TRUE to include output
true, // boolean withStderr - TRUE to return stderr data
false // boolean withCmpinfo - TRUE to return compilation info
);
if ($this->isError($response)) {
return;
}
// Replace the output if it exceeds a specified maximum length
$outputLimit = $this->getConfig('ideone.output_limit', 100);
var_dump($response);
if ($outputLimit && strlen($response['output']) > $outputLimit) {
$response['output'] = 'Output is too long to post';
}
// Format the message
$msg = $this->getConfig('ideone.format', '%nick%: [ %link% ] %output%');
$response['nick'] = $nick;
$response['link'] = 'http://ideone.com/' . $link;
foreach ($response as $key => $value) {
$msg = str_replace('%' . $key . '%', $value, $msg);
}
$this->doPrivmsg($source, $msg);
}
}

View File

@ -0,0 +1,126 @@
<?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
* @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
*/
/**
* Implements a filtering iterator for limiting executing of methods across
* a group of plugins.
*
* @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie
*/
class Phergie_Plugin_Iterator extends FilterIterator
{
/**
* List of short names of plugins to exclude when iterating
*
* @var array
*/
protected $plugins = array();
/**
* List of method names where plugins with these methods will be
* excluded when iterating
*
* @var array
*/
protected $methods = array();
/**
* Adds to a list of plugins to exclude when iterating.
*
* @param mixed $plugins String containing the short name of a single
* plugin to exclude or an array of short names of multiple
* plugins to exclude
*
* @return Phergie_Plugin_Iterator Provides a fluent interface
*/
public function addPluginFilter($plugins)
{
if (is_array($plugins)) {
$this->plugins = array_unique(
array_merge($this->plugins, $plugins)
);
} else {
$this->plugins[] = $plugins;
}
return $this;
}
/**
* Adds to a list of method names where plugins defining these methods
* will be excluded when iterating.
*
* @param mixed $methods String containing the name of a single method
* or an array containing the name of multiple methods
*
* @return Phergie_Plugin_Iterator Provides a fluent interface
*/
public function addMethodFilter($methods)
{
if (is_array($methods)) {
$this->methods = array_merge($this->methods, $methods);
} else {
$this->methods[]= $methods;
}
return $this;
}
/**
* Clears any existing plugin and methods filters.
*
* @return Phergie_Plugin_Iterator Provides a fluent interface
*/
public function clearFilters()
{
$this->plugins = array();
$this->methods = array();
}
/**
* Implements FilterIterator::accept().
*
* @return boolean TRUE to include the current item in those by returned
* during iteration, FALSE otherwise
*/
public function accept()
{
if (!$this->plugins && !$this->methods) {
return true;
}
$current = $this->current();
if (in_array($current->getName(), $this->plugins)) {
return false;
}
foreach ($this->methods as $method) {
if (method_exists($current, $method)) {
return false;
}
}
return true;
}
}

View File

@ -21,427 +21,407 @@
/** /**
* Handles requests for incrementation or decrementation of a maintained list * Handles requests for incrementation or decrementation of a maintained list
* of counters for specified terms and antithrottling to prevent extreme * of counters for specified terms.
* inflation or depression of counters by any single individual.
* *
* @category Phergie * @category Phergie
* @package Phergie_Plugin_Karma * @package Phergie_Plugin_Karma
* @author Phergie Development Team <team@phergie.org> * @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License * @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Karma * @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 class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
{ {
/** /**
* Stores the SQLite object * SQLite object
* *
* @var resource * @var resource
*/ */
protected $db = null; protected $db = null;
/** /**
* Retains the last garbage collection date * Prepared statement to add a new karma record
*
* @var array
*/
protected $lastGc = null;
/**
* Logs the karma usages and limits users to one karma change per word
* and per day
*
* @return void
*/
protected $log = array();
/**
* Some fixed karma values, keys must be lowercase
*
* @var array
*/
protected $fixedKarma;
/**
* A list of blacklisted values
*
* @var array
*/
protected $karmaBlacklist;
/**
* Answers for correct assertions
*/
protected $positiveAnswers;
/**
* Answers for incorrect assertions
*/
protected $negativeAnswers;
/**
* Prepared PDO statements
* *
* @var PDOStatement * @var PDOStatement
*/ */
protected $insertKarma; protected $insertKarma;
protected $updateKarma;
protected $fetchKarma;
protected $insertComment;
/** /**
* Connects to the database containing karma ratings and initializes * Prepared statement to update an existing karma record
* class properties. *
* @var PDOStatement
*/
protected $updateKarma;
/**
* Retrieves an existing karma record
*
* @var PDOStatement
*/
protected $fetchKarma;
/**
* Retrieves an existing fixed karma record
*
* @var PDOStatement
*/
protected $fetchFixedKarma;
/**
* Retrieves a positive answer for a karma comparison
*
* @var PDOStatement
*/
protected $fetchPositiveAnswer;
/**
* Retrieves a negative answer for a karma comparison
*
* @var PDOStatement
*/
protected $fetchNegativeAnswer;
/**
* Check for dependencies and initializes a database connection and
* prepared statements.
* *
* @return void * @return void
*/ */
public function onLoad() public function onLoad()
{ {
$this->db = null; $plugins = $this->getPluginHandler();
$this->lastGc = null; $plugins->getPlugin('Command');
$this->log = array(); $plugins->getPlugin('Message');
if(!defined('M_EULER')) { $file = dirname(__FILE__) . '/Karma/karma.db';
define('M_EULER', '0.57721566490153286061'); $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->insertKarma = $this->db->prepare('
INSERT INTO karmas (
word,
karma
)
VALUES (
:word,
:karma
)
');
$this->insertComment = $this->db->prepare('
INSERT INTO comments (
wordid,
comment
)
VALUES (
:wordid,
:comment
)
');
$this->fetchKarma = $this->db->prepare(' $this->fetchKarma = $this->db->prepare('
SELECT karma, ROWID id FROM karmas WHERE LOWER(word) = LOWER(:word) LIMIT 1 SELECT karma
FROM karmas
WHERE term = :term
LIMIT 1
');
$this->insertKarma = $this->db->prepare('
INSERT INTO karmas (term, karma)
VALUES (:term, :karma)
'); ');
$this->updateKarma = $this->db->prepare(' $this->updateKarma = $this->db->prepare('
UPDATE karmas SET karma = :karma WHERE LOWER(word) = LOWER(:word) UPDATE karmas
SET karma = :karma
WHERE term = :term
');
$this->fetchFixedKarma = $this->db->prepare('
SELECT karma
FROM fixed_karmas
WHERE term = :term
LIMIT 1
');
$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')) { $canonicalTerm = strtolower(preg_replace('|\s+|', ' ', trim($term, '()')));
$this->fail('PDO and pdo_sqlite extensions must be installed'); 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 * Intercepts a message and processes any contained recognized commands.
* ratings sent via messages from users.
* *
* @return void * @return void
*/ */
public function onPrivmsg() public function onPrivmsg()
{ {
$source = $this->event->getSource(); $message = $this->getEvent()->getText();
$message = $this->event->getArgument(1);
$target = $this->event->getNick();
// Command prefix check $termPattern = '\S+?|\([^<>]+?\)+';
$prefix = preg_quote(trim($this->getConfig('command.prefix'))); $actionPattern = '(?P<action>\+\+|--)';
$bot = preg_quote($this->getConfig('connections.nick'));
$exp = '(?:(?:' . $bot . '\s*[:,>]?\s+(?:' . $prefix . ')?)|(?:' . $prefix . '))';
// Karma status request $modifyPattern = <<<REGEX
if (preg_match('#^' . $exp . 'karma\s+(.+)$#i', $message, $m)) { {^
// Return user's value if "me" is requested (?J) # allow overwriting capture names
if (strtolower($m[1]) === 'me') { \s* # ignore leading whitespace
$m[1] = $target;
}
// Clean the term
$term = $this->doCleanWord($m[1]);
// Check the blacklist (?: # start with ++ or -- before the term
if (is_array($this->karmaBlacklist) && in_array($term, $this->karmaBlacklist)) { $actionPattern
$this->doNotice($target, $term . ' is blacklisted'); (?P<term>$termPattern)
return; | # follow the term with ++ or --
} (?P<term>$termPattern)
$actionPattern # allow no whitespace between the term and the action
)
$}ix
REGEX;
// Return fixed value if set $versusPattern = <<<REGEX
if (isset($this->fixedKarma[$term])) { {^
$this->doPrivmsg($source, $target . ': ' . sprintf($this->fixedKarma[$term], $m[1]) . '.'); (?P<term0>$termPattern)
return; \s+(?P<method><|>)\s+
} (?P<term1>$termPattern)$#
$}ix
REGEX;
// Return current karma or neutral if not set yet $match = null;
$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 (preg_match($modifyPattern, $message, $match)) {
if (!$res && substr_count($term, ' ') > 1 && !(substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')')) { $action = $match['action'];
return; $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);
} }
// 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();
}
}
}
}
}
protected function fetchPositiveAnswer($owner, $owned)
{
return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->positiveAnswers[array_rand($this->positiveAnswers,1)]);
}
protected function fetchNegativeAnswer($owned, $owner)
{
return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->negativeAnswers[array_rand($this->negativeAnswers,1)]);
}
protected function doCleanWord($word)
{
$word = trim($word);
if (substr($word, 0, 1) === '(' && substr($word, -1) === ')') {
$word = trim(substr($word, 1, -1));
}
$word = preg_replace('#\s+#', ' ', strtolower(trim($word)));
return $word;
} }
/** /**
* Performs garbage collection on the antithrottling log. * Get the karma rating for a given term.
*
* @param string $term Term for which the karma rating needs to be
* retrieved
* *
* @return void * @return void
*/ */
public function doGc() public function onCommandKarma($term)
{ {
unset($this->log); $source = $this->getEvent()->getSource();
$this->log = array(); $nick = $this->getEvent()->getNick();
$this->lastGc = date('d');
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);
}
/**
* 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)
{
$data = array(
':term' => $term,
':karma' => 0
);
$this->updateKarma->execute($data);
}
/**
* 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)
{
$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);
}
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);
}
/**
* Modifes a term's karma.
*
* @param string $term Term to modify
* @param string $action Karma action (either ++ or --)
*
* @return void
*/
protected function modifyKarma($term, $action)
{
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'];
} }
} }

View File

@ -0,0 +1,112 @@
<?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_Message
* @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_Message
*/
/**
* Generalized plugin providing utility methods for
* prefix and bot named based message extraction.
*
* @category Phergie
* @package Phergie_Plugin_Message
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Message
*/
class Phergie_Plugin_Message extends Phergie_Plugin_Abstract
{
/**
* Check whether a message is specifically targeted at the bot.
* This is the case when the message starts with the bot's name
* followed by [,:>] or when it is a private message.
*
* @return boolean true when the message is specifically targeted at the bot,
* false otherwise.
*/
public function isTargetedMessage()
{
$event = $this->getEvent();
$self = preg_quote($this->connection->getNick());
$targetPattern = <<<REGEX
{^
\s*{$self}\s*[:>,].* # expect the bots name, followed by a [:>,]
$}ix
REGEX;
return !$event->isInChannel()
|| preg_match($targetPattern, $event->getText()) > 0;
}
/**
* Allow for prefix and bot name aware extraction of a message
*
* @return string|bool $message The message, which is possibly targeted at the
* bot or false if a prefix requirement failed
*/
public function getMessage()
{
$event = $this->getEvent();
$prefix = preg_quote($this->getConfig('command.prefix'));
$self = preg_quote($this->connection->getNick());
$message = $event->getText();
// $prefixPattern matches : Phergie, do command <parameters>
// where $prefix = 'do' : do command <parameters>
// : Phergie, command <parameters>
$prefixPattern = <<<REGEX
{^
(?:
\s*{$self}\s*[:>,]\s* # start with bot name
(?:{$prefix})? # which is optionally followed by the prefix
|
\s*{$prefix} # or start with the prefix
)
\s*(.*) # always end with the message
$}ix
REGEX;
// $noPrefixPattern matches : Phergie, command <parameters>
// : command <parameters>
$noPrefixPattern = <<<REGEX
{^
\s*(?:{$self}\s*[:>,]\s*)? # optionally start with the bot name
(.*?) # always end with the message
$}ix
REGEX;
$pattern = $noPrefixPattern;
// If a prefix is set, force it as a requirement
if ($prefix && $event->isInChannel()) {
$pattern = $prefixPattern;
}
$match = null;
if (!preg_match($pattern, $message, $match)) {
return false;
}
return $match[1];
}
}

View File

@ -87,7 +87,7 @@ class Phergie_Plugin_Ping extends Phergie_Plugin_Abstract
{ {
$time = time(); $time = time();
if (!empty($this->lastPing)) { if (!empty($this->lastPing)) {
if ($time - $this->lastPing > $this->getConfig('ping.ping', 10)) { if ($time - $this->lastPing > $this->getConfig('ping.ping', 20)) {
$this->doQuit(); $this->doQuit();
} }
} elseif ( } elseif (

View File

@ -28,8 +28,6 @@
* @license http://phergie.org/license New BSD License * @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Quit * @link http://pear.phergie.org/package/Phergie_Plugin_Quit
* @uses Phergie_Plugin_Command pear.phergie.org * @uses Phergie_Plugin_Command pear.phergie.org
*
* @pluginDesc Terminates the current connection upon command.
*/ */
class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract
{ {
@ -41,8 +39,6 @@ class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract
public function onLoad() public function onLoad()
{ {
$this->getPluginHandler()->getPlugin('Command'); $this->getPluginHandler()->getPlugin('Command');
$help = $this->getPluginHandler()->getPlugin('Help');
$help->register($this);
} }
/** /**
@ -50,8 +46,6 @@ class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract
* bot terminate the current connection. * bot terminate the current connection.
* *
* @return void * @return void
*
* @pluginCmd terminates the connection
*/ */
public function onCommandQuit() public function onCommandQuit()
{ {

View File

@ -57,6 +57,7 @@ class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
if (!$this->plugins->hasPlugin($plugin)) { if (!$this->plugins->hasPlugin($plugin)) {
echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL; echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL;
$this->plugins->getPlugin($plugin); $this->plugins->getPlugin($plugin);
$this->plugins->command->populateMethodCache();
return; return;
} }
@ -79,7 +80,7 @@ class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
} }
$contents = preg_replace( $contents = preg_replace(
array('/<\?(?:php)?/', '/class\s+' . $class . '/i'), array('/^<\?(?:php)?/', '/class\s+' . $class . '/i'),
array('', 'class ' . $newClass), array('', 'class ' . $newClass),
$contents $contents
); );
@ -87,10 +88,16 @@ class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
$instance = new $newClass; $instance = new $newClass;
$instance->setName($plugin); $instance->setName($plugin);
$instance->setEvent($this->event);
$this->plugins $this->plugins
->removePlugin($plugin) ->removePlugin($plugin)
->addPlugin($instance); ->addPlugin($instance);
$this->plugins->command->populateMethodCache();
if ($this->plugins->hasPlugin('Help')) {
$this->plugins->help->populateRegistry();
}
echo 'DEBUG(Reload): Reloaded ', $class, ' to ', $newClass, PHP_EOL; echo 'DEBUG(Reload): Reloaded ', $class, ' to ', $newClass, PHP_EOL;
} }
} }

View File

@ -113,9 +113,10 @@ class Phergie_Plugin_Serve extends Phergie_Plugin_Abstract
$item = $this->getItem($database, $table, $match); $item = $this->getItem($database, $table, $match);
// Reprocess the request for censorship if required // Reprocess the request for censorship if required
if ($this->plugins->hasPlugin('Censor')) {
$plugin = $this->plugins->getPlugin('Censor');
$attempts = 0; $attempts = 0;
while ($censor && $attempts < 3) { while ($censor && $attempts < 3) {
$plugin = $this->plugins->getPlugin('Censor');
$clean = $plugin->cleanString($item->name); $clean = $plugin->cleanString($item->name);
if ($item->name != $clean) { if ($item->name != $clean) {
$attempts++; $attempts++;
@ -124,10 +125,10 @@ class Phergie_Plugin_Serve extends Phergie_Plugin_Abstract
$censor = false; $censor = false;
} }
} }
if ($censor && $attempts == 3) { if ($censor && $attempts == 3) {
$this->doAction($this->event->getSource(), 'shrugs.'); $this->doAction($this->event->getSource(), 'shrugs.');
} }
}
// Derive the proper article for the item // Derive the proper article for the item
if (preg_match('/^[aeiou]/i', $item->name)) { if (preg_match('/^[aeiou]/i', $item->name)) {

View File

@ -0,0 +1,69 @@
<?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_Tea
* @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_Tea
*/
/**
* Processes requests to serve users tea.
*
* @category Phergie
* @package Phergie_Plugin_Tea
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Tea
* @uses Phergie_Plugin_Command pear.phergie.org
* @uses Phergie_Plugin_Serve pear.phergie.org
*/
class Phergie_Plugin_Tea extends Phergie_Plugin_Abstract
{
/**
* Checks for dependencies.
*
* @return void
*/
public function onLoad()
{
$plugins = $this->plugins;
$plugins->getPlugin('Command');
$plugins->getPlugin('Serve');
}
/**
* Processes requests to serve a user tea.
*
* @param string $request Request including the target and an optional
* suggestion of what tea to serve
*
* @return void
*/
public function onCommandTea($request)
{
$format = $this->getConfig(
'tea.format',
'serves %target% a cup of %item% tea.'
);
$this->plugins->getPlugin('Serve')->serve(
dirname(__FILE__) . '/Tea/tea.db',
'tea',
$format,
$request
);
}
}

View File

@ -0,0 +1,49 @@
<?php
if (!defined('__DIR__')) {
define('__DIR__', dirname(__FILE__));
}
// Create database schema
echo 'Creating database', PHP_EOL;
$file = __DIR__ . '/tea.db';
if (file_exists($file)) {
unlink($file);
}
$db = new PDO('sqlite:' . $file);
$db->exec('CREATE TABLE tea (name VARCHAR(255), link VARCHAR(255))');
$db->exec('CREATE UNIQUE INDEX tea_name ON tea (name)');
$insert = $db->prepare('INSERT INTO tea (name, link) VALUES (:name, :link)');
// Get raw teacuppa.com data set
echo 'Downloading teacuppa.com data set', PHP_EOL;
$file = __DIR__ . '/tea-list.html';
if (!file_exists($file)) {
copy('http://www.teacuppa.com/tea-list.asp', $file);
}
$contents = file_get_contents($file);
// Extract data from data set
echo 'Processing teacuppa.com data', PHP_EOL;
$contents = tidy_repair_string($contents);
libxml_use_internal_errors(true);
$doc = new DOMDocument;
$doc->loadHTML($contents);
libxml_clear_errors();
$xpath = new DOMXPath($doc);
$teas = $xpath->query('//p[@class="page_title"]/following-sibling::table//a');
$db->beginTransaction();
foreach ($teas as $tea) {
$name = preg_replace(
array('/\s*\v+\s*/', '/\s+tea\s*$/i'),
array(' ', ''),
$tea->textContent
);
$link = 'http://teacuppa.com/' . $tea->getAttribute('href');
$insert->execute(array($name, $link));
}
$db->commit();
// Clean up
echo 'Cleaning up', PHP_EOL;
unlink($file);

View File

@ -31,9 +31,6 @@
* @link http://thefuckingweather.com * @link http://thefuckingweather.com
* @uses Phergie_Plugin_Command pear.phergie.org * @uses Phergie_Plugin_Command pear.phergie.org
* @uses Phergie_Plugin_Http pear.phergie.org * @uses Phergie_Plugin_Http pear.phergie.org
*
* @pluginDesc Detects and responds to requests for current weather
* conditions in a particular location.
*/ */
class Phergie_Plugin_TheFuckingWeather extends Phergie_Plugin_Abstract class Phergie_Plugin_TheFuckingWeather extends Phergie_Plugin_Abstract
@ -71,8 +68,6 @@ class Phergie_Plugin_TheFuckingWeather extends Phergie_Plugin_Abstract
* *
* @return void * @return void
* @todo Implement use of URL shortening here * @todo Implement use of URL shortening here
* @pluginCmd [location] Detects and responds to requests for current
* weather conditions in a particular location.
*/ */
public function onCommandThefuckingweather($location) public function onCommandThefuckingweather($location)
{ {
@ -92,7 +87,6 @@ class Phergie_Plugin_TheFuckingWeather extends Phergie_Plugin_Abstract
* @param string $location Location term * @param string $location Location term
* *
* @return void * @return void
* @pluginCmd [location] Alias for thefuckingweather command.
*/ */
public function onCommandTfw($location) public function onCommandTfw($location)
{ {

View File

@ -31,31 +31,32 @@
* @link http://pear.phergie.org/package/Phergie_Plugin_Tld * @link http://pear.phergie.org/package/Phergie_Plugin_Tld
* @uses extension PDO * @uses extension PDO
* @uses extension pdo_sqlite * @uses extension pdo_sqlite
*
* @pluginDesc Provides information for a top level domain.
*/ */
class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract
{ {
/** /**
* connection to the database * Connection to the database
*
* @var PDO * @var PDO
*/ */
protected $db; protected $db;
/** /**
* Prepared statement for selecting a single tld * Prepared statement for selecting a single TLD
*
* @var PDOStatement * @var PDOStatement
*/ */
protected $select; protected $select;
/** /**
* Prepared statement for selecting all tlds * Prepared statement for selecting all TLDs
*
* @var PDOStatement * @var PDOStatement
*/ */
protected $selectAll; protected $selectAll;
/** /**
* Checks for dependencies, sets up database and hard coded values * Checks for dependencies and sets up the database and hard-coded values.
* *
* @return void * @return void
*/ */
@ -65,9 +66,6 @@ class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract
$this->fail('PDO and pdo_sqlite extensions must be installed'); $this->fail('PDO and pdo_sqlite extensions must be installed');
} }
$help = $this->getPluginHandler()->getPlugin('Help');
$help->register($this);
$dbFile = dirname(__FILE__) . '/Tld/tld.db'; $dbFile = dirname(__FILE__) . '/Tld/tld.db';
try { try {
$this->db = new PDO('sqlite:' . $dbFile); $this->db = new PDO('sqlite:' . $dbFile);
@ -93,8 +91,6 @@ class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract
* @param string $tld tld to process * @param string $tld tld to process
* *
* @return null * @return null
*
* @pluginCmd .[tld] request details about the tld
*/ */
public function onCommandTld($tld) public function onCommandTld($tld)
{ {

View File

@ -148,7 +148,7 @@ class Phergie_Plugin_Youtube extends Phergie_Plugin_Abstract
{ {
switch ($parsed['host']) { switch ($parsed['host']) {
case 'youtu.be': case 'youtu.be':
$v = ltrim('/', $parsed['path']); $v = ltrim($parsed['path'], '/');
break; break;
case 'youtube.com': case 'youtube.com':
case 'www.youtube.com': case 'www.youtube.com':

View File

@ -99,8 +99,8 @@ abstract class Phergie_Process_Abstract
*/ */
protected function processEvents(Phergie_Connection $connection) protected function processEvents(Phergie_Connection $connection)
{ {
if (count($this->events)) {
$this->plugins->preDispatch(); $this->plugins->preDispatch();
if (count($this->events)) {
foreach ($this->events as $event) { foreach ($this->events as $event) {
$this->ui->onCommand($event, $connection); $this->ui->onCommand($event, $connection);
@ -110,6 +110,7 @@ abstract class Phergie_Process_Abstract
$event->getArguments() $event->getArguments()
); );
} }
}
$this->plugins->postDispatch(); $this->plugins->postDispatch();
if ($this->events->hasEventOfType(Phergie_Event_Request::TYPE_QUIT)) { if ($this->events->hasEventOfType(Phergie_Event_Request::TYPE_QUIT)) {
@ -119,7 +120,6 @@ abstract class Phergie_Process_Abstract
$this->events->clearEvents(); $this->events->clearEvents();
} }
}
/** /**
* Obtains and processes incoming events. * Obtains and processes incoming events.

View File

@ -117,11 +117,7 @@ class Phergie_Process_Async extends Phergie_Process_Abstract
if ($event = $this->driver->getEvent()) { if ($event = $this->driver->getEvent()) {
$this->ui->onEvent($event, $connection); $this->ui->onEvent($event, $connection);
$this->plugins->setEvent($event); $this->plugins->setEvent($event);
$this->plugins->preEvent();
if (!$this->plugins->preEvent()) {
continue;
}
$this->plugins->{'on' . ucfirst($event->getType())}(); $this->plugins->{'on' . ucfirst($event->getType())}();
} }

View File

@ -47,11 +47,7 @@ class Phergie_Process_Standard extends Phergie_Process_Abstract
if ($event = $this->driver->getEvent()) { if ($event = $this->driver->getEvent()) {
$this->ui->onEvent($event, $connection); $this->ui->onEvent($event, $connection);
$this->plugins->setEvent($event); $this->plugins->setEvent($event);
$this->plugins->preEvent();
if (!$this->plugins->preEvent()) {
continue;
}
$this->plugins->{'on' . ucfirst($event->getType())}(); $this->plugins->{'on' . ucfirst($event->getType())}();
} }