rendered paste body#!/usr/bin/php<?phpdefine("RSS_INTERVAL", 300); // seconds between downloading RSS changelogdefine("IGNORE_TIME", 60*15); // seconds we don't repeat linksdefine('TRACURL', 'trac.kohanaphp.com');define("ircBotPath", str_replace("\\", "/", dirName(__FILE__)));include(ircBotPath."/lib/ircBot.class.php");include('lastRSS.inc.php');if (version_compare(phpversion(),'4.3.0','<') || !defined("STDIN")) { define('STDIN',fopen("php://stdin","r")); define('STDOUT',fopen("php://stdout","r")); define('STDERR',fopen("php://stderr","r")); register_shutdown_function( create_function( '' , 'fclose(STDIN); fclose(STDOUT); fclose(STDERR); return true;' ) );}/************************ Configuration ************************/ircConf::set("botNick", "KohanaTrac"); /** Bot nick */ircConf::set("botName", "Kohana Trac Bot"); /** Bot's IRC name */ircConf::set("botIdent", "TracBot"); /** Bot's ident *//** NickServ password, comment out if you don't have one. NOTE: this is also used when the bot NickName is taken to try ghost it with NickServ, only when set *///ircConf::set("botNickServPass", "secret");/** Servers to connect to - MUST BE ARRAY()!!! Used for rotation */ircConf::set("servers", array("irc.freenode.net"));ircConf::set("serverPort", 6667); /** Server port */ircConf::set("stayAlive", true); /** Stay-Alive - Keep trying connections *//** Some networks and people don't like bots */ircConf::set("versionReply", "greg's fantastic trac bot");/** Bot channel configuration, this is a new requirement, channel users need tobe configured for each channel, users list MUST be an array */ircConf::setChannel("#kohana-dev", "+ntR", array('keyword'=>'secret'), array());ircConf::setChannel("#kohana", "+ntR", array(), array());ircConf::setChannel("#kohanatest", "+ntR", array(), array());/** Log/Debug, when TRUE messages will written to the logfile */define("ircDoLog", true);/** Log raw IRC output when in debug mode */define("ircDebug", false);$irc = new ircBot;/** ADD CALLBACKS * This is where the bot gets its functionality. * See comments & code below for writing callback functions */$irc->addCallback(onChanMsg, "channelResponses");$irc->addCallback(onChanMsg, "onMsgCheck");$irc->addCallback(onOp, "onOpCheck");//$irc->addCallback(onMode, "onModeCheck");$irc->addCallback(onTopic, "onTopicCheck");$irc->addCallback(onNotice, "onNoticeCheck");$irc->addCallback(onPrivMsg, "onPrivMsgCheck");$irc->addCallback(onJoin, "onEventCheck");$irc->addCallback(onKick, "onEventCheck");$irc->addCallback(onPart, "onEventCheck");$irc->addCallback(onQuit, "onEventCheck");$irc->addCallback(onKill, "onEventCheck");/** Finally start our bot and connect to IRC :) */$irc->init();function channelResponses(&$bot, $nick, $ident, $host, $channel, $text) { // ignore CIA bot if (preg_match('/^CIA-\d+/', $nick)) return; if (preg_match("/^".$GLOBALS["ircConf"]["botNick"].".*suck/", $text)) { // someone said "KohanaTrac sucks" $bot->provMsg($channel, 'No, '.$nick.', you suck'); return; } $direct_msg = false; if (preg_match("/^".$GLOBALS["ircConf"]["botNick"]."[:,\- ]/", $text)) { // this msg was directed at the bot $direct_msg = true; // replace our name (interferes with wiki links) $text = str_replace($GLOBALS["ircConf"]["botNick"],"",$text); } if (preg_match('/(?<!\w)r(\d+)(?!\w)/', $text, $matches) || preg_match('/(?<!\w)\[(\d+)\](?!\w)/', $text, $matches)) { $bot->log('Got possibly changeset link: '.$matches[0]); if (false !== ($submatch = scrapeUrl('http://'.TRACURL.'/changeset/'.$matches[1], '/id="searchable"><p>(.*)<\/p>/ms'))) { // have to call html_entity_decode() twice beacuse it puts stuff like &#34; $desc = html_entity_decode(strip_tags(html_entity_decode($submatch[1]))); // get rid of extra spaces, and newlines $desc = trim( str_replace(array("\n","\r"), array(" ",""), $desc) ); if (strlen($desc) > 60) { // trim if it's over 60, and show we trimmed it $desc = substr($desc,0,60).'...'; } if (!checkIgnore($matches[0]) || $direct_msg) { $bot->privMsg($channel, 'Revision '.$matches[1].' at http://'.TRACURL.'/changeset/'.$matches[1].': '.$desc); } } } if (preg_match('/(?<![\w\/])([A-Z][a-z]+)([A-Z0-9][a-z0-9]*)+/', $text, $matches)) { $bot->log('Got possibly wiki link: '.$matches[0].' checking http://'.TRACURL.'/wiki/'.$matches[0]." = ".checkUrl('http://'.TRACURL.'/wiki/'.$matches[0])); if (checkUrl('http://'.TRACURL.'/wiki/'.$matches[0]) == 200) { if (!checkIgnore($matches[0]) || $direct_msg) { $bot->privMsg($channel, 'Wiki page '.$matches[0].' at http://'.TRACURL.'/wiki/'.$matches[0]); } } } if (preg_match('/(?<!\w)(?:#|bug |ticket )(\d{1,4})(?!\w)/', $text, $matches)) { $bot->log('Got possibly ticket link: '.$matches[0]); if (false !== ($submatch = scrapeUrl('http://'.TRACURL.'/ticket/'.$matches[1], //'/<h2 class="summary">(.*)<\/h2>.*<h3 class="status">(.*)<\/h3>/msU') )) { '/<span class="status">(.*)<\/span>.*<h2 class="summary">(.*)<\/h2>/msU') )) { $bot->log("Its a link.. posting maybe"); $status = html_entity_decode(strip_tags(html_entity_decode($submatch[1]))); if (!checkIgnore($matches[0]) || $direct_msg) { $bot->log("Not ignoring, posting for sure"); $bot->privMsg($channel, 'Ticket '.$matches[1].' at http://'.TRACURL.'/ticket/'.$matches[1].': '.$submatch[2].' '.$status); } } }}function onMsgCheck(&$bot, $nick, $ident, $host, $channel, $text) { checkRss($bot);}function onOpCheck(&$bot, $nick, $ident, $host, $channel, $user) { checkRss($bot);}function onEventCheck(&$bot, $nick, $ident, $host, $channelOrQuit = null) { checkRss($bot);}function onTopicCheck(&$bot, $nick, $ident, $host, $channel, $topic) { checkRss($bot);}function onNoticeCheck(&$bot, $nick, $ident, $host, $dest, $text) { checkRss($bot);}function onPrivMsgCheck(&$bot, $nick, $ident, $host, $us, $text) { checkRss($bot);}function checkRss(&$bot) { static $lastchecked; static $lastitem; if (!isset($lastchecked) || ($lastchecked < time() - RSS_INTERVAL)) { $bot->log('Checking RSS feed..'); $rss = new lastRSS; $rss->cache_dir = ''; $rss->cp = 'US-ASCII'; //$rssurl = 'http://'.TRACURL.'/timeline?milestone=on&ticket=on&changeset=on&wiki=on&max=50&daysback=1&format=rss'; $rssurl = 'http://'.TRACURL.'/timeline?milestone=on&ticket=on&changeset=on&wiki=on&max=50&daysback=1&format=rss'; if ($rs = $rss->get($rssurl)) { if (!isset($lastitem)) { $lastitem = (isset($rs['items'][0]) ? md5($rs['items'][0]['title']) : ''); } else { $messages = array(); foreach ($rs['items'] as $item) { if (md5($item['title']) == $lastitem) { // don't show any more break; } $title = preg_replace( array( '/(?<!\w)r(\d+)(?!\w)/', '/(?<!\w)\[(\d+)\](?!\w)/', '/(?<!\w|&)#(\d+)(?!\w)/', ), array( 'http://'.TRACURL.'/changeset/$1', 'http://'.TRACURL.'/changeset/$1', 'http://'.TRACURL.'/ticket/$1', ), $item['title']); $title = preg_replace_callback('/([A-Z][a-z]+)([A-Z0-9][a-z0-9]+)*/', 'replace_wiki_url', $title); if (substr($title,0,9) == 'Changeset') { // changeset titles include a description $messages[] = $title.' ('.$item['pubDate'].')'; } else { // have to call html_entity_decode() twice beacuse it puts stuff like &#34; $desc = html_entity_decode(strip_tags(html_entity_decode($item['description']))); // get rid of extra spaces, and newlines $desc = trim( str_replace(array("\n","\r"), array(" ",""), $desc) ); if (strlen($desc) > 60) { // trim if it's over 60, and show we trimmed it $desc = substr($desc,0,60).'...'; } $messages[] = $title.': '.$desc.' ('.$item['pubDate'].')'; } } if (count($messages) > 0) { // send them backwards, because we processed the rss forwards foreach (array_reverse($messages) as $message) { $bot->privMsg('#kohana-dev', $message); } } // set the lastitem to the first entry now $lastitem = md5($rs['items'][0]['title']); } } else { // set $lastitem to something, so when we do get something, it will print it $lastitem = "nadda"; } $lastchecked = time(); }}function checkUrl($url) { $u = parse_url($url); if (empty($u['port'])) { switch($u['scheme']) { case 'http': $u['port'] = 80; break; case 'https': $u['port'] = 443; break; default: return false; } } if (!($s = fsockopen($u['host'],$u['port']))) { return false; } $request = "HEAD ".$u['path']." HTTP/1.1\r\n"; $request .= "Host: ".$u['host']."\r\n"; $request .= "Connection: Close\r\n\r\n"; fwrite($s, $request); $data = ''; while (!feof($s)) { $data .= fgets($s, 1024); } fclose($s); if (preg_match('/HTTP\/1.[01] (\d+)/', $data, $matches)) { return $matches[1]; } return false;}function replace_wiki_url($matches) { if (checkUrl('http://'.TRACURL.'/wiki/'.$matches[0]) == 200) { return 'http://'.TRACURL.'/wiki/'.$matches[0]; } else { return $matches[0]; }}function scrapeUrl($url, $regex) { $u = parse_url($url); if (empty($u['port'])) { switch($u['scheme']) { case 'http': $u['port'] = 80; break; case 'https': $u['port'] = 443; break; default: return false; } } if (!($s = fsockopen($u['host'],$u['port']))) { return false; } $request = "GET ".$u['path']." HTTP/1.1\r\n"; $request .= "Host: ".$u['host']."\r\n"; $request .= "Connection: Close\r\n\r\n"; fwrite($s, $request); $data = ''; while (!feof($s)) { $data .= fgets($s, 1024); } fclose($s); if (preg_match($regex, $data, $matches)) { return $matches; } return false;}// return true if we said this link within IGNORE_TIME secondsfunction checkIgnore($link) { global $irc; static $ignoring; if (!isset($ignoring)) $ignoring = array(); foreach ($ignoring as $key=>$time) { if ($time < time() - IGNORE_TIME) { $irc->log("No longer ignoring \"$key\""); unset($ignoring[$key]); } } if (!isset($ignoring[$link])) { $ignoring[$link] = time(); $irc->log("Now ignoring \"$link\" for ".IGNORE_TIME."s"); return false; } $irc->log("Currently ignoring \"$link\""); return true;}