--- /dev/null
+<?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');
if (!in_array($this->encoding, mb_list_encodings())) {
throw new Phergie_Connection_Exception(
'Encoding ' . $this->encoding . ' is not supported',
- Phergie_Connection_Exception::ENCODING_NOT_SUPPORTED
+ Phergie_Connection_Exception::ERR_ENCODING_NOT_SUPPORTED
);
}
* but that transport is not supported by the current PHP installation
*/
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;
}
// Parse the command and arguments
list($cmd, $args) = array_pad(explode(' ', $buffer, 2), 2, null);
+ $hostmask = new Phergie_Hostmask(null, null, $this->connection->getHost());
} else {
// If the event could be from the server or a user...
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Acl
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
*/
/**
- * 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.
*
- * @category Phergie
+ * 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
* @package Phergie_Plugin_Acl
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Acl
+ * @uses Phergie_Plugin_UserInfo pear.phergie.org
*/
class Phergie_Plugin_Acl extends Phergie_Plugin_Abstract
{
*/
public function onLoad()
{
+ $this->plugins->getPlugin('UserInfo');
+
if (!$this->getConfig('acl.blacklist')
&& !$this->getConfig('acl.whitelist')
) {
}
/**
- * Checks permission settings and short-circuits event processing for
+ * 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
* blacklisted users.
*
- * @return bool FALSE to short-circuit event processing if the user is
- * blacklisted, TRUE otherwise
+ * @return void
*/
public function preEvent()
{
// Ignore server responses
if ($this->event instanceof Phergie_Event_Response) {
- return true;
+ return;
}
// Ignore server-initiated events
if (!$this->event->isFromUser()) {
- return true;
+ return;
}
- // Determine whether a whitelist or blacklist is being used
- $list = $this->getConfig('acl.whitelist');
- $matches = true;
- if (!$list) {
- $list = $this->getConfig('acl.blacklist');
- $matches = false;
- }
+ // Get the iterator used to filter plugins when processing events
+ $iterator = $this->plugins->getIterator();
+
+ // Get configuration setting values
+ $whitelist = $this->getConfig('acl.whitelist', array());
+ $blacklist = $this->getConfig('acl.blacklist', array());
+ $ops = $this->getConfig('acl.ops', false);
- // Support host-specific lists
+ // Support host-specific lists
$host = $this->connection->getHost();
- if (isset($list[$host])) {
- $list = $list[$host];
+ foreach (array('whitelist', 'blacklist') as $var) {
+ 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();
- 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)) {
+ $whitelisted = true;
+ }
+ }
+ if (!$whitelisted) {
+ foreach ($whitelist as $pattern => $rules) {
+ $this->applyRules($iterator, $rules);
+ }
+ }
+ }
+
+ // Filter blacklisted commands if the user is on the blacklist
+ $blacklisted = false;
+ foreach ($blacklist as $pattern => $rules) {
if ($hostmask->matches($pattern)) {
- return $matches;
+ $this->applyRules($iterator, $rules);
+ break;
}
}
+ }
- // Allow event processing if appropriate
- return !$matches;
+ /**
+ * Clears filters on the plugin handler iterator.
+ *
+ * @return void
+ */
+ public function postDispatch()
+ {
+ $this->plugins->getIterator()->clearFilters();
}
}
--- /dev/null
+<?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
+ );
+ }
+}
--- /dev/null
+<?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);
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Command
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
*/
/**
- * Handles parsing and execution of commands sent by users via messages sent
+ * Handles parsing and execution of commands sent by users via messages sent
* to channels in which the bot is present or directly to the bot.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Command
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Command
* @uses extension reflection
+ * @uses Phergie_Plugin_Message pear.phergie.org
*/
class Phergie_Plugin_Command extends Phergie_Plugin_Abstract
{
/**
- * Cache for command lookups used to confirm that methods exist and
+ * Prefix for command method names
+ *
+ * @var string
+ */
+ const METHOD_PREFIX = 'onCommand';
+
+ /**
+ * Cache for command lookups used to confirm that methods exist and
* parameter counts match
*
* @var 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.
*
* @return void
*/
- protected function populateMethodCache()
+ public function populateMethodCache()
{
foreach ($this->getPluginHandler() as $plugin) {
$reflector = new ReflectionClass($plugin);
foreach ($reflector->getMethods() as $method) {
$name = $method->getName();
- if (strpos($name, $this->methodPrefix) === 0
+ if (strpos($name, self::METHOD_PREFIX) === 0
&& !isset($this->methods[$name])
) {
$this->methods[$name] = array(
$this->populateMethodCache();
}
- // Get the content of the message
- $event = $this->getEvent();
- $msg = trim($event->getText());
- $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;
- } else {
- $msg = substr($msg, strlen($prefix));
- }
+ // Check for a prefixed message
+ $msg = $this->plugins->message->getMessage();
+ if ($msg === false) {
+ return;
}
// Separate the command and arguments
$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) : '';
+ // 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
+ $method = self::METHOD_PREFIX . ucfirst($command);
if (empty($this->methods[$method])) {
return;
}
// Parse the arguments
$args = preg_split('/\s+/', $args, $this->methods[$method]['total']);
- // If the minimum arguments are passed, call the method
+ // If the minimum arguments are passed, call the method
if ($this->methods[$method]['required'] <= count($args)) {
call_user_func_array(
array($this->getPluginHandler(), $method),
foreach ($cookies as $cookie) {
$name = $cookie->textContent;
- foreach (range(0, mb_strlen($name) - 1) as $index) {
- echo mb_strcut($name, $index, 1), PHP_EOL;
- }
$name = str_replace(
array('(',')',"\n", 'cookies'),
array('','', ' ', 'cookie'),
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Daddy
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* Simply responds to messages addressed to the bot that contain the phrase
* "Who's your daddy?" and related variations.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Daddy
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
$text = $event->getArgument(1);
$target = $event->getNick();
$source = $event->getSource();
- $pattern
- = '/' . preg_quote($prefix) .
+ $pattern
+ = '/' . preg_quote($prefix) .
'\s*?who\'?s y(?:our|a) ([^?]+)\??/iAD';
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);
}
}
* @uses Phergie_Plugin_Command pear.phergie.org
* @uses Phergie_Plugin_Http 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
{
-
- /**
- * HTTP plugin
- *
- * @var Phergie_Plugin_Http
- */
- protected $http;
-
- /**
- * Language for Google Services
- */
- protected $lang;
-
/**
* Checks for dependencies.
*
{
$plugins = $this->getPluginHandler();
$plugins->getPlugin('Command');
- $this->http = $plugins->getPlugin('Http');
- $plugins->getPlugin('Help')->register($this);
+ $plugins->getPlugin('Http');
$plugins->getPlugin('Weather');
-
- $this->lang = $this->getConfig('google.lang', 'en');
}
/**
*
* @return void
* @todo Implement use of URL shortening here
- *
- * @pluginCmd [query] do a search on google
*/
public function onCommandG($query)
{
'v' => '1.0',
'q' => $query
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$json = $response->getContent()->responseData;
$event = $this->getEvent();
$source = $event->getSource();
* @param string $query Search term
*
* @return void
- *
- * @pluginCmd [query] Do a search on Google and count the results
*/
public function onCommandGc($query)
{
'v' => '1.0',
'q' => $query
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$json = $response->getContent()->responseData->cursor;
$count = $json->estimatedResultCount;
$event = $this->getEvent();
* @param string $query Term to translate
*
* @return void
- *
- * @pluginCmd [from language] [to language] [text to translate] Do a translation on Google
*/
public function onCommandGt($from, $to, $query)
{
'q' => $query,
'langpair' => $from . '|' . $to
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$json = $response->getContent();
$event = $this->getEvent();
$source = $event->getSource();
* between 0 and 3 to get the forecast
*
* @return void
- *
- * @pluginCmd [location] Show the weather for the specified location
*/
public function onCommandGw($location, $offset = null)
{
$url = 'http://www.google.com/ig/api';
$params = array(
'weather' => $location,
- 'hl' => $this->lang,
+ 'hl' => $this->getConfig('google.lang', 'en'),
'oe' => 'UTF-8'
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$xml = $response->getContent()->weather;
$event = $this->getEvent();
* @param string $location Location to search for
*
* @return void
- *
- * @pluginCmd [location] Get the location from Google Maps to the location specified
*/
public function onCommandGmap($location)
{
$params = array(
'q' => $location,
'output' => 'json',
- 'gl' => $this->lang,
+ 'gl' => $this->getConfig('google.lang', 'en'),
'sensor' => 'false',
'oe' => 'utf8',
'mrt' => 'all',
'key' => $this->getConfig('google.key')
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$json = $response->getContent();
if (!empty($json)) {
$qtd = count($json->Placemark);
* @param string $to Destination metric
*
* @return void
- *
- * @pluginCmd [value] [currency from] [currency to] Converts a monetary value from one currency to another
*/
public function onCommandGconvert($value, $from, $to)
{
'from' => $from,
'to' => $to
);
- $response = $this->http->get($url, $params);
+ $response = $this->plugins->http->get($url, $params);
$contents = $response->getContent();
$event = $this->getEvent();
$source = $event->getSource();
$nick = $event->getNick();
if ($contents) {
- preg_match(
- '#<span class=bld>.*? ' . $to . '</span>#im',
- $contents,
- $matches
- );
- if (!$matches[0]) {
- $this->doPrivmsg($source, $nick . ', I can\'t do that.');
- } else {
- $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);
- }
- } else {
- $this->doPrivmsg($source, $nick . ', we had a problem.');
+ libxml_use_internal_errors(true);
+ $doc = new DOMDocument;
+ $doc->loadHTML($contents);
+ libxml_clear_errors();
+ $xpath = new DOMXPath($doc);
+ $result = $xpath->query('//div[@id="currency_converter_result"]');
+ $div = $result->item(0);
+ $text = rtrim($div->textContent);
+ $this->doPrivmsg($source, $text);
}
}
public function onCommandConvert($query)
{
$url = 'http://www.google.com/search?q=' . urlencode($query);
- $response = $this->http->get($url);
+ $response = $this->plugins->http->get($url);
$contents = $response->getContent();
$event = $this->getEvent();
$source = $event->getSource();
*
* @return void
* @todo Implement use of URL shortening here
- *
- * @pluginCmd [query] do a search of a definition on Google Dictionary
*/
public function onCommandDefine($query)
{
- $query = urlencode($query);
- $url = 'http://www.google.com/dictionary/json?callback=result'.
- '&q='.$query.'&sl='.$this->lang.'&tl='.$this->lang.
- '&restrict=pr,de';
- $json = file_get_contents($url);
-
- //Remove some garbage from the json
- $json = str_replace(array("result(", ",200,null)"), "", $json);
+ $lang = $this->getConfig('google.lang', 'en');
+ $url = 'http://www.google.com/dictionary/json';
+ $params = array(
+ 'callback' => 'result',
+ 'q' => $query,
+ 'sl' => $lang,
+ 'tl' => $lang,
+ 'restrict' => 'pr,de'
+ );
+ $response = $this->plugins->http->get($url, $params);
+ $json = $response->getContent();
- //Awesome workaround to remove a lot of slashes from json
+ // Remove some garbage from the JSON and decode it
+ $json = str_replace(array('result(', ',200,null)'), '', $json);
$json = str_replace('"', '¿?¿', $json);
$json = strip_tags(stripcslashes($json));
$json = str_replace('"', "'", $json);
$json = str_replace('¿?¿', '"', $json);
-
$json = json_decode($json);
$event = $this->getEvent();
$source = $event->getSource();
$nick = $event->getNick();
- if (!empty($json->webDefinitions)){
- $results = count($json->webDefinitions[0]->entries);
- $more = $results > 1 ? ($results-1).' ' : NULL;
- $lang_code = substr($this->lang, 0, 2);
- $msg =
- $nick . ': ' .
- $json->webDefinitions[0]->entries[0]->terms[0]->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;
+ if (!empty($json->webDefinitions)) {
+ $results = 0;
+ foreach ($json->primaries[0]->entries as $entry) {
+ if ($entry->type == 'meaning') {
+ $results++;
+ if (empty($text)) {
+ foreach ($entry->terms as $term) {
+ if ($term->type == 'text') {
+ $text = trim($term->text);
+ }
+ }
+ }
+ }
+ }
+ $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);
- }else{
- if ($this->lang != 'en'){
- $temp = $this->lang;
- $this->lang = 'en';
+ } else {
+ if ($lang != 'en'){
+ $lang = 'en';
$this->onCommandDefine($query);
- $this->lang = $temp;
- }else{
+ } else {
$msg = $nick . ': No results for this query.';
$this->doPrivmsg($source, $msg);
}
*/
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
* plugins.
*/
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
- * individual plugin may short-circuit this process by explicitly
- * returning FALSE.
+ * Sets the iterator for all currently loaded plugin instances.
+ *
+ * @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 array $args Arguments passed in the method call
*
- * @return bool FALSE if a plugin short-circuits processing by returning
- * FALSE, TRUE otherwise
+ * @return void
*/
public function __call($name, array $args)
{
- foreach ($this->plugins as $plugin) {
- if (call_user_func_array(array($plugin, $name), $args) === false) {
- return false;
- }
+ foreach ($this->getIterator() as $plugin) {
+ call_user_func_array(array($plugin, $name), $args);
}
return true;
}
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Help
* @uses Phergie_Plugin_Command pear.phergie.org
- *
- * @pluginDesc Provides access to plugin help information
*/
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
*/
protected $registry;
- /**
- * Whether the registry has been alpha sorted
- *
- * @var bool
- */
- protected $registry_sorted = false;
-
/**
* Checks for dependencies.
*
public function onLoad()
{
$this->getPluginHandler()->getPlugin('Command');
- $this->register($this);
}
/**
- * Displays a list of plugins with help information available or
- * commands available for a specific plugin.
- *
- * @param string $plugin Short name of the plugin for which commands
- * should be returned, else a list of plugins with help
- * information available is returned
+ * Creates a registry of plugin metadata on connect.
*
* @return void
+ */
+ public function onConnect()
+ {
+ $this->populateRegistry();
+ }
+
+ /**
+ * Creates a registry of plugin metadata.
*
- * @pluginCmd Show all active plugins with help available
- * @pluginCmd [plugin] Shows commands line for a specific plugin
+ * @return void
*/
- public function onCommandHelp($plugin = null)
+ public function populateRegistry()
{
- $nick = $this->getEvent()->getNick();
+ $this->registry = array();
- if (!$plugin) {
- // protect from sorting the registry each time help is called
- if (!$this->registry_sorted) {
- asort($this->registry);
- $this->registry_sorted = true;
+ 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;
+ }
- $msg = 'These plugins below have help information available.';
- $this->doPrivMsg($nick, $msg);
+ $cmd = strtolower(substr($method->getName(), $methodPrefixLength));
+ $docblock = $method->getDocComment();
+ $annotations = $this->getAnnotations($docblock);
- 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')) {
- $msg
- = 'Note that these commands must be prefixed with "' .
- $this->getConfig('command.prefix') .
- '" (without quotes) when issued in a public channel.';
- $this->doPrivMsg($nick, $msg);
+ if (isset($annotations['pluginCmd'])) {
+ $cmdDesc = implode(' ', $annotations['pluginCmd']);
+ } else {
+ $cmdDesc = $this->parseShortDescription($docblock);
}
- foreach ($this->registry[strtolower($plugin)]['cmd']
- as $cmd => $descs
- ) {
- foreach ($descs as $desc) {
- $this->doPrivMsg($nick, "{$cmd} {$desc}");
+ $cmdParams = array();
+ if (!empty($annotations['param'])) {
+ foreach ($annotations['param'] as $param) {
+ $match = null;
+ if (preg_match('/\h+\$([^\h]+)\h+/', $param, $match)) {
+ $cmdParams[] = $match[1];
+ }
}
}
- } else {
- $this->doPrivMsg($nick, 'That plugin is not loaded.');
+ $this->registry[$pluginName]['cmds'][$cmd] = array(
+ 'desc' => $cmdDesc,
+ 'params' => $cmdParams
+ );
+ }
+
+ if (empty($this->registry[$pluginName]['cmds'])) {
+ unset($this->registry[$pluginName]);
}
}
}
/**
- * Sets the description for the plugin instance
+ * Displays a list of plugins with help information available or
+ * commands available for a specific plugin.
*
- * @param Phergie_Plugin_Abstract $plugin plugin instance
- * @param string $description plugin description
+ * @param string $query Optional short name of a plugin for which commands
+ * should be returned or a command; if unspecified, a list of
+ * plugins with help information available is returned
*
* @return void
*/
- public function setPluginDescription(
- Phergie_Plugin_Abstract $plugin,
- $description
- ) {
- $this->registry[strtolower($plugin->getName())]
- ['desc'] = $description;
- }
+ public function onCommandHelp($query = null)
+ {
+ if ($query == 'refresh') {
+ $this->populateRegistry();
+ }
- /**
- * 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;
+ $nick = $this->getEvent()->getNick();
+ $delay = $this->getConfig('help.delay', 2);
+
+ // Handle requests for a plugin list
+ if (!$query) {
+ $msg = 'These plugins have help information available: '
+ . implode(', ', array_keys($this->registry));
+ $this->doPrivmsg($nick, $msg);
+ return;
+ }
+
+ // Handle requests for plugin information
+ $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);
+
+ if ($this->getConfig('command.prefix')) {
+ $msg
+ = 'Note that these commands must be prefixed with "'
+ . $this->getConfig('command.prefix')
+ . '" (without quotes) when issued in a public channel.';
+ $this->doPrivmsg($nick, $msg);
+ }
+
+ return;
+ }
+
+ // Handle requests for command information
+ 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);
+ }
}
/**
- * registers the plugin with the help plugin. this will parse the docblocks
- * for specific annotations that this plugin will respond with when
- * queried.
+ * Parses and returns the short description from a docblock.
*
- * @param Phergie_Plugin_Abstract $plugin plugin instance
+ * @param string $docblock Docblock comment code
*
- * @return void
+ * @return string Short description (i.e. content from the start of the
+ * docblock up to the first double-newline)
*/
- public function register(Phergie_Plugin_Abstract $plugin)
+ protected function parseShortDescription($docblock)
{
- $class = new ReflectionClass($plugin);
-
- $annotations = self::parseAnnotations($class->getDocComment());
- if (isset($annotations['pluginDesc'])) {
- $this->setPluginDescription(
- $plugin,
- join(' ', $annotations['pluginDesc'])
- );
- }
-
- 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']
- );
- }
- }
- }
+ $desc = preg_replace(
+ array('#^\h*\*\h*#m', '#^/\*\*\h*\v+\h*#', '#(?:\r?\n){2,}.*#s', '#\s*\v+\s*#'),
+ array('', '', '', ' '),
+ $docblock
+ );
+ return $desc;
}
/**
- * Taken from PHPUnit/Util/Test.php:243 and modified to fix an issue
- * with tag content spanning multiple lines.
+ * Taken from PHPUnit/Util/Test.php and modified to fix an issue with
+ * tag content spanning multiple lines.
*
* PHPUnit
*
*
* @return array
*/
- protected static function parseAnnotations($docblock)
+ protected function getAnnotations($docblock)
{
$annotations = array();
$type = $this->response->getHeaders('content-type');
foreach ($this->handlers as $expr => $handler) {
if (preg_match('#^' . $expr . '$#i', $type)) {
- $body = call_user_func($handler, $body);
+ $handled = call_user_func($handler, $body);
+ if (!empty($handled)) {
+ $body = $handled;
+ }
}
}
public function post($url, array $query = array(),
array $post = array(), array $context = array()
) {
- if (!empty($params)) {
+ if (!empty($query)) {
$url .= '?' . http_build_query($query);
}
return $this->request($url, $context);
}
-}
+}
\ No newline at end of file
--- /dev/null
+<?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);
+ }
+}
--- /dev/null
+<?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;
+ }
+}
/**
* Handles requests for incrementation or decrementation of a maintained list
- * of counters for specified terms and antithrottling to prevent extreme
- * inflation or depression of counters by any single individual.
+ * of counters for specified terms.
*
* @category Phergie
* @package Phergie_Plugin_Karma
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Karma
+ * @uses extension PDO
+ * @uses extension pdo_sqlite
+ * @uses Phergie_Plugin_Command pear.phergie.org
+ * @uses Phergie_Plugin_Message pear.phergie.org
*/
class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
{
/**
- * Stores the SQLite object
+ * SQLite object
*
* @var resource
*/
protected $db = null;
/**
- * Retains the last garbage collection date
+ * Prepared statement to add a new karma record
*
- * @var array
+ * @var PDOStatement
*/
- protected $lastGc = null;
+ protected $insertKarma;
/**
- * Logs the karma usages and limits users to one karma change per word
- * and per day
+ * Prepared statement to update an existing karma record
*
- * @return void
+ * @var PDOStatement
*/
- protected $log = array();
+ protected $updateKarma;
/**
- * Some fixed karma values, keys must be lowercase
+ * Retrieves an existing karma record
*
- * @var array
+ * @var PDOStatement
*/
- protected $fixedKarma;
+ protected $fetchKarma;
/**
- * A list of blacklisted values
+ * Retrieves an existing fixed karma record
*
- * @var array
- */
- protected $karmaBlacklist;
-
- /**
- * Answers for correct assertions
+ * @var PDOStatement
*/
- protected $positiveAnswers;
+ protected $fetchFixedKarma;
/**
- * Answers for incorrect assertions
+ * Retrieves a positive answer for a karma comparison
+ *
+ * @var PDOStatement
*/
- protected $negativeAnswers;
+ protected $fetchPositiveAnswer;
/**
- * Prepared PDO statements
+ * Retrieves a negative answer for a karma comparison
*
* @var PDOStatement
*/
- protected $insertKarma;
- protected $updateKarma;
- protected $fetchKarma;
- protected $insertComment;
+ protected $fetchNegativeAnswer;
/**
- * Connects to the database containing karma ratings and initializes
- * class properties.
+ * Check for dependencies and initializes a database connection and
+ * prepared statements.
*
* @return void
*/
public function onLoad()
{
- $this->db = null;
- $this->lastGc = null;
- $this->log = array();
+ $plugins = $this->getPluginHandler();
+ $plugins->getPlugin('Command');
+ $plugins->getPlugin('Message');
- if(!defined('M_EULER')) {
- define('M_EULER', '0.57721566490153286061');
- }
+ $file = dirname(__FILE__) . '/Karma/karma.db';
+ $this->db = new PDO('sqlite:' . $file);
- $this->fixedKarma = array(
- 'phergie' => '%s has karma of awesome',
- 'pi' => '%s has karma of ' . M_PI,
- 'Î ' => '%s has karma of ' . M_PI,
- 'Ï€' => '%s has karma of ' . M_PI,
- 'chucknorris' => '%s has karma of Warning: Integer out of range',
- 'chuck norris' => '%s has karma of Warning: Integer out of range',
- 'c' => '%s has karma of 299 792 458 m/s',
- 'e' => '%s has karma of ' . M_E,
- 'euler' => '%s has karma of ' . M_EULER,
- 'mole' => '%s has karma of 6.02214e23 molecules',
- 'avogadro' => '%s has karma of 6.02214e23 molecules',
- 'spoon' => '%s has no karma. There is no spoon',
- 'mc^2' => '%s has karma of E',
- 'mc2' => '%s has karma of E',
- 'mc²' => '%s has karma of E',
- 'i' => '%s haz big karma',
- 'karma' => 'The karma law says that all living creatures are responsible for their karma - their actions and the effects of their actions. You should watch yours.'
- );
-
- $this->karmaBlacklist = array(
- '*',
- 'all',
- 'everything'
- );
-
- $this->positiveAnswers = array(
- 'No kidding, %owner% totally kicks %owned%\'s ass !',
- 'True that.',
- 'I concur.',
- 'Yay, %owner% ftw !',
- '%owner% is made of WIN!',
- 'Nothing can beat %owner%!',
- );
-
- $this->negativeAnswers = array(
- 'No sir, not at all.',
- 'You\'re wrong dude, %owner% wins.',
- 'I\'d say %owner% is better than %owned%.',
- 'You must be joking, %owner% ftw!',
- '%owned% is made of LOSE!',
- '%owned% = Epic Fail',
- );
-
- // Load or initialize the database
- $class = new ReflectionClass(get_class($this));
- $dir = dirname($class->getFileName() . '/' . $this->name);
- $this->db = new PDO('sqlite:' . $dir . 'karma.db');
-
- // Check to see if the table exists
- $table = $this->db->query('
- SELECT COUNT(*)
- FROM sqlite_master
- WHERE name = ' . $this->db->quote('karmas')
- )->fetchColumn();
-
- // Create database tables if necessary
- if (!$table) {
- $this->db->query('
- CREATE TABLE karmas ( word VARCHAR ( 255 ), karma MEDIUMINT );
- CREATE UNIQUE INDEX word ON karmas ( word );
- CREATE INDEX karmaIndex ON karmas ( karma );
- CREATE TABLE comments ( wordid INT , comment VARCHAR ( 255 ) );
- CREATE INDEX wordidIndex ON comments ( wordid );
- CREATE UNIQUE INDEX commentUnique ON comments ( comment );
- ');
- }
+ $this->fetchKarma = $this->db->prepare('
+ SELECT karma
+ FROM karmas
+ WHERE term = :term
+ LIMIT 1
+ ');
$this->insertKarma = $this->db->prepare('
- INSERT INTO karmas (
- word,
- karma
- )
- VALUES (
- :word,
- :karma
- )
+ INSERT INTO karmas (term, karma)
+ VALUES (:term, :karma)
');
- $this->insertComment = $this->db->prepare('
- INSERT INTO comments (
- wordid,
- comment
- )
- VALUES (
- :wordid,
- :comment
- )
+ $this->updateKarma = $this->db->prepare('
+ UPDATE karmas
+ SET karma = :karma
+ WHERE term = :term
');
- $this->fetchKarma = $this->db->prepare('
- SELECT karma, ROWID id FROM karmas WHERE LOWER(word) = LOWER(:word) LIMIT 1
+ $this->fetchFixedKarma = $this->db->prepare('
+ SELECT karma
+ FROM fixed_karmas
+ WHERE term = :term
+ LIMIT 1
');
- $this->updateKarma = $this->db->prepare('
- UPDATE karmas SET karma = :karma WHERE LOWER(word) = LOWER(:word)
+ $this->fetchPositiveAnswer = $this->db->prepare('
+ SELECT answer
+ FROM positive_answers
+ ORDER BY RANDOM()
+ LIMIT 1
+ ');
+
+ $this->fetchNegativeAnswer = $this->db->prepare('
+ SELECT answer
+ FROM negative_answers
+ ORDER BY RANDOM()
+ LIMIT 1
');
}
/**
- * Checks for dependencies.
+ * Get the canonical form of a given term.
*
- * @return void
+ * In the canonical form all sequences of whitespace
+ * are replaced by a single space and all characters
+ * are lowercased.
+ *
+ * @param string $term Term for which a canonical form is required
+ *
+ * @return string Canonical term
*/
- public static function onLoad()
+ protected function getCanonicalTerm($term)
{
- if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
- $this->fail('PDO and pdo_sqlite extensions must be installed');
- }
+ $canonicalTerm = strtolower(preg_replace('|\s+|', ' ', trim($term, '()')));
+ switch ($canonicalTerm) {
+ case 'me':
+ $canonicalTerm = strtolower($this->event->getNick());
+ break;
+ case 'all':
+ case '*':
+ case 'everything':
+ $canonicalTerm = 'everything';
+ break;
+ }
+ return $canonicalTerm;
}
/**
- * Handles requests for incrementation, decrementation, or lookup of karma
- * ratings sent via messages from users.
+ * Intercepts a message and processes any contained recognized commands.
*
* @return void
*/
public function onPrivmsg()
{
- $source = $this->event->getSource();
- $message = $this->event->getArgument(1);
- $target = $this->event->getNick();
-
- // Command prefix check
- $prefix = preg_quote(trim($this->getConfig('command.prefix')));
- $bot = preg_quote($this->getConfig('connections.nick'));
- $exp = '(?:(?:' . $bot . '\s*[:,>]?\s+(?:' . $prefix . ')?)|(?:' . $prefix . '))';
-
- // Karma status request
- if (preg_match('#^' . $exp . 'karma\s+(.+)$#i', $message, $m)) {
- // Return user's value if "me" is requested
- if (strtolower($m[1]) === 'me') {
- $m[1] = $target;
- }
- // Clean the term
- $term = $this->doCleanWord($m[1]);
-
- // Check the blacklist
- if (is_array($this->karmaBlacklist) && in_array($term, $this->karmaBlacklist)) {
- $this->doNotice($target, $term . ' is blacklisted');
- return;
- }
-
- // Return fixed value if set
- if (isset($this->fixedKarma[$term])) {
- $this->doPrivmsg($source, $target . ': ' . sprintf($this->fixedKarma[$term], $m[1]) . '.');
- return;
- }
-
- // Return current karma or neutral if not set yet
- $this->fetchKarma->execute(array(':word'=>$term));
- $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
-
- // Sanity check if someone if someone prefixed their conversation with karma
- if (!$res && substr_count($term, ' ') > 1 && !(substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')')) {
- return;
- }
-
- // Clean the raw term if it was contained within brackets
- if (substr($m[1], 0, 1) === '(' && substr($m[1], -1) === ')') {
- $m[1] = substr($m[1], 1, -1);
- }
-
- if ($res && $res['karma'] != 0) {
- $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has karma of ' . $res['karma'] . '.');
- } else {
- $this->doPrivmsg($source, $target . ': ' . $m[1] . ' has neutral karma.');
- }
- // Incrementation/decrementation request
- } elseif (preg_match('{^' . $exp . '?(?:(\+{2,2}|-{2,2})(\S+?|\(.+?\)+)|(\S+?|\(.+?\)+)(\+{2,2}|-{2,2}))(?:\s+(.*))?$}ix', $message, $m)) {
- if (!empty($m[4])) {
- $m[1] = $m[4]; // Increment/Decrement
- $m[2] = $m[3]; // Word
- }
- $m[3] = (isset($m[5]) ? $m[5] : null); // Comment
- unset($m[4], $m[5]);
- list(, $sign, $word, $comment) = array_pad($m, 4, null);
-
- // Clean the word
- $word = strtolower($this->doCleanWord($word));
- if (empty($word)) {
- return;
- }
-
- // Do nothing if the karma is fixed or blacklisted
- if (isset($this->fixedKarma[$word]) ||
- is_array($this->karmaBlacklist) && in_array($word, $this->karmaBlacklist)) {
- return;
- }
-
- // Force a decrementation if someone tries to update his own karma
- if ($word == strtolower($target) && $sign != '--' && !$this->fromAdmin(true)) {
- $this->doNotice($target, 'Bad ' . $target . '! You can not modify your own Karma. Shame on you!');
- $sign = '--';
- }
-
- // Antithrottling check
- $host = $this->event->getHost();
- $limit = $this->getConfig('karma.limit');
- // This is waiting on the Acl plugin from Elazar, being bypassed for now
- //if ($limit > 0 && !$this->fromAdmin()) {
- if ($limit > 0) {
- if (isset($this->log[$host][$word]) && $this->log[$host][$word] >= $limit) {
- // Three strikes, you're out, so lets decrement their karma for spammage
- if ($this->log[$host][$word] == ($limit+3)) {
- $this->doNotice($target, 'Bad ' . $target . '! Didn\'t I tell you that you reached your limit already?');
- $this->log[$host][$word] = $limit;
- $word = $target;
- $sign = '--';
- // Toss a notice to the user if they reached their limit
- } else {
- $this->doNotice($target, 'You have currently reached your limit in modifying ' . $word . ' for this day, please wait a bit.');
- $this->log[$host][$word]++;
- return;
- }
- } else {
- if (isset($this->log[$host][$word])) {
- $this->log[$host][$word]++;
- } else {
- $this->log[$host][$word] = 1;
- }
- }
- }
-
- // Get the current value then update or create entry
- $this->fetchKarma->execute(array(':word'=>$word));
- $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
- if ($res) {
- $karma = ($res['karma'] + ($sign == '++' ? 1 : -1));
- $args = array(
- ':word' => $word,
- ':karma' => $karma
- );
- $this->updateKarma->execute($args);
- } else {
- $karma = ($sign == '++' ? '1' : '-1');
- $args = array(
- ':word' => $word,
- ':karma' => $karma
- );
- $this->insertKarma->execute($args);
- $this->fetchKarma->execute(array(':word'=>$word));
- $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
- }
- $id = $res['id'];
- // Add comment
- $comment = preg_replace('{(?:^//(.*)|^#(.*)|^/\*(.*?)\*/$)}', '$1$2$3', $comment);
- if (!empty($comment)) {
- $this->insertComment->execute(array(':wordid' => $id, ':comment' => $comment));
- }
- // Perform garbage collection on the antithrottling log if needed
- if (date('d') !== $this->lastGc) {
- $this->doGc();
- }
- // Assertion request
- } elseif (preg_match('#^' . $exp . '?([^><]+)(<|>)([^><]+)$#', $message, $m)) {
- // Trim words
- $word1 = strtolower($this->doCleanWord($m[1]));
- $word2 = strtolower($this->doCleanWord($m[3]));
- $operator = $m[2];
-
- // Do nothing if the karma is fixed
- if (isset($this->fixedKarma[$word1]) || isset($this->fixedKarma[$word2]) ||
- empty($word1) || empty($word2)) {
- return;
- }
-
- // Fetch first word
- if ($word1 === '*' || $word1 === 'all' || $word1 === 'everything') {
- $res = array('karma' => 0);
- $word1 = 'everything';
- } else {
- $this->fetchKarma->execute(array(':word'=>$word1));
- $res = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
- }
- // If it exists, fetch second word
- if ($res) {
- if ($word2 === '*' || $word2 === 'all' || $word2 === 'everything') {
- $res2 = array('karma' => 0);
- $word2 = 'everything';
- } else {
- $this->fetchKarma->execute(array(':word'=>$word2));
- $res2 = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
- }
- // If it exists, compare and return value
- if ($res2 && $res['karma'] != $res2['karma']) {
- $assertion = ($operator === '<' && $res['karma'] < $res2['karma']) || ($operator === '>' && $res['karma'] > $res2['karma']);
- // Switch arguments if they are in the wrong order
- if ($operator === '<') {
- $tmp = $word2;
- $word2 = $word1;
- $word1 = $tmp;
- }
- $this->doPrivmsg($source, $assertion ? $this->fetchPositiveAnswer($word1, $word2) : $this->fetchNegativeAnswer($word1, $word2));
- // If someone asserts that something is greater or lesser than everything, we increment/decrement that something at the same time
- if ($word2 === 'everything') {
- $this->event = clone$this->event;
- $this->event->setArguments(array($this->event->getArgument(0), '++'.$word1));
- $this->onPrivmsg();
- } elseif ($word1 === 'everything') {
- $this->event = clone$this->event;
- $this->event->setArguments(array($this->event->getArgument(0), '--'.$word2));
- $this->onPrivmsg();
- }
- }
- }
+ $message = $this->getEvent()->getText();
+
+ $termPattern = '\S+?|\([^<>]+?\)+';
+ $actionPattern = '(?P<action>\+\+|--)';
+
+ $modifyPattern = <<<REGEX
+ {^
+ (?J) # allow overwriting capture names
+ \s* # ignore leading whitespace
+
+ (?: # start with ++ or -- before the term
+ $actionPattern
+ (?P<term>$termPattern)
+ | # follow the term with ++ or --
+ (?P<term>$termPattern)
+ $actionPattern # allow no whitespace between the term and the action
+ )
+ $}ix
+REGEX;
+
+ $versusPattern = <<<REGEX
+ {^
+ (?P<term0>$termPattern)
+ \s+(?P<method><|>)\s+
+ (?P<term1>$termPattern)$#
+ $}ix
+REGEX;
+
+ $match = null;
+
+ if (preg_match($modifyPattern, $message, $match)) {
+ $action = $match['action'];
+ $term = $this->getCanonicalTerm($match['term']);
+ $this->modifyKarma($term, $action);
+ } elseif (preg_match($versusPattern, $message, $match)) {
+ $term0 = trim($match['term0']);
+ $term1 = trim($match['term1']);
+ $method = $match['method'];
+ $this->compareKarma($term0, $term1, $method);
}
}
- protected function fetchPositiveAnswer($owner, $owned)
+ /**
+ * Get the karma rating for a given term.
+ *
+ * @param string $term Term for which the karma rating needs to be
+ * retrieved
+ *
+ * @return void
+ */
+ public function onCommandKarma($term)
{
- return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->positiveAnswers[array_rand($this->positiveAnswers,1)]);
+ $source = $this->getEvent()->getSource();
+ $nick = $this->getEvent()->getNick();
+
+ if (empty($term)) {
+ return;
+ }
+
+ $canonicalTerm = $this->getCanonicalTerm($term);
+
+ $fixedKarma = $this->fetchFixedKarma($canonicalTerm);
+ if ($fixedKarma) {
+ $message = $nick . ': ' . $term . $fixedKarma . '.';
+ $this->doPrivmsg($source, $message);
+ return;
+ }
+
+ $karma = $this->fetchKarma($canonicalTerm);
+
+ $message = $nick . ': ';
+
+ if ($term == 'me') {
+ $message .= 'You have';
+ } else {
+ $message .= $term . ' has';
+ }
+
+ $message .= ' ';
+
+ if ($karma) {
+ $message .= 'karma of ' . $karma;
+ } else {
+ $message .= 'neutral karma';
+ }
+
+ $message .= '.';
+
+ $this->doPrivmsg($source, $message);
}
- protected function fetchNegativeAnswer($owned, $owner)
+ /**
+ * Resets the karma for a term to 0.
+ *
+ * @param string $term Term for which to reset the karma rating
+ *
+ * @return void
+ */
+ public function onCommandReincarnate($term)
{
- return str_replace(array('%owner%','%owned%'), array($owner, $owned), $this->negativeAnswers[array_rand($this->negativeAnswers,1)]);
+ $data = array(
+ ':term' => $term,
+ ':karma' => 0
+ );
+ $this->updateKarma->execute($data);
}
- protected function doCleanWord($word)
+ /**
+ * Compares the karma between two terms. Optionally increases/decreases
+ * the karma of either term.
+ *
+ * @param string $term0 First term
+ * @param string $term1 Second term
+ * @param string $method Comparison method (< or >)
+ *
+ * @return void
+ */
+ protected function compareKarma($term0, $term1, $method)
{
- $word = trim($word);
- if (substr($word, 0, 1) === '(' && substr($word, -1) === ')') {
- $word = trim(substr($word, 1, -1));
+ $event = $this->getEvent();
+ $nick = $event->getNick();
+ $source = $event->getSource();
+
+ $canonicalTerm0 = $this->getCanonicalTerm($term0);
+ $canonicalTerm1 = $this->getCanonicalTerm($term1);
+
+ $fixedKarma0 = $this->fetchFixedKarma($canonicalTerm0);
+ $fixedKarma1 = $this->fetchFixedKarma($canonicalTerm1);
+
+ if ($fixedKarma0
+ || $fixedKarma1
+ || empty($canonicalTerm0)
+ || empty($canonicalTerm1)
+ ) {
+ return;
+ }
+
+ if ($canonicalTerm0 == 'everything') {
+ $change = $method == '<' ? '++' : '--';
+ $this->modifyKarma($canonicalTerm1, $change);
+ $karma0 = 0;
+ $karma1 = $this->fetchKarma($canonicalTerm1);
+ } elseif ($canonicalTerm1 == 'everything') {
+ $change = $method == '<' ? '--' : '++';
+ $this->modifyKarma($canonicalTerm0, $change);
+ $karma0 = $this->fetchKarma($canonicalTerm1);
+ $karma1 = 0;
+ } else {
+ $karma0 = $this->fetchKarma($canonicalTerm0);
+ $karma1 = $this->fetchKarma($canonicalTerm1);
}
- $word = preg_replace('#\s+#', ' ', strtolower(trim($word)));
- return $word;
+
+ if (($method == '<'
+ && $karma0 < $karma1)
+ || ($method == '>'
+ && $karma0 > $karma1)) {
+ $replies = $this->fetchPositiveAnswer;
+ } else {
+ $replies = $this->fetchNegativeAnswer;
+ }
+ $replies->execute();
+ $reply = $replies->fetchColumn();
+
+ if (max($karma0, $karma1) == $karma1) {
+ list($canonicalTerm0, $canonicalTerm1) =
+ array($canonicalTerm1, $canonicalTerm0);
+ }
+
+ $message = str_replace(
+ array('%owner%','%owned%'),
+ array($canonicalTerm0, $canonicalTerm1),
+ $reply
+ );
+
+ $this->doPrivmsg($source, $message);
}
/**
- * Performs garbage collection on the antithrottling log.
+ * Modifes a term's karma.
+ *
+ * @param string $term Term to modify
+ * @param string $action Karma action (either ++ or --)
*
* @return void
*/
- public function doGc()
+ protected function modifyKarma($term, $action)
{
- unset($this->log);
- $this->log = array();
- $this->lastGc = date('d');
+ if (empty($term)) {
+ return;
+ }
+
+ $karma = $this->fetchKarma($term);
+ if ($karma !== false) {
+ $statement = $this->updateKarma;
+ } else {
+ $statement = $this->insertKarma;
+ }
+
+ $karma += ($action == '++') ? 1 : -1;
+
+ $args = array(
+ ':term' => $term,
+ ':karma' => $karma
+ );
+ $statement->execute($args);
+ }
+
+ /**
+ * Returns the karma rating for a specified term for which the karma
+ * rating can be modified.
+ *
+ * @param string $term Term for which to fetch the corresponding karma
+ * rating
+ *
+ * @return integer|boolean Integer value denoting the term's karma or
+ * FALSE if there is the specified term has no associated karma
+ * rating
+ */
+ protected function fetchKarma($term)
+ {
+ $this->fetchKarma->execute(array(':term' => $term));
+ $result = $this->fetchKarma->fetch(PDO::FETCH_ASSOC);
+
+ if ($result === false) {
+ return false;
+ }
+
+ return (int) $result['karma'];
+ }
+
+ /**
+ * Returns a phrase describing the karma rating for a specified term for
+ * which the karma rating is fixed.
+ *
+ * @param string $term Term for which to fetch the corresponding karma
+ * rating
+ *
+ * @return string Phrase describing the karma rating, which may be append
+ * to the term to form a complete response
+ */
+ protected function fetchFixedKarma($term)
+ {
+ $this->fetchFixedKarma->execute(array(':term' => $term));
+ $result = $this->fetchFixedKarma->fetch(PDO::FETCH_ASSOC);
+
+ if ($result === false) {
+ return false;
+ }
+
+ return $result['karma'];
}
}
--- /dev/null
+<?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];
+ }
+}
\ No newline at end of file
{
$time = time();
if (!empty($this->lastPing)) {
- if ($time - $this->lastPing > $this->getConfig('ping.ping', 10)) {
+ if ($time - $this->lastPing > $this->getConfig('ping.ping', 20)) {
$this->doQuit();
}
} elseif (
/**
* Set the last ping time
* lastPing needs to be exposed for unit testing
- *
+ *
* @param int|null $ping timestamp of last ping
*
* @return self
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Quit
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Terminates the current connection upon command.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie_Plugin_Quit
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
* @link http://pear.phergie.org/package/Phergie_Plugin_Quit
* @uses Phergie_Plugin_Command pear.phergie.org
- *
- * @pluginDesc Terminates the current connection upon command.
*/
class Phergie_Plugin_Quit extends Phergie_Plugin_Abstract
{
public function onLoad()
{
$this->getPluginHandler()->getPlugin('Command');
- $help = $this->getPluginHandler()->getPlugin('Help');
- $help->register($this);
}
/**
- * Issues a quit command when a message is received requesting that the
+ * Issues a quit command when a message is received requesting that the
* bot terminate the current connection.
*
* @return void
- *
- * @pluginCmd terminates the connection
*/
public function onCommandQuit()
{
if (!$this->plugins->hasPlugin($plugin)) {
echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL;
$this->plugins->getPlugin($plugin);
+ $this->plugins->command->populateMethodCache();
return;
}
}
$contents = preg_replace(
- array('/<\?(?:php)?/', '/class\s+' . $class . '/i'),
+ array('/^<\?(?:php)?/', '/class\s+' . $class . '/i'),
array('', 'class ' . $newClass),
$contents
);
$instance = new $newClass;
$instance->setName($plugin);
+ $instance->setEvent($this->event);
$this->plugins
->removePlugin($plugin)
->addPlugin($instance);
+ $this->plugins->command->populateMethodCache();
+ if ($this->plugins->hasPlugin('Help')) {
+ $this->plugins->help->populateRegistry();
+ }
+
echo 'DEBUG(Reload): Reloaded ', $class, ' to ', $newClass, PHP_EOL;
}
}
$item = $this->getItem($database, $table, $match);
// Reprocess the request for censorship if required
- $attempts = 0;
- while ($censor && $attempts < 3) {
+ if ($this->plugins->hasPlugin('Censor')) {
$plugin = $this->plugins->getPlugin('Censor');
- $clean = $plugin->cleanString($item->name);
- if ($item->name != $clean) {
- $attempts++;
- $item = $this->getItem($database, $table, $match);
- } else {
- $censor = false;
+ $attempts = 0;
+ while ($censor && $attempts < 3) {
+ $clean = $plugin->cleanString($item->name);
+ if ($item->name != $clean) {
+ $attempts++;
+ $item = $this->getItem($database, $table, $match);
+ } else {
+ $censor = false;
+ }
+ }
+ if ($censor && $attempts == 3) {
+ $this->doAction($this->event->getSource(), 'shrugs.');
}
- }
-
- if ($censor && $attempts == 3) {
- $this->doAction($this->event->getSource(), 'shrugs.');
}
// Derive the proper article for the item
--- /dev/null
+<?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
+ );
+ }
+}
--- /dev/null
+<?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);
* @link http://thefuckingweather.com
* @uses Phergie_Plugin_Command 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
*
* @return void
* @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)
{
* @param string $location Location term
*
* @return void
- * @pluginCmd [location] Alias for thefuckingweather command.
*/
public function onCommandTfw($location)
{
* @link http://pear.phergie.org/package/Phergie_Plugin_Tld
* @uses extension PDO
* @uses extension pdo_sqlite
- *
- * @pluginDesc Provides information for a top level domain.
*/
class Phergie_Plugin_Tld extends Phergie_Plugin_Abstract
{
/**
- * connection to the database
+ * Connection to the database
+ *
* @var PDO
*/
protected $db;
/**
- * Prepared statement for selecting a single tld
+ * Prepared statement for selecting a single TLD
+ *
* @var PDOStatement
*/
protected $select;
/**
- * Prepared statement for selecting all tlds
+ * Prepared statement for selecting all TLDs
+ *
* @var PDOStatement
*/
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
*/
$this->fail('PDO and pdo_sqlite extensions must be installed');
}
- $help = $this->getPluginHandler()->getPlugin('Help');
- $help->register($this);
-
$dbFile = dirname(__FILE__) . '/Tld/tld.db';
try {
$this->db = new PDO('sqlite:' . $dbFile);
* @param string $tld tld to process
*
* @return null
- *
- * @pluginCmd .[tld] request details about the tld
*/
public function onCommandTld($tld)
{
{
switch ($parsed['host']) {
case 'youtu.be':
- $v = ltrim('/', $parsed['path']);
+ $v = ltrim($parsed['path'], '/');
break;
case 'youtube.com':
case 'www.youtube.com':
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
/**
* Base class for obtaining and processing incoming events.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
protected $driver;
/**
- * Current connection handler instance
+ * Current connection handler instance
*
- * @var Phergie_Connection_Handler
+ * @var Phergie_Connection_Handler
*/
protected $connections;
/**
* Gets the required class refences from Phergie_Bot.
*
- * @param Phergie_Bot $bot Current bot instance in use
+ * @param Phergie_Bot $bot Current bot instance in use
* @param array $options Optional processor arguments
- *
+ *
* @return void
*/
public function __construct(Phergie_Bot $bot, array $options = array())
*/
protected function processEvents(Phergie_Connection $connection)
{
+ $this->plugins->preDispatch();
if (count($this->events)) {
- $this->plugins->preDispatch();
foreach ($this->events as $event) {
$this->ui->onCommand($event, $connection);
$event->getArguments()
);
}
- $this->plugins->postDispatch();
-
- if ($this->events->hasEventOfType(Phergie_Event_Request::TYPE_QUIT)) {
- $this->ui->onQuit($connection);
- $this->connections->removeConnection($connection);
- }
+ }
+ $this->plugins->postDispatch();
- $this->events->clearEvents();
+ if ($this->events->hasEventOfType(Phergie_Event_Request::TYPE_QUIT)) {
+ $this->ui->onQuit($connection);
+ $this->connections->removeConnection($connection);
}
+
+ $this->events->clearEvents();
}
/**
if ($event = $this->driver->getEvent()) {
$this->ui->onEvent($event, $connection);
$this->plugins->setEvent($event);
-
- if (!$this->plugins->preEvent()) {
- continue;
- }
-
+ $this->plugins->preEvent();
$this->plugins->{'on' . ucfirst($event->getType())}();
}
<?php
/**
- * Phergie
+ * Phergie
*
* PHP version 5
*
* It is also available through the world-wide-web at this URL:
* http://phergie.org/license
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @copyright 2008-2010 Phergie Development Team (http://phergie.org)
* Connection data processor which reads all connections looking
* for a response.
*
- * @category Phergie
+ * @category Phergie
* @package Phergie
* @author Phergie Development Team <team@phergie.org>
* @license http://phergie.org/license New BSD License
class Phergie_Process_Standard extends Phergie_Process_Abstract
{
/**
- * Obtains and processes incoming events, then sends resulting outgoing
+ * Obtains and processes incoming events, then sends resulting outgoing
* events.
*
* @return void
if ($event = $this->driver->getEvent()) {
$this->ui->onEvent($event, $connection);
$this->plugins->setEvent($event);
-
- if (!$this->plugins->preEvent()) {
- continue;
- }
-
+ $this->plugins->preEvent();
$this->plugins->{'on' . ucfirst($event->getType())}();
}