Changeset 60
- Timestamp:
- 02/17/08 04:25:26 (5 years ago)
- Location:
- trunk/Phergie
- Files:
-
- 1 removed
- 23 modified
- 1 moved
-
Bot.php (modified) (6 diffs)
-
CONTRIBUTE (deleted)
-
Driver/Abstract.php (modified) (1 diff)
-
Driver/Streams.php (modified) (3 diffs)
-
Event/Handler.php (modified) (3 diffs)
-
Event/Request.php (modified) (14 diffs)
-
Plugin/Abstract/AdminCommand.php (modified) (3 diffs)
-
Plugin/Abstract/Command.php (modified) (2 diffs)
-
Plugin/Acronym.php (modified) (5 diffs)
-
Plugin/Autojoin.php (modified) (1 diff)
-
Plugin/Daddy.php (modified) (1 diff)
-
Plugin/Debug.php (modified) (1 diff)
-
Plugin/Drink.php (modified) (7 diffs)
-
Plugin/Karma.php (modified) (4 diffs)
-
Plugin/Lart.php (modified) (5 diffs)
-
Plugin/Logging.php (modified) (6 diffs)
-
Plugin/Math.php (modified) (2 diffs)
-
Plugin/Nickserv.php (modified) (1 diff)
-
Plugin/Php.php (modified) (2 diffs)
-
Plugin/Puppet.php (moved) (moved from trunk/Phergie/Plugin/Say.php) (2 diffs)
-
Plugin/UrbanDictionary.php (modified) (2 diffs)
-
Plugin/Url.php (modified) (2 diffs)
-
Plugin/Users.php (modified) (3 diffs)
-
README (modified) (4 diffs)
-
phergie.ini (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Phergie/Bot.php
r59 r60 30 30 31 31 /** 32 * Path to the directory containing the Phergie directory 33 * 34 * @const string 35 */ 36 define('PHERGIE_DIR', dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR); 37 38 /** 32 39 * Add the Phergie directory to the include path 33 40 */ … … 35 42 get_include_path() 36 43 . PATH_SEPARATOR . 37 dirname(dirname(__FILE__))44 PHERGIE_DIR 38 45 ); 39 46 … … 62 69 */ 63 70 $required = array('server', 'username', 'nick'); 64 $config = @parse_ini_file( $ini);71 $config = @parse_ini_file(PHERGIE_DIR . 'Phergie' . DIRECTORY_SEPARATOR . $ini); 65 72 66 73 if (count($config) == 0) { … … 70 77 71 78 foreach ($required as $setting) { 72 if (!isset($config[$setting]) ) {79 if (!isset($config[$setting]) || empty($config[$setting])) { 73 80 echo 'Required configuration setting missing: ' . $setting . "\n"; 74 81 return; … … 113 120 * Set up event handlers 114 121 */ 115 $iterator = new DirectoryIterator( 'Plugin');122 $iterator = new DirectoryIterator(PHERGIE_DIR . '/Phergie/Plugin'); 116 123 foreach ($iterator as $entry) { 117 124 if ($iterator->isFile() … … 120 127 require_once 'Phergie/Plugin/' . $entry; 121 128 $class = 'Phergie_Plugin_' . str_replace('.php', '', $entry); 122 $client->addEventHandler(new $class($client)); 129 $instance = new $class($client); 130 $client->addEventHandler($instance); 131 $client->debug('Loaded ' . $instance->getName()); 123 132 } 124 133 } -
trunk/Phergie/Driver/Abstract.php
r59 r60 76 76 { 77 77 if ($this->getIni('debug')) { 78 echo date('H:i:s') . ' driver' . $message . "\n";78 echo date('H:i:s') . ' ' . $message . "\n"; 79 79 } 80 80 } -
trunk/Phergie/Driver/Streams.php
r59 r60 152 152 153 153 switch ($cmd) { 154 case 'names': 155 case 'nick': 156 case 'quit': 157 case 'ping': 158 case 'join': 159 $args = array(ltrim($args, ':')); 160 break; 161 case 'notice': 162 $args = explode(' :', $args); 163 break; 154 164 case 'oper': 155 165 case 'topic': 156 166 case 'mode': 157 $args = explode(' ', $args); 158 break; 159 case 'join': 160 $args = explode(' ', $args); 161 $args[0] = explode(',', $args[0]); 162 if (isset ($args[1])) { 163 $args[1] = explode(',', $args[1]); 164 } 167 $args = preg_split('/ :?/', $args); 165 168 break; 166 169 case 'part': 167 case 'names': 168 $args = array(explode(',', $args)); 169 break; 170 case 'nick': 171 case 'quit': 172 case 'ping': 173 $args = array(ltrim($args, ':')); 174 break; 175 case 'notice': 176 $args = explode(' :', $args); 170 $args = preg_split('/ :?/', $args, 2); 171 break; 172 case 'kick': 173 $args = preg_split('/ :?/', $args, 3); 177 174 break; 178 175 case 'privmsg': … … 188 185 } else { 189 186 $args = explode(' :', $args); 190 $args[0] = explode(',', $args[0]);191 187 } 192 188 break; … … 234 230 } 235 231 $this->doQuit($reason); 232 break; 236 233 } 237 234 unset($this->queue, $event, $command, $arguments); -
trunk/Phergie/Event/Handler.php
r59 r60 23 23 { 24 24 /** 25 * Flag indicating whether or not the plugin requires its own directory 26 * for local storage 27 * 28 * @see $dir 29 * @var bool 30 */ 31 protected $needsDir = false; 32 33 /** 34 * Path to the directory for the plugin if needsDir is enabled 35 * 36 * @see $needsDir 37 */ 38 protected $dir; 39 40 /** 25 41 * Reference back to the client used to initiate commands 26 42 * … … 55 71 $name = get_class($this); 56 72 $this->name = substr($name, strrpos($name, '_') + 1); 73 74 if ($this->needsDir) { 75 $class = new ReflectionClass($name); 76 $dir = 77 dirname($class->getFilename()) . 78 DIRECTORY_SEPARATOR . 79 $this->name . 80 DIRECTORY_SEPARATOR; 81 if (!file_exists($dir)) { 82 mkdir($dir); 83 } 84 $this->dir = $dir; 85 } 57 86 } 58 87 … … 123 152 public function debug($message) 124 153 { 125 $this->client->debug( date('H:i:s') . ' ' . $this->name. ' -> ' . $message);154 $this->client->debug(strtolower($this->name) . ' -> ' . $message); 126 155 } 127 156 -
trunk/Phergie/Event/Request.php
r59 r60 10 10 /** 11 11 * Password message 12 * 13 * @const string 12 14 */ 13 15 const TYPE_PASS = 'pass'; … … 15 17 /** 16 18 * Nick message 19 * 20 * @const string 17 21 */ 18 22 const TYPE_NICK = 'nick'; … … 20 24 /** 21 25 * User message 26 * 27 * @const string 22 28 */ 23 29 const TYPE_USER = 'user'; … … 25 31 /** 26 32 * Operator command 33 * 34 * @const string 27 35 */ 28 36 const TYPE_OPER = 'oper'; … … 30 38 /** 31 39 * Quit command 40 * 41 * @const string 32 42 */ 33 43 const TYPE_QUIT = 'quit'; … … 35 45 /** 36 46 * Join message 47 * 48 * @const string 37 49 */ 38 50 const TYPE_JOIN = 'join'; 39 51 40 52 /** 53 * Kick message 54 * 55 * @const string 56 */ 57 const TYPE_KICK = 'kick'; 58 59 /** 41 60 * Part message 61 * 62 * @const string 42 63 */ 43 64 const TYPE_PART = 'part'; … … 45 66 /** 46 67 * Mode message 68 * 69 * @const string 47 70 */ 48 71 const TYPE_MODE = 'mode'; … … 50 73 /** 51 74 * Topic message 75 * 76 * @const string 52 77 */ 53 78 const TYPE_TOPIC = 'topic'; … … 55 80 /** 56 81 * Private message command 82 * 83 * @const string 57 84 */ 58 85 const TYPE_PRIVMSG = 'privmsg'; … … 60 87 /** 61 88 * Notice message 89 * 90 * @const string 62 91 */ 63 92 const TYPE_NOTICE = 'notice'; … … 65 94 /** 66 95 * Pong message 96 * 97 * @const string 67 98 */ 68 99 const TYPE_PONG = 'pong'; … … 70 101 /** 71 102 * CTCP ACTION command 103 * 104 * @const string 72 105 */ 73 106 const TYPE_ACTION = 'action'; … … 249 282 public function getSource() 250 283 { 251 if ($this->type == Phergie_Event_Request::TYPE_PRIVMSG 252 && $this->arguments[0][0] == '#') { 284 if ($this->arguments[0][0] == '#') { 253 285 return $this->arguments[0]; 254 286 } … … 257 289 258 290 /** 291 * Returns whether or not the event occurred within a channel. 292 * 293 * @return TRUE if the event is in a channel, FALSE otherwise 294 */ 295 public function isInChannel() 296 { 297 return (substr($this->getSource(), 0, 1) == '#'); 298 } 299 300 /** 259 301 * Returns whether or not the event originated from a user. 260 302 * -
trunk/Phergie/Plugin/Abstract/AdminCommand.php
r59 r60 82 82 $ini = $this->getIni('admins', 'admincommand'); 83 83 } 84 $admins = preg_split('#[\s\r\n,]+#', $ini); 85 foreach ($admins as $admin) { 86 // Find out which chars are present in the config mask and exclude them from the regex match 87 $excluded = ''; 88 if (strpos($admin, '!')!==false) { 89 $excluded .= '!'; 84 if (!empty ($ini)) { 85 $admins = preg_split('#[\s\r\n,]+#', $ini); 86 foreach ($admins as $admin) { 87 // Find out which chars are present in the config mask and exclude them from the regex match 88 $excluded = ''; 89 if (strpos($admin, '!')!==false) { 90 $excluded .= '!'; 91 } 92 if (strpos($admin, '@')!==false) { 93 $excluded .= '@'; 94 } 95 96 // Escape regex meta characters 97 $admin = str_replace( 98 array('\\', '^', '$', '.', '[', ']', '|', '(', ')', '?', '+', '{', '}'), 99 array('\\\\', '\\^', '\\$', '\\.', '\\[', '\\]', '\\|', '\\(', '\\)', '\\?', '\\+', '\\{', '\\}'), 100 $admin 101 ); 102 // Replace * so that they match correctly in a regex 103 $this->admins[$class][] = str_replace('*', ($excluded === '' ? '.*' : '[^'.$excluded.']*'), $admin); 90 104 } 91 if (strpos($admin, '@')!==false) { 92 $excluded .= '@'; 93 } 94 95 // Escape regex meta characters 96 $admin = str_replace( 97 array('\\', '^', '$', '.', '[', ']', '|', '(', ')', '?', '+', '{', '}'), 98 array('\\\\', '\\^', '\\$', '\\.', '\\[', '\\]', '\\|', '\\(', '\\)', '\\?', '\\+', '\\{', '\\}'), 99 $admin 100 ); 101 // Replace * so that they match correctly in a regex 102 $this->admins[$class][] = str_replace('*', ($excluded === '' ? '.*' : '[^'.$excluded.']*'), $admin); 105 $this->admins[$class] = '/^' . implode('|', $this->admins[$class]) . '$/'; 103 106 } 104 107 } … … 114 117 { 115 118 $class = $this->getName(); 116 $nick = $this->event->getSource(); 117 $mask = $this->event->getHostmask(); 119 // Try to match mask against admin masks 120 if (isset ($this->admins[$class]) 121 && preg_match($this->admins[$class], $this->event->getHostmask())) { 122 return true; 123 } 118 124 // Check if is op and ops are admins 119 if ($this->ops[$class]) { 120 foreach (Phergie_Plugin_Users::getChannels($nick) as $chan) { 121 if (Phergie_Plugin_Users::isOp($nick, $chan)) { 122 return true; 123 } 124 } 125 } 126 // Try to match mask against admin masks 127 if (isset ($this->admins[$class])) { 128 return in_array($mask, $this->admins[$class]); 125 $nick = $this->event->getNick(); 126 $source = $this->event->getSource(); 127 if ($this->ops[$class] && Phergie_Plugin_Users::isOp($nick, $source)) { 128 return true; 129 129 } 130 130 return false; … … 141 141 if ($this->fromAdmin()) { 142 142 $target = $this->event->getArgument(0); 143 $exp = $target[0] [0]== '#' ?143 $exp = $target[0] == '#' ? 144 144 '/^' . $this->getIni('nick') . '\s*:?\s+(.*)$/' : 145 145 '/^(?:' . $this->getIni('nick') . '\s*:?\s+)?(.*)$/'; -
trunk/Phergie/Plugin/Abstract/Command.php
r59 r60 45 45 $name = $method->getName(); 46 46 if (strpos($name, 'onDo') === 0) { 47 $this->methods[substr($name, 4)] = $method->getNumberOfRequiredParameters(); 47 $this->methods[strtolower(substr($name, 4))] = 48 $method->getNumberOfRequiredParameters(); 48 49 } 49 50 } … … 60 61 $this->$method(); 61 62 } 62 } else if ($this->methods[$command] <= count($params)){63 } else { 63 64 $params = preg_split('/\s+/', $params, $this->methods[$command]); 64 call_user_func_array(array($this, $method), $params); 65 if ($this->methods[$command] <= count($params)) { 66 call_user_func_array(array($this, $method), $params); 67 } 65 68 } 66 69 } -
trunk/Phergie/Plugin/Acronym.php
r59 r60 46 46 { 47 47 $limit = $this->getIni('limit'); 48 if ($limit < 0 ) {48 if ($limit < 0 || $limit === null) { 49 49 $this->limit = 5; 50 50 } else { … … 60 60 * @return void 61 61 */ 62 protected function _randomAction($target)62 protected function randomAction($target) 63 63 { 64 64 do { … … 87 87 $message = $this->event->getArgument(1); 88 88 89 if (!preg_match('/ ^(?:[^\s:]+\s*:?\s*)?((?:[A-Z]\.?){2,})\?$/', $message, $acronym)) {89 if (!preg_match('/((?:[A-Z]\.?){2,})\?/', $message, $acronym)) { 90 90 return; 91 91 } … … 110 110 $contents = @file_get_contents($url, false, $context); 111 111 112 if (empty($contents)) { 112 if (empty ($contents) 113 || strpos($contents, 'no abbreviation matches') !== false 114 || strpos($contents, 'has exceeded the daily query limit') !== false) { 113 115 $this->randomAction($target); 114 116 } else { … … 130 132 } while (($this->limit == 0 || count($matches) < $this->limit) && $count == 1); 131 133 132 if (count($matches) == 0) { 133 $this->randomAction($target); 134 } else { 135 $text = 'Possible matches for ' . $acronym . ': ' . implode(', ', $matches); 136 $this->doPrivmsg($target, $text); 137 } 134 $text = 'Possible matches for ' . $acronym . ': ' . implode(', ', $matches); 135 $this->doPrivmsg($target, $text); 138 136 } 139 137 } -
trunk/Phergie/Plugin/Autojoin.php
r59 r60 23 23 public function onResponse() 24 24 { 25 if ($this->event->getCode() == Phergie_Event_Response::RPL_ENDOFMOTD) { 26 $channels = $this->getIni('channels'); 27 if (!empty ($channels)) { 28 $this->doJoin(implode(' ', preg_split('#[, ]+#', $channels))); 29 } 25 switch ($this->event->getCode()) { 26 case Phergie_Event_Response::RPL_ENDOFMOTD: 27 case Phergie_Event_Response::ERR_NOMOTD: 28 $channels = $this->getIni('channels'); 29 if (!empty ($channels)) { 30 $this->doJoin(implode(' ', preg_split('#[, ]+#', $channels))); 31 } 32 break; 30 33 } 31 34 } -
trunk/Phergie/Plugin/Daddy.php
r59 r60 22 22 $bot = $this->getIni('nick'); 23 23 $text = $this->event->getArgument(1); 24 if (preg_match('/' . $bot . '\s*:?\s+?who\'?s your daddy\ \??/iAD', $text)) {25 $this->doPrivmsg($this-> getSource(), 'You\'re my daddy, ' . $this->event->getNick() . '!');24 if (preg_match('/' . $bot . '\s*:?\s+?who\'?s your daddy\??/iAD', $text)) { 25 $this->doPrivmsg($this->event->getSource(), 'You\'re my daddy, ' . $this->event->getNick() . '!'); 26 26 } 27 27 } -
trunk/Phergie/Plugin/Debug.php
r59 r60 21 21 { 22 22 $text = 'current : '.number_format(memory_get_usage() / 1024).'KB / peak : '.number_format(memory_get_peak_usage() / 1024).'KB'; 23 $this->doPrivmsg($this-> getSource(), $text);23 $this->doPrivmsg($this->event->getSource(), $text); 24 24 } 25 25 } -
trunk/Phergie/Plugin/Drink.php
r59 r60 18 18 class Phergie_Plugin_Drink extends Phergie_Plugin_Abstract_Command 19 19 { 20 /** 21 * Indicates that a local directory is required for this plugin 22 * 23 * @var bool 24 */ 25 protected $needsDir = true; 26 20 27 /** 21 28 * PDO resource for a SQLite database containing the drinks … … 76 83 77 84 /** 78 * Determines if a table does not exist or is empty.79 *80 * @param string $name Table name81 * @return bool TRUE if the table does not exist or is empty, FALSE82 * otherwise83 */84 private function needTable($name)85 {86 $table = $this->db87 ->query('SELECT COUNT(*) FROM sqlite_master WHERE name = ' . $this->db->quote($name))88 ->fetchColumn();89 90 if (!$table) {91 return true;92 }93 94 return !$this->db95 ->query('SELECT COUNT(*) FROM ' . $name)96 ->fetchColumn();97 }98 99 /**100 * Populates a source database table with a given set of data.101 *102 * @param string $table Name of the table103 * @param array $names List of drink names104 * @return void105 */106 private function populateTable($table, $names)107 {108 $this->db->exec('109 CREATE TABLE ' . $name . ' ( name VARCHAR(255) );110 CREATE UNIQUE INDEX ' . $name . ' ON ' . $name . ' (name);111 ');112 113 $stmt = $this->db->prepare('INSERT INTO ' . $table . ' (name) VALUES (:name)');114 $this->db->beginTransaction();115 foreach (array_unique($names) as $name) {116 foreach ($this->filter as $filter) {117 if (preg_match('/(^|[^a-z])' . $filter . '([^a-z]|$)/i', $name)) {118 $this->debug('Filtered out ' . $name . ' because it contains ' . $filter);119 continue 2;120 }121 }122 $this->debug('Inserted ' . $name);123 $stmt->execute(array('name' => $name));124 }125 $this->db->commit();126 }127 128 /**129 * Returns a random record value from a given table130 *131 * @string $table Name of the table132 * @return string Value of the name column for the selected record133 */134 private function getRandomRecord($table)135 {136 return $this->db137 ->query('SELECT name FROM ' . $table . ' ORDER BY Random() LIMIT 1')138 ->fetchColumn();139 }140 141 /**142 * Returns whether or not a given value has characters that may not be143 * displayed correctly144 *145 * @param string $name Value to check146 */147 private function hasBadChars($name)148 {149 return (max(array_map('ord', str_split($name))) > 126);150 }151 152 /**153 85 * Connects to the database and populates tables where needed. 154 86 * … … 162 94 } 163 95 164 // Create the plugin directory if needed165 $dir = dirname(__FILE__) . '/' . $this->getName();166 if (!file_exists($dir)) {167 mkdir($dir);168 }169 170 96 // Initialize the database connection 171 $this->db = new PDO('sqlite:' . $ dir . '/drink.db');97 $this->db = new PDO('sqlite:' . $this->dir . 'drink.db'); 172 98 173 99 // Populate the database if necessary … … 224 150 $contents = @file_get_contents('http://www.energyfiend.com/huge-caffeine-database/'); 225 151 if ($contents) { 152 // List of drinks to filter out 153 $filter = array( 154 'tea', 155 'coffee', 156 'starbucks' 157 ); 226 158 $start = stripos($contents, 'id="caffeinedb"'); 227 159 $end = stripos($contents, '</table>', $start); … … 231 163 foreach ($matches[2] as $name) { 232 164 $name = html_entity_decode(trim(preg_replace('/ \\([^)]+\\)| - .*$/', '', $name))); 233 if (!preg_match('/(?:^|\s+)(?: tea|coffee)(?:\s+|$)/', $name)) {165 if (!preg_match('/(?:^|\s+)(?:' . implode('|', $filter) . ')(?:\s+|$)/i', $name)) { 234 166 $names[] = $name; 235 167 } … … 250 182 } 251 183 } 184 185 unset($this->filter); 186 } 187 188 /** 189 * Determines if a table does not exist or is empty. 190 * 191 * @param string $name Table name 192 * @return bool TRUE if the table does not exist or is empty, FALSE 193 * otherwise 194 */ 195 private function needTable($name) 196 { 197 $table = $this->db 198 ->query('SELECT COUNT(*) FROM sqlite_master WHERE name = ' . $this->db->quote($name)) 199 ->fetchColumn(); 200 201 if (!$table) { 202 return true; 203 } 204 205 return !$this->db 206 ->query('SELECT COUNT(*) FROM ' . $name) 207 ->fetchColumn(); 208 } 209 210 /** 211 * Populates a source database table with a given set of data. 212 * 213 * @param string $table Name of the table 214 * @param array $names List of drink names 215 * @return void 216 */ 217 private function populateTable($table, $names) 218 { 219 $this->db->exec('CREATE TABLE ' . $table . ' (name VARCHAR(255))'); 220 $this->db->exec('CREATE UNIQUE INDEX ' . $table . '_name ON ' . $table . ' (name)'); 221 222 $stmt = $this->db->prepare('INSERT INTO ' . $table . ' (name) VALUES (:name)'); 223 $this->db->beginTransaction(); 224 foreach (array_unique($names) as $name) { 225 if (preg_match('/(?:^|[^a-z])(' . implode('|', $this->filter) . ')(?:[^a-z]|$)/i', $name, $match)) { 226 $this->debug('Filtered out ' . $name . ' because it contains ' . $match[1]); 227 continue; 228 } 229 $this->debug('Inserted ' . $name); 230 $stmt->execute(array('name' => $name)); 231 } 232 $this->db->commit(); 233 } 234 235 /** 236 * Returns a random record value from a given table 237 * 238 * @string $table Name of the table 239 * @return string Value of the name column for the selected record 240 */ 241 private function getRandomRecord($table) 242 { 243 return $this->db 244 ->query('SELECT name FROM ' . $table . ' ORDER BY Random() LIMIT 1') 245 ->fetchColumn(); 246 } 247 248 /** 249 * Returns whether or not a given value has characters that may not be 250 * displayed correctly 251 * 252 * @param string $name Value to check 253 */ 254 private function hasBadChars($name) 255 { 256 return (max(array_map('ord', str_split($name))) > 126); 252 257 } 253 258 … … 290 295 $text = 'pours '.$target.' a cup of '.$drink.' tea'; 291 296 } 292 $this->doAction($this->event->getSource(), $text );297 $this->doAction($this->event->getSource(), $text . '.'); 293 298 } 294 299 } -
trunk/Phergie/Plugin/Karma.php
r59 r60 13 13 class Phergie_Plugin_Karma extends Phergie_Event_Handler 14 14 { 15 /** 16 * Indicates that a local directory is required for this plugin 17 * 18 * @var bool 19 */ 20 protected $needsDir = true; 21 15 22 /** 16 23 * Stores the SQLite object … … 57 64 ( 58 65 'pi' => 'pi has karma of ' . M_PI, 59 'chucknorris' => ' pihas karma of Warning: Integer out of range',66 'chucknorris' => 'chucknorris has karma of Warning: Integer out of range', 60 67 'c' => 'c has karma of 299 792 458 m/s', 61 68 'e' => 'e has karma of ' . M_E, 62 69 'euler' => 'euler has karma of ' . M_EULER, 63 70 'mole' => 'mole has karma of 6.02214e23 molecules', 71 'avagadro' => 'avagadro has karma of 6.02214e23 molecules', 64 72 'spoon' => 'spoon has no karma. There is no spoon.', 65 strtolower($this->getIni('nick')) => $this->getIni('static'),66 73 'mc^2' => 'mc^2 has karma of e', 67 74 'mc2' => 'mc2 has karma of e', 68 75 'mc²' => 'mc² has karma of e', 69 76 ); 77 78 $static = $this->getIni('static'); 79 if ($static) { 80 $this->fixedKarma[strtolower($this->getIni('nick'))] = $static; 81 } 70 82 71 83 // Check to ensure that the SQLite extension is loaded … … 74 86 } 75 87 76 // Create a directory to contain the database if needed77 if (!file_exists(dirname(__FILE__).'/Karma'))78 mkdir(dirname(__FILE__).'/Karma');79 80 88 // Load or initialize the database 81 $db = dirname(__FILE__).'/Karma/karma.db';89 $db = $this->dir . 'karma.db'; 82 90 if (!file_exists($db)) { 83 91 $this->db = new SQLiteDatabase($db); … … 108 116 return; 109 117 } 110 $ target = $this->getSource();118 $source = $this->event->getSource(); 111 119 $message = $this->event->getArgument(1); 112 120 // Karma status request 113 121 if (preg_match('#^karma (\S+?)$#i', $message, $m)) { 122 $term = strtolower($m[1]); 114 123 // Return fixed value if set 115 if (isset($this->fixedKarma[strtolower($m[1])])) {116 $this->doPrivmsg($source, $ m[1] . ' ' . $this->fixedKarma[strtolower($m[1])]);124 if (isset ($this->fixedKarma[$term])) { 125 $this->doPrivmsg($source, $this->fixedKarma[$term]); 117 126 return; 118 127 } 119 128 // Return current karma or neutral if not set yet 120 $res = $this->db->query('SELECT karma FROM karmas WHERE word = \''.sqlite_escape_string( strtolower($m[1])).'\' LIMIT 1', SQLITE_NUM);129 $res = $this->db->query('SELECT karma FROM karmas WHERE word = \''.sqlite_escape_string($term).'\' LIMIT 1', SQLITE_NUM); 121 130 if ($res->numRows() && $res->column(0) != 0) { 122 131 $this->doPrivmsg($source, $m[1].' has karma of '.$res->column(0)); -
trunk/Phergie/Plugin/Lart.php
r59 r60 14 14 { 15 15 /** 16 * Indicates that a local directory is required for this plugin 17 * 18 * @var bool 19 */ 20 protected $needsDir = true; 21 22 /** 16 23 * Date string indicating the last time the cache was emptied 17 24 * … … 26 33 */ 27 34 protected $cache; 35 36 /** 37 * PDO instance for the database 38 * 39 * @var PDO 40 */ 41 protected $db; 28 42 29 43 /** … … 62 76 } 63 77 64 // Create the plugin directory if needed65 $dir = dirname(__FILE__) . '/' . $this->getName();66 if (!file_exists($dir)) {67 mkdir($dir);68 }69 70 78 // Initialize the database connection 71 $db = $ dir . '/lart.db';72 $create = !file_exists($db);79 $db = $this->dir . 'lart.db'; 80 $create = !file_exists($db); 73 81 $this->db = new PDO('sqlite:' . $db); 74 82 75 83 // Create database tables if necessary 76 84 if ($create) { 77 $this->db->exec('CREATE TABLE lart ( name VARCHAR(255), definition TEXT )'); 78 $this->db->exec('CREATE UNIQUE INDEX lart_name ON lart (name)'); 85 $this->db->exec(' 86 CREATE TABLE lart ( name VARCHAR(255), definition TEXT ); 87 CREATE UNIQUE INDEX lart_name ON lart (name) 88 '); 79 89 } 80 90 … … 141 151 } 142 152 $this->doAction( 143 $this->event->get Source(),153 $this->event->getArgument(0), 144 154 'puts her hands over her ears and cries, "Stop confusing me!"' 145 155 ); 156 return; 146 157 } 147 158 $definition = $redirect; … … 182 193 } 183 194 184 if (preg_match('/^(' . $nick . ': )?(.*) is (.*)$/U', $message, $match)) {195 if (preg_match('/^(' . $nick . ':?\s*)?(.*) is (.*)$/i', $message, $match)) { 185 196 list (, $address, $name, $definition) = $match; 186 if ( !empty($nick) || $source[0] != '#') {197 if (empty ($address) || $source[0] == '#') { 187 198 $name = strtolower($name); 188 199 $this->replace->execute(array(':name' => $name, ':definition' => $definition)); 189 200 $this->cache[$name] = $definition; 190 201 } 191 } else if (preg_match('/^(' . $nick . ': )?forget (.*)$/U', $message, $match)) { 192 if (!empty($nick) || $source[0] != '#') { 193 $name = strtolower($match[2]); 202 } else if (preg_match('/^(' . $nick . ':?\s*)?forget (.*)$/i', $message, $match)) { 203 list (, $address, $name) = $match; 204 if (empty ($address) || $source[0] == '#') { 205 $name = strtolower($name); 194 206 $this->delete->execute(array(':name' => $name)); 195 207 unset($this->cache[$name]); -
trunk/Phergie/Plugin/Logging.php
r59 r60 2 2 3 3 /** 4 * @see Phergie_ Event_Handler4 * @see Phergie_Plugin_Abstract_Command 5 5 */ 6 require_once 'Phergie/ Event/Handler.php';6 require_once 'Phergie/Plugin/Abstract/Command.php'; 7 7 8 8 /** … … 11 11 * containing a given search phrase. 12 12 */ 13 class Phergie_Plugin_Logging extends Phergie_ Event_Handler13 class Phergie_Plugin_Logging extends Phergie_Plugin_Abstract_Command 14 14 { 15 /** 16 * Indicates that a local directory is required for this plugin 17 * 18 * @var bool 19 */ 20 protected $needsDir = true; 21 22 /** 23 * Indicates a JOIN event in the type column of the logs table 24 * 25 * @const int 26 */ 15 27 const JOIN = 1; 28 29 /** 30 * Indicates a PART event in the type column of the logs table 31 * 32 * @const int 33 */ 16 34 const PART = 2; 35 36 /** 37 * Indicates a QUIT event in the type column of the logs table 38 * 39 * @const int 40 */ 17 41 const QUIT = 3; 18 const TALK = 4; 42 43 /** 44 * Indicates a PRIVMSG event in the type column of the logs table 45 * 46 * @const int 47 */ 48 const PRIVMSG = 4; 49 50 /** 51 * Indicates a CTCP ACTION event in the type column of the logs table 52 * 53 * @const int 54 */ 19 55 const ACTION = 5; 56 57 /** 58 * Indicates a NICK event in the type column of the logs table 59 * 60 * @const int 61 */ 20 62 const NICK = 6; 21 const KICKED = 7; 22 23 /** 24 * SQLite object 25 * 26 * @var resource 63 64 /** 65 * Indicates a KICK event in the type column of the logs table 66 * 67 * @const int 68 */ 69 const KICK = 7; 70 71 /** 72 * Indicates a MODE event in the type column of the logs table 73 * 74 * @const int 75 */ 76 const MODE = 8; 77 78 /** 79 * Indicates a TOPIC event in the type column of the logs table 80 * 81 * @const int 82 */ 83 const TOPIC = 9; 84 85 /** 86 * PDO instance for the database 87 * 88 * @var PDO 27 89 */ 28 90 protected $db = null; 29 91 30 92 /** 93 * Prepared statement for searching for the last logged action in which a 94 * particular user was mentioned 95 * 96 * @var PDOStatement 97 */ 98 protected $search; 99 100 /** 101 * Prepared statement for searching for the last logged action originating 102 * from a particular user 103 * 104 * @var PDOStatement 105 */ 106 protected $seen; 107 108 /** 109 * Prepared statement for searching for the last logged PRIVMSG action or 110 * CTCP ACTION command originating from a particular user 111 * 112 * @var PDOStatement 113 */ 114 protected $heard; 115 116 /** 117 * Prepared statement for inserting new log entries 118 * 119 * @var PDOStatement 120 */ 121 protected $insert; 122 123 /** 31 124 * Answers for seen when the user is present 32 125 * 33 126 * @var array 34 127 */ 35 protected $ msgPresent = array128 protected $present = array 36 129 ( 37 130 'Open your eyes already!', … … 42 135 43 136 /** 44 * Answers for heard when the user can't be found137 * Answers for when record of a user cannot be found 45 138 * 46 139 * @var array 47 140 */ 48 protected $m sgNeverHeard= array141 protected $missing = array 49 142 ( 50 143 'must be mute, or one of your imaginary friends?', … … 55 148 56 149 /** 57 * Action descriptions corresponding to event constants .150 * Action descriptions corresponding to event constants 58 151 * 59 152 * @var array … … 64 157 self::PART => 'leaving this channel because', 65 158 self::QUIT => 'quitting', 66 self:: TALK=> 'saying',159 self::PRIVMSG => 'saying', 67 160 self::ACTION => 'saying', 68 161 self::NICK => 'changing nick to', 69 self::KICK ED=> 'being kicked off because',162 self::KICK => 'being kicked off because', 70 163 ); 71 164 … … 77 170 public function init() 78 171 { 79 // Check to make sure SQLite is present80 if (!extension_loaded(' sqlite')) {172 // Check for the necessary extensions 173 if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { 81 174 return; 82 175 } 83 176 84 // Create plugin dir if not there yet 85 $dir = dirname(__FILE__) . '/Logging'; 86 if (!file_exists($dir)) { 87 mkdir($dir); 88 } 89 90 // Load or initialize the logging database 91 $db = $dir . '/logging.db'; 92 if (!file_exists($db)) { 93 $this->db = new SQLiteDatabase($db); 94 $this->db->queryExec(' 95 CREATE TABLE logs ( tstamp VARCHAR(14), type SHORTINT, chan VARCHAR(45), nick VARCHAR(25), message VARCHAR(255) ) ; 96 CREATE INDEX channicktype ON logs ( tstamp, type, chan, nick ) ; 97 CREATE INDEX channick ON logs ( tstamp, chan, nick ) ; 177 // Initialize the database connection 178 $db = $this->dir . 'logging.db'; 179 $create = !file_exists($db); 180 $this->db = new PDO('sqlite:' . $db); 181 182 // Create database tables if necessary 183 if ($create) { 184 $result = $this->db->exec(' 185 CREATE TABLE logs ( 186 tstamp VARCHAR(14), 187 type SHORTINT, 188 chan VARCHAR(45), 189 nick VARCHAR(25), 190 message VARCHAR(255) 191 ); 192 CREATE INDEX channicktype ON logs (tstamp, type, chan, nick); 193 CREATE INDEX channick ON logs (tstamp, chan, nick); 98 194 '); 195 } 196 197 // Initialize prepared statements for common operations 198 $this->search = $this->db->prepare(' 199 SELECT tstamp, type, chan, nick, message 200 FROM logs 201 WHERE nick LIKE :phrase 202 OR message LIKE :phrase 203 ORDER BY tstamp DESC 204 LIMIT :limit 205 '); 206 207 $this->seen = $this->db->prepare(' 208 SELECT tstamp, type, message 209 FROM logs 210 WHERE nick = :name 211 AND chan = :chan 212 ORDER BY tstamp DESC 213 LIMIT 1 214 '); 215 216 $this->heard = $this->db->prepare(' 217 SELECT tstamp, type, message 218 FROM logs 219 WHERE type IN (' . self::PRIVMSG . ', ' . self::ACTION . ') 220 AND nick = :nick 221 AND chan = :chan 222 ORDER BY tstamp DESC 223 LIMIT 1 224 '); 225 226 $this->insert = $this->db->prepare(' 227 INSERT INTO logs ( 228 tstamp, 229 type, 230 chan, 231 nick, 232 message 233 ) 234 VALUES ( 235 :tstamp, 236 :type, 237 :chan, 238 :nick, 239 :message 240 ) 241 '); 242 } 243 244 /** 245 * Formats a timestamp for display purposes. 246 * 247 * @param string $timestamp Timestamp to format 248 * @return string Formatted timestamp 249 */ 250 protected function formatTimestamp($timestamp) 251 { 252 return preg_replace( 253 '#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$#', 254 '$1-$2-$3 @ $4:$5:$6', 255 $timestamp 256 ); 257 } 258 259 /** 260 * Returns a random message used when a user is currently present. 261 * 262 * @return string 263 */ 264 protected function randomPresent() 265 { 266 return $this->present[array_rand($this->present)]; 267 } 268 269 /** 270 * Returns a random message used when no logged record of a user exists. 271 * 272 * @return string 273 */ 274 protected function randomMissing() 275 { 276 return $this->missing[array_rand($this->missing)]; 277 } 278 279 /** 280 * Inserts a new entry in the log database. 281 * 282 * @param int $type Class constant representing the event type 283 * @param string $chan Name of the channel in which the event occurs 284 * @param string $nick Nick of the user from which the event originates 285 * @param string $message Message associated with the event if applicable 286 * (optional) 287 * @return void 288 */ 289 protected function insertEvent($type, $chan, $nick, $message = null) 290 { 291 $params = array( 292 ':tstamp' => date('YmdHis'), 293 ':type' => $type, 294 ':chan' => $chan, 295 ':nick' => $nick, 296 ':message' => trim($message) 297 ); 298 299 $result = $this->insert->execute($params); 300 } 301 302 /** 303 * Logs incoming messages. 304 * 305 * @return void 306 */ 307 public function onPrivmsg() 308 { 309 // Allow the Command plugin to process command calls 310 parent::onPrivmsg(); 311 312 if ($this->event->isInChannel()) { 313 $this->insertEvent( 314 self::PRIVMSG, 315 $this->event->getSource(), 316 $this->event->getNick(), 317 $this->event->getArgument(1) 318 ); 319 } 320 } 321 322 /** 323 * Logics incoming actions. 324 * 325 * @return void 326 */ 327 public function onAction() 328 { 329 if ($this->event->isInChannel()) { 330 $this->insertEvent( 331 self::ACTION, 332 $this->event->getSource(), 333 $this->event->getNick(), 334 $this->event->getArgument(1) 335 ); 336 } 337 } 338 339 /** 340 * Tracks users joining. 341 * 342 * @return void 343 */ 344 public function onJoin() 345 { 346 $this->insertEvent( 347 self::JOIN, 348 $this->event->getSource(), 349 $this->event->getNick() 350 ); 351 } 352 353 /** 354 * Tracks users parting. 355 * 356 * @return void 357 */ 358 public function onPart() 359 { 360 $this->insertEvent( 361 self::PART, 362 $this->event->getSource(), 363 $this->event->getNick(), 364 $this->event->getArgument(1) 365 ); 366 } 367 368 /** 369 * Tracks users being kicked. 370 * 371 * @return void 372 */ 373 public function onKick() 374 { 375 $this->insertEvent( 376 self::KICK, 377 $this->event->getSource(), 378 $this->event->getNick(), 379 $this->event->getArgument(1) 380 ); 381 } 382 383 /** 384 * Tracks users changing modes. 385 * 386 * @return void 387 */ 388 public function onMode() 389 { 390 $this->insertEvent( 391 self::MODE, 392 $this->event->getSource(), 393 $this->event->getNick(), 394 implode(' ', array_slice($this->event->getArguments(), 1)) 395 ); 396 } 397 398 /** 399 * Tracks channel topic changes. 400 * 401 * @return void 402 */ 403 public function onTopic() 404 { 405 $this->insertEvent( 406 self::TOPIC, 407 $this->event->getSource(), 408 $this->event->getNick(), 409 $this->event->getArgument(1) 410 ); 411 } 412 413 /** 414 * Tracks users quitting. 415 * 416 * @return void 417 */ 418 public function onQuit() 419 { 420 $nick = $this->event->getNick(); 421 422 foreach (Phergie_Plugin_Users::getChannels($nick) as $chan) { 423 $this->insertEvent( 424 self::QUIT, 425 $chan, 426 $this->event->getNick(), 427 $this->event->getArgument(0) 428 ); 429 } 430 } 431 432 /** 433 * Tracks users changing nicks. 434 * 435 * @return void 436 */ 437 public function onNick() 438 { 439 $nick = $this->event->getNick(); 440 441 foreach (Phergie_Plugin_Users::getChannels($nick) as $chan) { 442 $this->insertEvent( 443 self::NICK, 444 $chan, 445 $this->event->getNick(), 446 $this->event->getArgument(0) 447 ); 448 } 449 } 450 451 /** 452 * Responds to requests for logged messages containing a particular search 453 * phrase. 454 * 455 * @param string $phrase Phrase to search for 456 * @return void 457 */ 458 public function onDoSearch($phrase) 459 { 460 $source = $this->event->getSource(); 461 462 $params = array( 463 ':phrase' => '%' . $phrase . '%', 464 ':limit' => ($source[0] == '#' ? 1 : 6) 465 ); 466 467 $this->search->execute($params); 468 469 foreach($this->search as $row) { 470 $this->doPrivmsg( 471 $source, 472 sprintf( 473 '%s was seen %s: %s on %s (on %s)', 474 $row['nick'], 475 $this->actions[$row['type']], 476 $row['message'], 477 $row['chan'], 478 $this->formatTimestamp($row['tstamp']) 479 ) 480 ); 481 } 482 } 483 484 /** 485 * Responds to requests for the last logged action originating from a 486 * particular user. 487 * 488 * @param string $user Nick of the user to search for 489 * @return void 490 */ 491 public function onDoSeen($user) 492 { 493 $source = $this->event->getSource(); 494 $target = $this->event->getNick(); 495 496 // Person is online, send a random prank answer 497 if (Phergie_Plugin_Users::isIn($user, $source)) { 498 $this->doPrivmsg( 499 $source, 500 sprintf( 501 '%s: %s', 502 $target, 503 $this->randomPresent() 504 ) 505 ); 506 507 // Person is offline 99 508 } else { 100 $this->db = new SQLiteDatabase($db); 101 } 102 } 103 104 /** 105 * Logs incoming messages and responds to requests for the last action 106 * taken or message sent by a particular user or messages containing a 107 * particular search phrase. 108 * 109 * @todo Encapsulate log insertions into its own routine and call it here 110 * @return void 111 */ 112 public function onPrivmsg() 113 { 114 $source = $this->getSource(); 115 $target = $this->event->getNick(); 116 $message = $this->event->getArgument(1); 117 if (preg_match('#^search (\S+)$#', $message, $m)) { 118 // Search DB 119 $res = $this->db->query('SELECT tstamp, type, chan, nick, message FROM logs 120 WHERE nick LIKE \'%'.sqlite_escape_string($m[1]).'%\' OR message LIKE \'%'.sqlite_escape_string($m[1]).'%\' 121 ORDER BY tstamp DESC LIMIT '.($source[0] === '#' ? 1 : 6), SQLITE_ASSOC); 122 // Output 123 foreach($res as $r) { 509 $params = array( 510 ':name' => $user, 511 ':chan' => $source 512 ); 513 514 $this->seen->execute($params); 515 $row = $this->seen->fetch(PDO::FETCH_ASSOC); 516 517 // Send the last action if available 518 if ($row) { 124 519 $this->doPrivmsg( 125 520 $source, 126 // @todo Is there a SQLite function that could be used in the query itself rather than this preg_replace? 127 $r['nick'].' was seen '.$this->actions[$r['type']] .'; '.$r['message'].' on '.$r['chan'].' (on '.preg_replace('#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$#', '$1-$2-$3 @ $4:$5:$6', $r['tstamp']).')' 521 sprintf( 522 '%s: %s was last seen %s: %s (on %s)', 523 $source, 524 $user, 525 $this->actions[$row['type']], 526 $row['message'], 527 $this->formatTimestamp($row['tstamp']) 528 ) 529 ); 530 531 // Send a prank answer if no last action is found 532 } else { 533 $this->doPrivmsg( 534 $source, 535 sprintf( 536 '%s: %s %s', 537 $target, 538 $user, 539 $this->randomMissing() 540 ) 128 541 ); 129 542 } 130 unset($res, $r, $m); 131 } 132 // @todo Why are logs restricted to in-channel messages only? 133 if ($source[0] === '#') { 134 // Last Seen Handler 135 if (preg_match('#^seen (\S+)$#', $message, $m)) { 136 // Person is online, send a random prank answer 137 if (Phergie_Plugin_Users::isIn($m[1], $source)) { 138 $this->doPrivmsg($source, $target.': '.$this->msgPresent[array_rand($this->msgPresent,1)]); 139 } else { // Person is offline, show last action 140 $res = $this->db->query('SELECT tstamp, type, message FROM logs 141 WHERE nick=\''.sqlite_escape_string($m[1]).'\' AND chan=\''.sqlite_escape_string($source).'\' 142 ORDER BY tstamp DESC LIMIT 1', SQLITE_ASSOC); 143 foreach($res as $r) { 144 // @todo Is there a SQLite function that could be used in the query itself rather than this preg_replace? 145 $this->doPrivmsg($source, $source.': '.$m[1].' was last seen '.$this->actions[$r['type']].'; '.$r['message'].' (on '.preg_replace('#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$#', '$1-$2-$3 @ $4:$5:$6', $r['tstamp']).')'); 146 } 147 unset($res, $r); 148 } 149 unset($m); 150 // Last Heard Handler 151 } elseif (preg_match('#^heard (\S+)$#', $message, $m)) { 152 $res = $this->db->query('SELECT tstamp, type, message FROM logs 153 WHERE (type='.self::TALK.' OR type='.self::ACTION.') AND 154 nick=\''.sqlite_escape_string($m[1]).'\' AND chan=\''.sqlite_escape_string($source).'\' 155 ORDER BY tstamp DESC LIMIT 1', SQLITE_ASSOC); 156 // Not found, random prank answer 157 if($res->numRows() === 0) { 158 $this->doPrivmsg($source, $target.': '.$m[1].' '. $this->msgNeverHeard[array_rand($this->msgNeverHeard,1)]); 159 // Send last words 160 } else { 161 $r = $res->fetch(); 162 $this->doPrivmsg($source, $target.': '.$m[1].'\'s last words were '.$r['message'].' (on '.preg_replace('#^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$#', '$1-$2-$3 @ $4:$5:$6', $r['tstamp']).')'); 163 } 164 unset($res, $r, $m); 165 } 166 $this->db->queryExec('INSERT INTO logs (tstamp, type, chan, nick, message) VALUES (\''.date('YmdHis').'\', '.self::TALK.', \''.sqlite_escape_string($source).'\', \''.sqlite_escape_string($target).'\', \''.sqlite_escape_string($message).'\')'); 167 } 168 } 169 170 /** 171 * Tracks users joining. 172 * 173 * @todo Encapsulate log insertions into its own routine and call it here 174 * @return void 175 */ 176 public function onJoin() 177 { 178 $this->db->queryExec('INSERT INTO logs (tstamp, type, chan, nick) VALUES (\''.date('YmdHis').'\', '.self::JOIN.', \''.sqlite_escape_string($this->getSource()).'\', \''.sqlite_escape_string($this->event->getNick()).'\')'); 179 } 180 181 /** 182 * Tracks users parting. 183 * 184 * @todo Encapsulate log insertions into its own routine and call it here 185 * @return void 186 */ 187 public function onPart() 188 { 189 $this->db->queryExec('INSERT INTO logs (tstamp, type, chan, nick, message) VALUES (\''.date('YmdHis').'\', '.self::PART.', \''.sqlite_escape_string($this->getSource()).'\', \''.sqlite_escape_string($this->event->getNick()).'\', \''.sqlite_escape_string(trim($this->event->getArgument(1), '"')).'\')'); 190 } 191 192 /** 193 * Tracks users quitting. 194 * 195 * @todo Encapsulate log insertions into its own routine and call it here 196 * @return void 197 */ 198 public function onQuit() 199 { 200 $nick = $this->event->getNick(); 201 $message = trim($this->event->getArgument(1), '"'); 202 foreach (Phergie_Plugin_Users::getChannels($nick) as $chan) { 203 $this->db->queryExec('INSERT INTO logs (tstamp, type, chan, nick, message) VALUES (\''.date('YmdHis').'\', '.self::QUIT.', \''.sqlite_escape_string($chan).'\', \''.sqlite_escape_string($nick).'\', \''.sqlite_escape_string($message).'\')'); 204 } 205 } 206 207 /** 208 * Tracks users changing nicks. 209 * 210 * @todo Encapsulate log insertions into its own routine and call it here 211 * @return void 212 */ 213 public function onNick() 214 { 215 $nick = $this->event->getNick(); 216 $message = $this->event->getArgument(0); 217 foreach (Phergie_Plugin_Users::getChannels($nick) as $chan) { 218 $this->db->queryExec('INSERT INTO logs (tstamp, type, chan, nick, message) VALUES (\''.date('YmdHis').'\', '.self::NICK.', \''.sqlite_escape_string($chan).'\', \''.sqlite_escape_string($nick).'\', \''.sqlite_escape_string($message).'\')'); 543 } 544 } 545 546 /** 547 * Responds to requests for the last logged PRIVMSG action or CTCP ACTION 548 * command originating from a particular user. 549 * 550 * @param string $user Nick of the user to search for 551 * @return void 552 */ 553 public function onDoHeard($user) 554 { 555 $source = $this->event->getSource(); 556 $target = $this->event->getNick(); 557 558 $params = array( 559 ':nick' => $user, 560 ':chan' => $source 561 ); 562 563 $this->heard->execute($params); 564 $row = $this->heard->fetch(PDO::FETCH_ASSOC); 565 566 // Send the last action if available 567 if($row) { 568 $this->doPrivmsg( 569 $source, 570 sprintf( 571 '%s: %s\'s last words were: %s (on %s)', 572 $target, 573 $user, 574 $row['message'], 575 $this->formatTimestamp($row['tstamp']) 576 ) 577 ); 578 579 // Send a prank answer if no last action is found 580 } else { 581 $this->doPrivmsg( 582 $source, 583 sprintf( 584 '%s: %s %s', 585 $target, 586 $user, 587 $this->randomMissing() 588 ) 589 ); 219 590 } 220 591 } -
trunk/Phergie/Plugin/Math.php
r59 r60 47 47 public function onPrivmsg() 48 48 { 49 $source = $this-> getSource();49 $source = $this->event->getSource(); 50 50 $message = $this->event->getArgument(1); 51 51 if (preg_match('/^(?:math|calc) /', $message)) { … … 108 108 } 109 109 } 110 $res = eval('return ' . $out . ';');110 $res = @eval('return ' . $out . ';'); 111 111 if($res === false) { 112 112 $this->doPrivmsg($source, 'Computation error, division by zero?'); -
trunk/Phergie/Plugin/Nickserv.php
r59 r60 64 64 if ($altnick) { 65 65 $this->doNick($altnick); 66 $this->setIni('nick', $altnick); 66 67 } 67 68 } -
trunk/Phergie/Plugin/Php.php
r59 r60 44 44 { 45 45 if (!isset ($this->cache[$function])) { 46 $contents = file_get_contents('http://php.net/manual/en/function.' . $function . '.php'); 46 $name = str_replace('_', '-', $function); 47 $contents = file_get_contents('http://php.net/manual/en/function.' . $name . '.php'); 47 48 $start = strpos($contents, '<div class="methodsynopsis">'); 48 49 $end = strpos($contents, '</div>', $start); … … 52 53 $this->cache[$function] = $contents; 53 54 } 54 $this->doPrivmsg($this-> getSource(), $this->cache[$function]);55 $this->doPrivmsg($this->event->getSource(), $this->cache[$function]); 55 56 } 56 57 } -
trunk/Phergie/Plugin/Puppet.php
r59 r60 7 7 8 8 /** 9 * Allows administrators to effectively speak a s the bot.9 * Allows administrators to effectively speak and act as the bot. 10 10 */ 11 class Phergie_Plugin_ Sayextends Phergie_Plugin_Abstract_AdminCommand11 class Phergie_Plugin_Puppet extends Phergie_Plugin_Abstract_AdminCommand 12 12 { 13 13 /** … … 15 15 * channel. 16 16 * 17 * <code> Botname: say [#chan]message</code>17 * <code>say #chan message</code> 18 18 * 19 * @param string $chan Name of the channel 20 * @param string $message Message to repeat 19 21 * @return void 20 22 */ 21 public function onDoSay($ what)23 public function onDoSay($chan, $message) 22 24 { 23 $source = $this->getSource(); 24 preg_match('{^(#\S+\s)?(.+)}', $what, $match); 25 if (!empty ($match[1])) { 26 $this->doPrivmsg(trim($match[1]), $match[2]); 27 } elseif ($source[0] === '#') { 28 $this->doPrivmsg($source, $what); 29 } 25 $this->doPrivmsg($chan, $message); 26 } 27 28 /** 29 * Handles a request for the bot to repeat a given action in a specified 30 * channel. 31 * 32 * <code>act #chan action</code> 33 * 34 * @param string $chan Name of the channel 35 * @param string $action Action to perform 36 * @return void 37 */ 38 public function onDoAct($chan, $action) 39 { 40 $this->doAction($chan, $action); 30 41 } 31 42 } -
trunk/Phergie/Plugin/UrbanDictionary.php
r59 r60 47 47 * length should be 510 characters according to the IRC RFC. 48 48 */ 49 $max = 445 - strlen($channel) - strlen($url); 49 $target = $this->event->getSource(); 50 $max = 445 - strlen($target) - strlen($url); 50 51 if (strlen($contents) > $max) { 51 52 $contents = substr($contents, 0, $max); … … 54 55 $end = $max; 55 56 } 56 $contents = substr($contents, 0, $end) . '...' . $url;57 $contents = substr($contents, 0, $end) . '...'; 57 58 } 59 $contents .= $url; 58 60 59 $this->doPrivmsg($t his->getSource(), $contents);61 $this->doPrivmsg($target, $contents); 60 62 } 61 63 } -
trunk/Phergie/Plugin/Url.php
r59 r60 45 45 { 46 46 // URL Match 47 if (preg_match('#(https?://(?:[a-z0-9_-]+\.)+[a-z]{2,6}[^\s]*)#is', $ event->getArgument(1), $m)) {47 if (preg_match('#(https?://(?:[a-z0-9_-]+\.)+[a-z]{2,6}[^\s]*)#is', $this->event->getArgument(1), $m)) { 48 48 $url = rtrim($m[1], '), ]'); 49 49 // @todo if image or something, output content type 50 50 $tinyUrl = $this->tinyUrl($url); 51 $title = '';52 51 $titleLength = $this->getIni('title_length'); 53 if ($page = @fopen($url, 'r')) { 54 $content = ''; 55 while ($chunk = fread($page, 512)) { 56 $content .= $chunk; 57 if (preg_match('#<title[^>]*>([^<]*)#is', $content, $m)) { 58 $title = $this->decode($m[1], $titleLength); 59 break; 52 53 $opts = array('http' => 54 array( 55 'method' => 'GET', 56 'header' => 'Content-type: application/x-www-form-urlencoded', 57 'user_agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12' 58 ) 59 ); 60 $context = stream_context_create($opts); 61 62 if ($page = @fopen($url, 'r', false, $context)) { 63 $data = stream_get_meta_data($page); 64 foreach ($data['wrapper_data'] as $header) { 65 if (preg_match('/^Content-Type: ([^;]+)/', $header, $match) 66 && ! preg_match('/^text\\/x?html$/', $match[1])) { 67 $title = $match[1]; 60 68 } 61 if (preg_match('#</head>|<body#i', $content)) { 62 break; 69 } 70 if (!isset ($title)) { 71 $content = ''; 72 while ($chunk = fread($page, 512)) { 73 $content .= $chunk; 74 if (preg_match('#<title[^>]*>([^<]*)#is', $content, $m)) { 75 $title = $this->decode($m[1], $titleLength); 76 break; 77 } 78 if (preg_match('#</head>|<body#i', $content)) { 79 break; 80 } 63 81 } 64 82 } … … 67 85 68 86 $this->doPrivmsg( 69 $this-> getSource(),87 $this->event->getSource(), 70 88 str_replace( 71 89 array('%title%', '%link%'), -
trunk/Phergie/Plugin/Users.php
r59 r60 22 22 public function onMode() 23 23 { 24 if (preg_match('{(\S+)\s((?:\+|-)[hov+-]+)\s((?:\s*\S+)+)$}i', $this->event->getArgument(0), $m)) { 25 $chan = $m[1]; 26 $modes = str_split($m[2], 1); 27 $nicks = explode(' ', $m[3]); 24 $args = $this->event->getArguments(); 25 if (count($args) != 3) { 26 return; 27 } 28 list ($chan, $modes, $nicks) = $args; 29 if (preg_match('/(?:\+|-)[hov+-]+/i', $modes)) { 30 $modes = str_split($modes, 1); 31 $nicks = explode(' ', $nicks); 28 32 while ($char = array_shift($modes)) { 29 33 switch($char) { … … 63 67 } 64 68 65 // Function used for debugging purposes 66 /* 69 /** 70 * Debugging function 71 * 72 * @return void 73 */ 67 74 public function onPrivmsg() 68 75 { 69 list ($target, $nick) = $this->event->getArguments(); 70 if(preg_match('#^ishere (\S+)$#', $nick, $m)) { 71 $this->doPrivmsg($target, self::isIn($m[1], $target) ? 'true' : 'false'); 72 } 73 if(preg_match('#^isop (\S+)$#', $nick, $m)) { 74 $this->doPrivmsg($target, self::isOp($m[1], $target) ? 'true' : 'false'); 75 } 76 if(preg_match('#^isvoice (\S+)$#', $nick, $m)) { 77 $this->doPrivmsg($target, self::isVoice($m[1], $target) ? 'true' : 'false'); 78 } 79 } 80 */ 76 if ($this->getIni('debug')) { 77 list ($target, $msg) = $this->event->getArguments(); 78 if (preg_match('#^ishere (\S+)$#', $msg, $m)) { 79 $this->doPrivmsg($target, self::isIn($m[1], $target) ? 'true' : 'false'); 80 } elseif (preg_match('#^isop (\S+)$#', $msg, $m)) { 81 $this->doPrivmsg($target, self::isOp($m[1], $target) ? 'true' : 'false'); 82 } elseif (preg_match('#^isvoice (\S+)$#', $msg, $m)) { 83 $this->doPrivmsg($target, self::isVoice($m[1], $target) ? 'true' : 'false'); 84 } 85 } 86 } 81 87 82 88 /** … … 128 134 $nick = $this->event->getNick(); 129 135 $newNick = $this->event->getArgument(0); 130 $this->debug($nick . ' => ' . $newNick);131 136 foreach (self::$list as $channame=>$chan) { 132 137 if (isset($chan[$nick])) { -
trunk/Phergie/README
r57 r60 2 2 -- WHO IS PHERGIE? 3 3 ----------------------------------------------------------------------------- 4 Phergie is an IRC bot written in pure PHP 5 with an OO API. She is named 5 after the delicious and delectable Stacy Ann Ferguson, better known by her 4 Phergie is an IRC bot written in pure PHP 5 with an OO API. She is named 5 after the delicious and delectable Stacy Ann Ferguson, better known by her 6 6 stage name Fergie. 7 7 8 8 ----------------------------------------------------------------------------- 9 -- LICENSE 9 -- LICENSE 10 10 ----------------------------------------------------------------------------- 11 11 Phergie is released under the GNU Lesser General Public License version 3.0. 12 See the LICENSE file included in the source tarball or go to the URL below 12 See the LICENSE file included in the source tarball or go to the URL below 13 13 to obtain a copy. 14 14 … … 16 16 17 17 ----------------------------------------------------------------------------- 18 -- RUNNING PHERGIE 18 -- RUNNING PHERGIE 19 19 ----------------------------------------------------------------------------- 20 20 Download and decompress the source tarball, available at the URL below. … … 22 22 http://bluelyte.sytes.net/phergie.tar.gz 23 23 24 Make sure that register_argc_argv is enabled if you want to use a 25 configuration file that is not in the Phergie directory or is not named 26 phergie.ini. register_argc_argv is often disabled in CGI environments since 27 it's generally only needed for CLI purposes. If you have not run PHP from the 28 command line before, it may be a good idea to read the related manual section 24 Make sure that register_argc_argv is enabled if you want to use a 25 configuration file that is not in the Phergie directory or is not named 26 phergie.ini. register_argc_argv is often disabled in CGI environments since 27 it's generally only needed for CLI purposes. If you have not run PHP from the 28 command line before, it may be a good idea to read the related manual section 29 29 at the URL below. 30 30 31 31 http://us.php.net/manual/en/features.commandline.php 32 32 33 Once you've made any necessary PHP configuration changes, navigate to the 34 Phergie directory. From there, open your Phergie configuration file and 35 update configuration settings there as needed. Once finished, open up a 36 command prompt/terminal/shell. Execute the PHP interpreter in CLI mode and 33 Once you've made any necessary PHP configuration changes, navigate to the 34 Phergie directory. From there, open your Phergie configuration file and 35 update configuration settings there as needed. Once finished, open up a 36 command prompt/terminal/shell. Execute the PHP interpreter in CLI mode and 37 37 pass it the filename Bot.php, as shown below. 38 38 … … 42 42 43 43 ----------------------------------------------------------------------------- 44 -- USING PLUGINS 44 -- USING PLUGINS 45 45 ----------------------------------------------------------------------------- 46 Obviously, I hope that usage of Phergie gets to be widespread enough that 47 people post their own plugins and you happen upon one that you find to be 48 useful. 46 Obviously, I hope that usage of Phergie gets to be widespread enough that 47 people post their own plugins and you happen upon one that you find to be 48 useful. 49 49 50 Plugins are stored in the Phergie/Event/Handler folder. Each plugin should 51 have its own file in that folder and, if needed, a directory by the same 52 name (minus the extension, obviously) to include any other files it 50 Plugins are stored in the Phergie/Event/Handler folder. Each plugin should 51 have its own file in that folder and, if needed, a directory by the same 52 name (minus the extension, obviously) to include any other files it 53 53 requires. 54 54 55 Some plugins might also require certain configuration settings to be set up 56 in the phergie.ini configuration file, so be sure to look for any 55 Some plugins might also require certain configuration settings to be set up 56 in the phergie.ini configuration file, so be sure to look for any 57 57 instructions to this effect. 58 58 59 59 ----------------------------------------------------------------------------- 60 -- EXTENDING PHERGIE60 -- HISTORY 61 61 ----------------------------------------------------------------------------- 62 For instructions on how to write plugins for Phergie, see the CONTRIBUTE 63 file included in the source tarball. 64 65 ----------------------------------------------------------------------------- 66 -- HISTORY 67 ----------------------------------------------------------------------------- 68 The maintainer of the Ai bot for the #phpc channel on the Freenode IRC 69 network could never find time to package its source code for release and 70 only had the odd moment to handle feature requests. Additionally, the bot was 71 written in PHP 4, for which support will end on 8/8/08. With other existing 72 libraries having odd APIs, missing features, or just overall not seeming cut 73 out for the job, it was decided that a new bot should be built from scratch. 62 The maintainer of the Ai bot for the #phpc channel on the Freenode IRC 63 network could never find time to package its source code for release and 64 only had the odd moment to handle feature requests. Additionally, the bot was 65 written in PHP 4, for which support will end on 8/8/08. With other existing 66 libraries having odd APIs, missing features, or just overall not seeming cut 67 out for the job, it was decided that a new bot should be built from scratch. 74 68 Hence, we have Phergie. -
trunk/Phergie/phergie.ini
r59 r60 5 5 ; server : 6 6 ; host name of the server to which the bot should connect 7 server = irc.freenode.net7 server = 8 8 9 9 ; username : 10 10 ; username to use for the bot 11 username = Phergie11 username = 12 12 13 13 ; nick : 14 14 ; nick to use for the bot 15 nick = Phergie15 nick = 16 16 17 17 ; altnick0 : … … 19 19 ; is unavailable; this setting may be repeated with a different number 20 20 ; (i. e. altnick1, altnick2, etc.) 21 altnick0 = Phergie_ 22 altnick1 = Phergie__ 23 altnick2 = Phergie___ 21 altnick0 = 24 22 25 23 ; realname : 26 24 ; real name to use for the bot 27 realname = Phergie25 realname = 28 26 29 27 ; gender : 30 28 ; M or F to indicate the gender of the bot for instances when the bot must 31 29 ; refer to itself in the third person for actions 32 gender = F30 gender = 33 31 34 32 ; plugins : … … 79 77 ; a comma-delimited list of channels which the bot should automatically 80 78 ; join upon successfully connecting 81 autojoin.channels = 82 83 ;----------------------------------------------------------------------------- 84 ; Karma 85 ;----------------------------------------------------------------------------- 86 87 ; karma.limit : 88 ; Maximum number of karma rating changes allowed per user, per term, per 89 ; day, or 0 for unlimited changes 90 karma.limit = 5 91 92 ; karma.static : 93 ; If non-empty, the static karma response to use for the bot's nick 94 karma.static = 95 96 ;----------------------------------------------------------------------------- 97 ; NickServ 98 ;----------------------------------------------------------------------------- 99 100 ; nickserv.password : 101 ; password to use when identifying the bot to NickServ where 102 ; authentication will only take place if a non-empty password is specified 103 nickserv.password = 79 autojoin.channels = 104 80 105 81 ;----------------------------------------------------------------------------- … … 131 107 132 108 ;----------------------------------------------------------------------------- 133 ; Urban Dictionary109 ; Karma 134 110 ;----------------------------------------------------------------------------- 135 111 136 ; urbandictionary.key : 137 ; API key for Urban Dictionary 138 ; @see http://www.urbandictionary.com/api.php 139 urbandictionary.key = 112 ; karma.limit : 113 ; Maximum number of karma rating changes allowed per user, per term, per 114 ; day, or 0 for unlimited changes 115 karma.limit = 5 116 117 ; karma.static : 118 ; If non-empty, the static karma response to use for the bot's nick 119 karma.static = 120 121 ;----------------------------------------------------------------------------- 122 ; NickServ 123 ;----------------------------------------------------------------------------- 124 125 ; nickserv.password : 126 ; password to use when identifying the bot to NickServ where 127 ; authentication will only take place if a non-empty password is specified 128 nickserv.password = 140 129 141 130 ;-----------------------------------------------------------------------------