Misplaced Pages

User:ClueBot/Source: Difference between revisions

Article snapshot taken from Wikipedia with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.
< User:ClueBot Browse history interactively← Previous editContent deleted Content addedVisualWikitext
Revision as of 07:31, 24 July 2007 view sourceNaomiAmethyst (talk | contribs)Edit filter managers, Extended confirmed users, Rollbackers, Template editors6,269 editsNo edit summary← Previous edit Latest revision as of 13:01, 27 November 2010 view source ClueBot (talk | contribs)1,596,818 edits Automated source upload. 
(730 intermediate revisions by 49 users not shown)
Line 3: Line 3:




==Classes (wikibot.classes.php)==
==Source to ClueBot==


<source lang="php"><?PHP <pre>&lt;?PHP
/**
* @author Cobi Carter
**/
/**
* This class is designed to provide a simplified interface to cURL which maintains cookies.
* @author Cobi
**/
class http { class http {
private $ch; private $ch;
private $uid; private $uid;
public $postfollowredirs;
public $getfollowredirs;


/**
function data_encode ($data, $keyprefix = "", $keypostfix = "") {
* Our constructor function. This just does basic cURL initialization.
assert( is_array($data) );
* @return void
$vars=null;
**/
foreach($data as $key=>$value) {
function __construct () {
if(is_array($value)) $vars .= $this->data_encode($value, $keyprefix.$key.$keypostfix.urlencode(""));
global $proxyhost, $proxyport, $proxytype;
else $vars .= $keyprefix.$key.$keypostfix."=".urlencode($value)."&";
$this-&gt;ch = curl_init();
$this-&gt;uid = dechex(rand(0,99999999));
curl_setopt($this-&gt;ch,CURLOPT_COOKIEJAR,'/tmp/cluewikibot.cookies.'.$this-&gt;uid.'.dat');
curl_setopt($this-&gt;ch,CURLOPT_COOKIEFILE,'/tmp/cluewikibot.cookies.'.$this-&gt;uid.'.dat');
curl_setopt($this-&gt;ch,CURLOPT_MAXCONNECTS,100);
curl_setopt($this-&gt;ch,CURLOPT_CLOSEPOLICY,CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
curl_setopt($this-&gt;ch,CURLOPT_USERAGENT,'ClueBot/1.1');
if (isset($proxyhost) and isset($proxyport) and ($proxyport != null) and ($proxyhost != null)) {
curl_setopt($this-&gt;ch,CURLOPT_PROXYTYPE,isset( $proxytype ) ? $proxytype : CURLPROXY_HTTP);
curl_setopt($this-&gt;ch,CURLOPT_PROXY,$proxyhost);
curl_setopt($this-&gt;ch,CURLOPT_PROXYPORT,$proxyport);
} }
$this-&gt;postfollowredirs = 0;
return $vars;
$this-&gt;getfollowredirs = 1;
}

function __construct () {
$this->ch = curl_init();
$this->uid = dechex(rand(0,99999999));
curl_setopt($this->ch,CURLOPT_COOKIEJAR,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
curl_setopt($this->ch,CURLOPT_COOKIEFILE,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
} }


/**
* Post to a URL.
* @param $url The URL to post to.
* @param $data The post-data to post, should be an array of key =&gt; value pairs.
* @return Data retrieved from the POST request.
**/
function post ($url,$data) { function post ($url,$data) {
$time = microtime(1);
curl_setopt($this->ch,CURLOPT_URL,$url);
curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,1); curl_setopt($this-&gt;ch,CURLOPT_URL,$url);
curl_setopt($this->ch,CURLOPT_MAXREDIRS,10); curl_setopt($this-&gt;ch,CURLOPT_FOLLOWLOCATION,$this-&gt;postfollowredirs);
curl_setopt($this->ch,CURLOPT_HEADER,0); curl_setopt($this-&gt;ch,CURLOPT_MAXREDIRS,10);
curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($this-&gt;ch,CURLOPT_HEADER,0);
curl_setopt($this->ch,CURLOPT_TIMEOUT,30); curl_setopt($this-&gt;ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10); curl_setopt($this-&gt;ch,CURLOPT_TIMEOUT,30);
curl_setopt($this->ch,CURLOPT_POST,1); curl_setopt($this-&gt;ch,CURLOPT_CONNECTTIMEOUT,10);
curl_setopt($this->ch,CURLOPT_POSTFIELDS, substr($this->data_encode($data), 0, -1) ); curl_setopt($this-&gt;ch,CURLOPT_POST,1);
return curl_exec($this->ch); curl_setopt($this-&gt;ch,CURLOPT_POSTFIELDS, $data);
curl_setopt($this-&gt;ch,CURLOPT_HTTPHEADER, array('Expect:'));
$data = curl_exec($this-&gt;ch);
global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'POST: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data).&quot; b)\n&quot;);
return $data;
} }


/**
* Get a URL.
* @param $url The URL to get.
* @return Data retrieved from the GET request.
**/
function get ($url) { function get ($url) {
$time = microtime(1);
curl_setopt($this->ch,CURLOPT_URL,$url);
curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,1); curl_setopt($this-&gt;ch,CURLOPT_URL,$url);
curl_setopt($this->ch,CURLOPT_MAXREDIRS,10); curl_setopt($this-&gt;ch,CURLOPT_FOLLOWLOCATION,$this-&gt;getfollowredirs);
curl_setopt($this->ch,CURLOPT_HEADER,0); curl_setopt($this-&gt;ch,CURLOPT_MAXREDIRS,10);
curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($this-&gt;ch,CURLOPT_HEADER,0);
curl_setopt($this->ch,CURLOPT_TIMEOUT,30); curl_setopt($this-&gt;ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10); curl_setopt($this-&gt;ch,CURLOPT_TIMEOUT,30);
curl_setopt($this->ch,CURLOPT_HTTPGET,1); curl_setopt($this-&gt;ch,CURLOPT_CONNECTTIMEOUT,10);
return curl_exec($this->ch); curl_setopt($this-&gt;ch,CURLOPT_HTTPGET,1);
$data = curl_exec($this-&gt;ch);
global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'GET: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data).&quot; b)\n&quot;);
return $data;
} }


/**
* Our destructor. Cleans up cURL and unlinks temporary files.
**/
function __destruct () { function __destruct () {
curl_close($this->ch); curl_close($this-&gt;ch);
@unlink('/tmp/cluewikibot.cookies.'.$this->uid.'.dat'); @unlink('/tmp/cluewikibot.cookies.'.$this-&gt;uid.'.dat');
} }
} }


/**
* This class is a deprecated wrapper class which allows legacy code written for Misplaced Pages's query.php API to still work with wikipediaapi::.
**/
class wikipediaquery { class wikipediaquery {
private $http; private $http;
private $api;
private $queryurl = 'http://en.wikipedia.org/w/query.php';
public $queryurl = 'http://en.wikipedia.org/w/query.php'; //Obsolete, but kept for compatibility purposes.


/**
* This is our constructor.
* @return void
**/
function __construct () { function __construct () {
global $__wp__http; global $__wp__http;
Line 67: Line 111:
$__wp__http = new http; $__wp__http = new http;
} }
$this->http = &$__wp__http; $this-&gt;http = &amp;$__wp__http;
$this-&gt;api = new wikipediaapi;
} }

/**
* Reinitializes the queryurl.
* @private
* @return void
**/
private function checkurl() {
$this-&gt;api-&gt;apiurl = str_replace('query.php','api.php',$this-&gt;queryurl);
}

/**
* Gets the content of a page.
* @param $page The wikipedia page to fetch.
* @return The wikitext for the page.
**/
function getpage ($page) { function getpage ($page) {
$this-&gt;checkurl();
$ret = unserialize($this->http->get($this->queryurl.'?what=content&format=php&titles='.urlencode($page)));
$ret = $this-&gt;api-&gt;revisions($page,1,'older',true,null,true,false,false,false);
foreach ($ret as $page) {
return $page; return $ret;
} }

/**
* Gets the page id for a page.
* @param $page The wikipedia page to get the id for.
* @return The page id of the page.
**/
function getpageid ($page) {
$this-&gt;checkurl();
$ret = $this-&gt;api-&gt;revisions($page,1,'older',false,null,true,false,false,false);
return $ret;
}

/**
* Gets the number of contributions a user has.
* @param $user The username for which to get the edit count.
* @return The number of contributions the user has.
**/
function contribcount ($user) {
$this-&gt;checkurl();
$ret = $this-&gt;api-&gt;users($user,1,null,true);
if ($ret !== false) return $ret;
return false;
} }
} }


/**
* This class is for interacting with Misplaced Pages's api.php API.
**/
class wikipediaapi { class wikipediaapi {
private $http; private $http;
private $apiurl = 'http://en.wikipedia.org/w/api.php'; private $edittoken;
private $tokencache;
private $user, $pass;
public $apiurl = 'http://en.wikipedia.org/w/api.php';


/**
* This is our constructor.
* @return void
**/
function __construct () { function __construct () {
global $__wp__http; global $__wp__http;
Line 87: Line 178:
$__wp__http = new http; $__wp__http = new http;
} }
$this->http = &$__wp__http; $this-&gt;http = &amp;$__wp__http;
} }


/**
* This function takes a username and password and logs you into wikipedia.
* @param $user Username to login as.
* @param $pass Password that corrisponds to the username.
* @return void
**/
function login ($user,$pass) { function login ($user,$pass) {
$this-&gt;user = $user;
$this->http->post($this->apiurl.'?action=login',array('lgname' => $user, 'lgpassword' => $pass));
$this-&gt;pass = $pass;
$x = unserialize($this-&gt;http-&gt;post($this-&gt;apiurl.'?action=login&amp;format=php',array('lgname' =&gt; $user, 'lgpassword' =&gt; $pass)));
if($x == 'Success')
return true;
if($x == 'NeedToken') {
$x = unserialize($this-&gt;http-&gt;post($this-&gt;apiurl.'?action=login&amp;format=php',array('lgname' =&gt; $user, 'lgpassword' =&gt; $pass, 'lgtoken' =&gt; $x)));
if($x == 'Success')
return true;
}
return false;
} }


/**
* This function returns the edit token.
* @return Edit token.
**/
function getedittoken () {
$tokens = $this-&gt;gettokens('Main Page');
if ($tokens == '') $tokens = $this-&gt;gettokens('Main Page',true);
$this-&gt;edittoken = $tokens;
return $tokens;
}

/**
* This function returns the various tokens for a certain page.
* @param $title Page to get the tokens for.
* @param $flush Optional - internal use only. Flushes the token cache.
* @return An associative array of tokens for the page.
**/
function gettokens ($title,$flush = false) {
if (!is_array($this-&gt;tokencache)) $this-&gt;tokencache = array();
foreach ($this-&gt;tokencache as $t =&gt; $data) if (time() - $data &gt; 6*60*60) unset($this-&gt;tokencache);
if (isset($this-&gt;tokencache) &amp;&amp; (!$flush)) {
return $this-&gt;tokencache;
} else {
$tokens = array();
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;format=php&amp;prop=info&amp;intoken=edit|delete|protect|move|block|unblock|email&amp;titles='.urlencode($title));
$x = unserialize($x);
foreach ($x as $y) {
$tokens = $y;
$tokens = $y;
$tokens = $y;
$tokens = $y;
$tokens = $y;
$tokens = $y;
$tokens = $y;
$this-&gt;tokencache = array(
'timestamp' =&gt; time(),
'tokens' =&gt; $tokens
);
return $tokens;
}
}
}

/**
* This function returns the recent changes for the wiki.
* @param $count The number of items to return. (Default 10)
* @param $namespace The namespace ID to filter items on. Null for no filtering. (Default null)
* @param $dir The direction to pull items. &quot;older&quot; or &quot;newer&quot;. (Default 'older')
* @param $ts The timestamp to start at. Null for the beginning/end (depending on direction). (Default null)
* @return Associative array of recent changes metadata.
**/
function recentchanges ($count = 10,$namespace = null,$dir = 'older',$ts = null) { function recentchanges ($count = 10,$namespace = null,$dir = 'older',$ts = null) {
$append = ''; $append = '';
if ($ts != null) { $append .= '&rcstart='.urlencode($ts); } if ($ts !== null) { $append .= '&amp;rcstart='.urlencode($ts); }
$append .= '&rcdir='.urlencode($dir); $append .= '&amp;rcdir='.urlencode($dir);
if ($namespace != null) { $append .= '&rcnamespace='.urlencode($namespace); } if ($namespace !== null) { $append .= '&amp;rcnamespace='.urlencode($namespace); }
$x = $this->http->get($this->apiurl.'?action=query&list=recentchanges&rcprop=user|comment|flags|timestamp|title|ids|sizes&format=php&rclimit='.$count.$append); $x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=recentchanges&amp;rcprop=user|comment|flags|timestamp|title|ids|sizes&amp;format=php&amp;rclimit='.$count.$append);
$x = unserialize($x); $x = unserialize($x);
return $x; return $x;
}

/**
* This function returns search results from Misplaced Pages's internal search engine.
* @param $search The query string to search for.
* @param $limit The number of results to return. (Default 10)
* @param $offset The number to start at. (Default 0)
* @param $namespace The namespace ID to filter by. Null means no filtering. (Default 0)
* @param $what What to search, 'text' or 'title'. (Default 'text')
* @param $redirs Whether or not to list redirects. (Default false)
* @return Associative array of search result metadata.
**/
function search ($search,$limit = 10,$offset = 0,$namespace = 0,$what = 'text',$redirs = false) {
$append = '';
if ($limit != null) $append .= '&amp;srlimit='.urlencode($limit);
if ($offset != null) $append .= '&amp;sroffset='.urlencode($offset);
if ($namespace != null) $append .= '&amp;srnamespace='.urlencode($namespace);
if ($what != null) $append .= '&amp;srwhat='.urlencode($what);
if ($redirs == true) $append .= '&amp;srredirects=1';
else $append .= '&amp;srredirects=0';
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=search&amp;format=php&amp;srsearch='.urlencode($search).$append);
$x = unserialize($x);
return $x;
}

/**
* Retrieve entries from the WikiLog.
* @param $user Username who caused the entry. Null means anyone. (Default null)
* @param $title Object to which the entry refers. Null means anything. (Default null)
* @param $limit Number of entries to return. (Default 50)
* @param $type Type of logs. Null means any type. (Default null)
* @param $start Date to start enumerating logs. Null means beginning/end depending on $dir. (Default null)
* @param $end Where to stop enumerating logs. Null means whenever limit is satisfied or there are no more logs. (Default null)
* @param $dir Direction to enumerate logs. &quot;older&quot; or &quot;newer&quot;. (Default 'older')
* @return Associative array of logs metadata.
**/
function logs ($user = null,$title = null,$limit = 50,$type = null,$start = null,$end = null,$dir = 'older') {
$append = '';
if ($user != null) $append.= '&amp;leuser='.urlencode($user);
if ($title != null) $append.= '&amp;letitle='.urlencode($title);
if ($limit != null) $append.= '&amp;lelimit='.urlencode($limit);
if ($type != null) $append.= '&amp;letype='.urlencode($type);
if ($start != null) $append.= '&amp;lestart='.urlencode($start);
if ($end != null) $append.= '&amp;leend='.urlencode($end);
if ($dir != null) $append.= '&amp;ledir='.urlencode($dir);
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;format=php&amp;list=logevents&amp;leprop=ids|title|type|user|timestamp|comment|details'.$append);
$x = unserialize($x);
return $x;
}

/**
* Retrieves metadata about a user's contributions.
* @param $user Username whose contributions we want to retrieve.
* @param $count Number of entries to return. (Default 50)
* @param $continue Where to continue enumerating if part of a larger, split request. This is filled with the next logical continuation value. (Default null)
* @param $dir Which direction to enumerate from, &quot;older&quot; or &quot;newer&quot;. (Default 'older')
* @return Associative array of contributions metadata.
**/
function usercontribs ($user,$count = 50,&amp;$continue = null,$dir = 'older') {
if ($continue != null) {
$append = '&amp;ucstart='.urlencode($continue);
} else {
$append = '';
}
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;format=php&amp;list=usercontribs&amp;ucuser='.urlencode($user).'&amp;uclimit='.urlencode($count).'&amp;ucdir='.urlencode($dir).$append);
$x = unserialize($x);
$continue = $x;
return $x;
}

/**
* Returns revision data (meta and/or actual).
* @param $page Page for which to return revision data for.
* @param $count Number of revisions to return. (Default 1)
* @param $dir Direction to start enumerating multiple revisions from, &quot;older&quot; or &quot;newer&quot;. (Default 'older')
* @param $content Whether to return actual revision content, true or false. (Default false)
* @param $revid Revision ID to start at. (Default null)
* @param $wait Whether or not to wait a few seconds for the specific revision to become available. (Default true)
* @param $getrbtok Whether or not to retrieve a rollback token for the revision. (Default false)
* @param $dieonerror Whether or not to kill the process with an error if an error occurs. (Default false)
* @param $redirects Whether or not to follow redirects. (Default false)
* @return Associative array of revision data.
**/
function revisions ($page,$count = 1,$dir = 'older',$content = false,$revid = null,$wait = true,$getrbtok = false,$dieonerror = true,$redirects = false) {
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;prop=revisions&amp;titles='.urlencode($page).'&amp;rvlimit='.urlencode($count).'&amp;rvprop=timestamp|ids|user|comment'.(($content)?'|content':'').'&amp;format=php&amp;meta=userinfo&amp;rvdir='.urlencode($dir).(($revid !== null)?'&amp;rvstartid='.urlencode($revid):'').(($getrbtok == true)?'&amp;rvtoken=rollback':'').(($redirects == true)?'&amp;redirects':''));
$x = unserialize($x);
if ($revid !== null) {
$found = false;
if (!isset($x) or !is_array($x)) {
if ($dieonerror == true) die('No such page.'.&quot;\n&quot;);
else return false;
}
foreach ($x as $data) {
if (!isset($data) or !is_array($data)) {
if ($dieonerror == true) die('No such page.'.&quot;\n&quot;);
else return false;
}
foreach ($data as $data2) if ($data2 == $revid) $found = true;
unset($data,$data2);
break;
}

if ($found == false) {
if ($wait == true) {
sleep(1);
return $this-&gt;revisions($page,$count,$dir,$content,$revid,false,$getrbtok,$dieonerror);
} else {
if ($dieonerror == true) die('Revision error.'.&quot;\n&quot;);
}
}
}
foreach ($x as $key =&gt; $data) {
$data = $data;
$data = $data;
$data = $x;
// $data = $x;
$data = $x;
$data = $key;
return $data;
}
}

/**
* Enumerates user metadata.
* @param $start The username to start enumerating from. Null means from the beginning. (Default null)
* @param $limit The number of users to enumerate. (Default 1)
* @param $group The usergroup to filter by. Null means no filtering. (Default null)
* @param $requirestart Whether or not to require that $start be a valid username. (Default false)
* @param $continue This is filled with the name to continue from next query. (Default null)
* @return Associative array of user metadata.
**/
function users ($start = null,$limit = 1,$group = null,$requirestart = false,&amp;$continue = null) {
$append = '';
if ($start != null) $append .= '&amp;aufrom='.urlencode($start);
if ($group != null) $append .= '&amp;augroup='.urlencode($group);
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=allusers&amp;format=php&amp;auprop=blockinfo|editcount|registration|groups&amp;aulimit='.urlencode($limit).$append);
$x = unserialize($x);
$continue = $x;
if (($requirestart == true) and ($x != $start)) return false;
return $x;
}

/**
* Get members of a category.
* @param $category Category to enumerate from.
* @param $count Number of members to enumerate. (Default 500)
* @param $continue Where to continue enumerating from. This is automatically filled in when run. (Default null)
* @return Associative array of category member metadata.
**/
function categorymembers ($category,$count = 500,&amp;$continue = null) {
if ($continue != null) {
$append = '&amp;cmcontinue='.urlencode($continue);
} else {
$append = '';
}
$category = 'Category:'.str_ireplace('category:','',$category);
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=categorymembers&amp;cmtitle='.urlencode($category).'&amp;format=php&amp;cmlimit='.$count.$append);
$x = unserialize($x);
$continue = $x;
return $x;
}

/**
* Enumerate all categories.
* @param $start Where to start enumerating. This is updated automatically with the value to continue from. (Default null)
* @param $limit Number of categories to enumerate. (Default 50)
* @param $dir Direction to enumerate in. 'ascending' or 'descending'. (Default 'ascending')
* @param $prefix Only enumerate categories with this prefix. (Default null)
* @return Associative array of category list metadata.
**/
function listcategories (&amp;$start = null,$limit = 50,$dir = 'ascending',$prefix = null) {
$append = '';
if ($start != null) $append .= '&amp;acfrom='.urlencode($start);
if ($limit != null) $append .= '&amp;aclimit='.urlencode($limit);
if ($dir != null) $append .= '&amp;acdir='.urlencode($dir);
if ($prefix != null) $append .= '&amp;acprefix='.urlencode($prefix);

$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=allcategories&amp;acprop=size&amp;format=php'.$append);
$x = unserialize($x);

$start = $x;

return $x;
}

/**
* Enumerate all backlinks to a page.
* @param $page Page to search for backlinks to.
* @param $count Number of backlinks to list. (Default 500)
* @param $continue Where to start enumerating from. This is automatically filled in. (Default null)
* @param $filter Whether or not to include redirects. Acceptible values are 'all', 'redirects', and 'nonredirects'. (Default null)
* @return Associative array of backlink metadata.
**/
function backlinks ($page,$count = 500,&amp;$continue = null,$filter = null) {
if ($continue != null) {
$append = '&amp;blcontinue='.urlencode($continue);
} else {
$append = '';
}
if ($filter != null) {
$append .= '&amp;blfilterredir='.urlencode($filter);
}

$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=backlinks&amp;bltitle='.urlencode($page).'&amp;format=php&amp;bllimit='.$count.$append);
$x = unserialize($x);
$continue = $x;
return $x;
}

/**
* Gets a list of transcludes embedded in a page.
* @param $page Page to look for transcludes in.
* @param $count Number of transcludes to list. (Default 500)
* @param $continue Where to start enumerating from. This is automatically filled in. (Default null)
* @return Associative array of transclude metadata.
**/
function embeddedin ($page,$count = 500,&amp;$continue = null) {
if ($continue != null) {
$append = '&amp;eicontinue='.urlencode($continue);
} else {
$append = '';
}
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=embeddedin&amp;eititle='.urlencode($page).'&amp;format=php&amp;eilimit='.$count.$append);
$x = unserialize($x);
$continue = $x;
return $x;
}

/**
* Gets a list of pages with a common prefix.
* @param $prefix Common prefix to search for.
* @param $namespace Numeric namespace to filter on. (Default 0)
* @param $count Number of pages to list. (Default 500)
* @param $continue Where to start enumerating from. This is automatically filled in. (Default null)
* @return Associative array of page metadata.
**/
function listprefix ($prefix,$namespace = 0,$count = 500,&amp;$continue = null) {
$append = '&amp;apnamespace='.urlencode($namespace);
if ($continue != null) {
$append .= '&amp;apfrom='.urlencode($continue);
}
$x = $this-&gt;http-&gt;get($this-&gt;apiurl.'?action=query&amp;list=allpages&amp;apprefix='.urlencode($prefix).'&amp;format=php&amp;aplimit='.$count.$append);
$x = unserialize($x);
$continue = $x;
return $x;
}

/**
* Edits a page.
* @param $page Page name to edit.
* @param $data Data to post to page.
* @param $summary Edit summary to use.
* @param $minor Whether or not to mark edit as minor. (Default false)
* @param $bot Whether or not to mark edit as a bot edit. (Default true)
* @param $wpStarttime Time in MW TS format of beginning of edit. (Default now)
* @param $wpEdittime Time in MW TS format of last edit to that page. (Default correct)
* @return boolean True on success, false on failure.
**/
function edit ($page,$data,$summary = '',$minor = false,$bot = true,$wpStarttime = null,$wpEdittime = null,$checkrun = true) {
global $run, $user;

$wpq = new wikipediaquery; $wpq-&gt;queryurl = str_replace('api.php','query.php',$this-&gt;apiurl);

if ($checkrun == true)
if (!preg_match('/(yes|enable|true)/iS',((isset($run))?$run:$wpq-&gt;getpage('User:'.$user.'/Run'))))
return false; /* Check /Run page */

$params = Array(
'action' =&gt; 'edit',
'format' =&gt; 'php',
'assert' =&gt; 'bot',
'title' =&gt; $page,
'text' =&gt; $data,
'token' =&gt; $this-&gt;getedittoken(),
'summary' =&gt; $summary,
($minor?'minor':'notminor') =&gt; '1',
($bot?'bot':'notbot') =&gt; '1'
);

if ($wpStarttime !== null) $params = $wpStarttime;
if ($wpEdittime !== null) $params = $wpEdittime;

$x = $this-&gt;http-&gt;post($this-&gt;apiurl,$params);
$x = unserialize($x);
var_export($x);
if ($x == 'Success') return true;
if ($x == 'badtoken') {
if($this-&gt;login($this-&gt;user,$this-&gt;pass)) {
$this-&gt;gettokens('Main Page',true);
return $this-&gt;edit($page,$data,$summary,$minor,$bot,$wpStarttime,$wpEdittime,$checkrun);
} else
return false;
}
else return false;
}

/**
* Moves a page.
* @param $old Name of page to move.
* @param $new New page title.
* @param $reason Move summary to use.
* @return void
**/
function move ($old,$new,$reason) {
$tokens = $this-&gt;gettokens($old);
$params = array(
'action' =&gt; 'move',
'format' =&gt; 'php',
'from' =&gt; $old,
'to' =&gt; $new,
'token' =&gt; $tokens,
'reason' =&gt; $reason
);

$x = $this-&gt;http-&gt;post($this-&gt;apiurl,$params);
$x = unserialize($x);
var_export($x);
}

/**
* Rollback an edit.
* @param $title Title of page to rollback.
* @param $user Username of last edit to the page to rollback.
* @param $reason Edit summary to use for rollback.
* @param $token Rollback token. If not given, it will be fetched. (Default null)
* @return void
**/
function rollback ($title,$user,$reason,$token = null) {
if (($token == null) or ($token == '')) {
$token = $this-&gt;revisions($title,1,'older',false,null,true,true);
print_r($token);
if ($token == $user) {
$token = $token;
} else {
return false;
}
}
$params = array(
'action' =&gt; 'rollback',
'format' =&gt; 'php',
'title' =&gt; $title,
'user' =&gt; $user,
'summary' =&gt; $reason,
'token' =&gt; $token,
'markbot' =&gt; 0
);

echo 'Posting to API: ';
var_export($params);
$x = $this-&gt;http-&gt;post($this-&gt;apiurl,$params);
$x = unserialize($x);
var_export($x);
return ( isset($x) and isset( $x ) and $x )
? true
: false;
} }
} }


/**
* This class is for interacting with Misplaced Pages's browser interface, index.php.
* Many of these functions are deprecated.
**/
class wikipediaindex { class wikipediaindex {
private $http; private $http;
private $indexurl = 'http://en.wikipedia.org/search/'; public $indexurl = 'http://en.wikipedia.org/search/';
private $postinterval = 0;
private $lastpost;
private $edittoken;


/**
* This is our constructor.
* @return void
**/
function __construct () { function __construct () {
global $__wp__http; global $__wp__http;
Line 114: Line 639:
$__wp__http = new http; $__wp__http = new http;
} }
$this->http = &$__wp__http; $this-&gt;http = &amp;$__wp__http;
} }


/**
function post ($page,$data,$summery = '') {
* Post data to a page, nicely.
* @param $page Page title.
* @param $data Data to post to page.
* @param $summery Edit summary. (Default '')
* @param $minor Whether to mark edit as minor. (Default false)
* @param $rv Revision data. If not given, it will be fetched. (Default null)
* @param $bot Whether to mark edit as bot. (Default true)
* @return HTML data from the page.
* @deprecated
* @see wikipediaapi::edit
**/
function post ($page,$data,$summery = '',$minor = false,$rv = null,$bot = true) {
global $user; global $user;
global $maxlag;
$html = $this->http->get($this->indexurl.'?title='.urlencode($page).'&action=edit');
global $irc;
if (preg_match('/'.preg_quote('{{nobots}}','/').'/i',$html)) { return false; } /* Honor the bots flags */
global $irctechchannel;
if (preg_match('/'.preg_quote('{{bots|allow=none}}','/').'/i',$html)) { return false; }
global $run;
if (preg_match('/'.preg_quote('{{bots|deny=all}}','/').'/i',$html)) { return false; }
global $maxlagkeepgoing;
if (preg_match('/'.preg_quote('{{bots|deny=','/').'(.*)'.preg_quote('}}','/').'/i',$html,$m)) { if (in_array(explode(',',$m),$user)) { return false; } } /* /Honor the bots flags */

if (!preg_match('/'.preg_quote($user,'/').'/i',$html)) { return false; } /* We need to be logged in */
$wpq = new wikipediaquery; $wpq-&gt;queryurl = str_replace('index.php','query.php',$this-&gt;indexurl);
if (preg_match('/'.preg_quote('You have new messages','/').'/i',$html)) { return false; } /* Check talk page */
$wpapi = new wikipediaapi; $wpapi-&gt;apiurl = str_replace('index.php','api.php',$this-&gt;indexurl);
$wpq = new wikipediaquery;

if (!preg_match('/(yes|enable|true)/i',$wpq->getpage('User:'.$user.'/Run'))) { return false; } /* Check /Run page */
if ((!$this-&gt;edittoken) or ($this-&gt;edittoken == '')) $this-&gt;edittoken = $wpapi-&gt;getedittoken();
return $this->forcepost($page,$data,$summery); /* Go ahead and post. */
if ($rv == null) $rv = $wpapi-&gt;revisions($page,1,'older',true);
if (!$rv) $rv = $wpq-&gt;getpage($page);

//Fake the edit form.
$now = gmdate('YmdHis', time());
$token = htmlspecialchars($this-&gt;edittoken);
$tmp = date_parse($rv);
$edittime = gmdate('YmdHis', gmmktime($tmp,$tmp,$tmp,$tmp,$tmp,$tmp));
$html = &quot;&lt;input type='hidden' value=\&quot;{$now}\&quot; name=\&quot;wpStarttime\&quot; /&gt;\n&quot;;
$html.= &quot;&lt;input type='hidden' value=\&quot;{$edittime}\&quot; name=\&quot;wpEdittime\&quot; /&gt;\n&quot;;
$html.= &quot;&lt;input type='hidden' value=\&quot;{$token}\&quot; name=\&quot;wpEditToken\&quot; /&gt;\n&quot;;
$html.= '&lt;input name=&quot;wpAutoSummary&quot; type=&quot;hidden&quot; value=&quot;'.md5('').'&quot; /&gt;'.&quot;\n&quot;;

if (preg_match('/'.preg_quote('{{nobots}}','/').'/iS',$rv)) { return false; } /* Honor the bots flags */
if (preg_match('/'.preg_quote('{{bots|allow=none}}','/').'/iS',$rv)) { return false; }
if (preg_match('/'.preg_quote('{{bots|deny=all}}','/').'/iS',$rv)) { return false; }
if (preg_match('/'.preg_quote('{{bots|deny=','/').'(.*)'.preg_quote('}}','/').'/iS',$rv,$m)) { if (in_array(explode(',',$m),$user)) { return false; } } /* /Honor the bots flags */
if (!preg_match('/'.preg_quote($user,'/').'/iS',$rv)) { return false; } /* We need to be logged in */
// if (preg_match('/'.preg_quote('You have new messages','/').'/iS',$rv)) { return false; } /* Check talk page */
if (!preg_match('/(yes|enable|true)/iS',((isset($run))?$run:$wpq-&gt;getpage('User:'.$user.'/Run')))) { return false; } /* Check /Run page */

$x = $this-&gt;forcepost($page,$data,$summery,$minor,$html,$maxlag,$maxlagkeepgoing,$bot); /* Go ahead and post. */
$this-&gt;lastpost = time();
return $x;
} }


/**
function forcepost ($page,$data,$summery = '') {
* Post data to a page.
* @param $page Page title.
* @param $data Data to post to page.
* @param $summery Edit summary. (Default '')
* @param $minor Whether to mark edit as minor. (Default false)
* @param $edithtml HTML from the edit form. If not given, it will be fetched. (Default null)
* @param $maxlag Maxlag for posting. (Default null)
* @param $mlkg Whether to keep going after encountering a maxlag error and sleeping or not. (Default null)
* @param $bot Whether to mark edit as bot. (Default true)
* @return HTML data from the page.
* @deprecated
* @see wikipediaapi::edit
**/
function forcepost ($page,$data,$summery = '',$minor = false,$edithtml = null,$maxlag = null,$mlkg = null,$bot = true) {
$post = ''; $post = '';
$post = ''; $post = '';
if ($minor == true) { $post = 1; }
$post = $data; $post = $data;
$post = $summery; $post = $summery;
$html = $this->http->get($this->indexurl.'?title='.urlencode($page).'&action=edit'); if ($edithtml == null) {
$html = $this-&gt;http-&gt;get($this-&gt;indexurl.'?title='.urlencode($page).'&amp;action=edit');
} else {
preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpStarttime\" /\>|U',$html,$m);
$html = $edithtml;
}
preg_match('|\&lt;input type\=\\\'hidden\\\' value\=\&quot;(.*)\&quot; name\=\&quot;wpStarttime\&quot; /\&gt;|U',$html,$m);
$post = $m; $post = $m;
preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEdittime\" /\>|U',$html,$m); preg_match('|\&lt;input type\=\\\'hidden\\\' value\=\&quot;(.*)\&quot; name\=\&quot;wpEdittime\&quot; /\&gt;|U',$html,$m);
$post = $m; $post = $m;
preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEditToken\" /\>|U',$html,$m); preg_match('|\&lt;input type\=\\\'hidden\\\' value\=\&quot;(.*)\&quot; name\=\&quot;wpEditToken\&quot; /\&gt;|U',$html,$m);
$post = $m; $post = $m;
preg_match('|\<input name\=\"wpAutoSummary\" type\=\"hidden\" value\=\"(.*)\" /\>|U',$html,$m); preg_match('|\&lt;input name\=\&quot;wpAutoSummary\&quot; type\=\&quot;hidden\&quot; value\=\&quot;(.*)\&quot; /\&gt;|U',$html,$m);
$post = $m; $post = $m;
if ($maxlag != null) {
$this->http->post($this->indexurl.'?title='.urlencode($page).'&action=submit',$post);
$x = $this-&gt;http-&gt;post($this-&gt;indexurl.'?title='.urlencode($page).'&amp;action=submit&amp;maxlag='.urlencode($maxlag).'&amp;bot='.(($bot == true)?'1':'0'),$post);
if (preg_match('/Waiting for (*): (+) seconds lagged/S',$x,$lagged)) {
global $irc;
if (is_resource($irc)) {
global $irctechchannel;
foreach(explode(',',$irctechchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :'.$lagged.' is lagged out by '.$lagged.' seconds. ('.$lagged.')'.&quot;\n&quot;);
}
}
sleep(10);
if ($mlkg != true) { return false; }
else { $x = $this-&gt;http-&gt;post($this-&gt;indexurl.'?title='.urlencode($page).'&amp;action=submit&amp;bot='.(($bot == true)?'1':'0'),$post); }
}
return $x;
} else {
return $this-&gt;http-&gt;post($this-&gt;indexurl.'?title='.urlencode($page).'&amp;action=submit&amp;bot='.(($bot == true)?'1':'0'),$post);
}
}

/**
* Get a diff.
* @param $title Page title to get the diff of.
* @param $oldid Old revision ID.
* @param $id New revision ID.
* @param $wait Whether or not to wait for the diff to become available. (Default true)
* @return Array of added data, removed data, and a rollback token if one was fetchable.
**/
function diff ($title,$oldid,$id,$wait = true) {
$deleted = '';
$added = '';

$html = $this-&gt;http-&gt;get($this-&gt;indexurl.'?title='.urlencode($title).'&amp;action=render&amp;diff='.urlencode($id).'&amp;oldid='.urlencode($oldid).'&amp;diffonly=1');

if (preg_match_all('/\&amp;amp\;(oldid\=)(\d*)\\\'\&gt;(Revision as of|Current revision as of)/USs', $html, $m, PREG_SET_ORDER)) {
//print_r($m);
if ((($oldid != $m) and (is_numeric($oldid))) or (($id != $m) and (is_numeric($id)))) {
if ($wait == true) {
sleep(1);
return $this-&gt;diff($title,$oldid,$id,false);
} else {
echo 'OLDID as detected: '.$m.' Wanted: '.$oldid.&quot;\n&quot;;
echo 'NEWID as detected: '.$m.' Wanted: '.$id.&quot;\n&quot;;
echo $html;
die('Revision error.'.&quot;\n&quot;);
}
}
}
if (preg_match_all('/\&lt;td class\=(\&quot;|\\\')diff-addedline\1\&gt;\&lt;div\&gt;(.*)\&lt;\/div\&gt;\&lt;\/td\&gt;/USs', $html, $m, PREG_SET_ORDER)) {
//print_r($m);
foreach ($m as $x) {
$added .= htmlspecialchars_decode(strip_tags($x)).&quot;\n&quot;;
}
}

if (preg_match_all('/\&lt;td class\=(\&quot;|\\\')diff-deletedline\1\&gt;\&lt;div\&gt;(.*)\&lt;\/div\&gt;\&lt;\/td\&gt;/USs', $html, $m, PREG_SET_ORDER)) {
//print_r($m);
foreach ($m as $x) {
$deleted .= htmlspecialchars_decode(strip_tags($x)).&quot;\n&quot;;
}
}

//echo $added.&quot;\n&quot;.$deleted.&quot;\n&quot;;

if (preg_match('/action\=rollback\&amp;amp\;from\=.*\&amp;amp\;token\=(.*)\&quot;/US', $html, $m)) {
$rbtoken = $m;
$rbtoken = urldecode($rbtoken);
// echo 'rbtoken: '.$rbtoken.' -- '; print_r($m); echo &quot;\n\n&quot;;
return array($added,$deleted,$rbtoken);
}

return array($added,$deleted);
}

/**
* Rollback an edit.
* @param $title Page title to rollback.
* @param $user Username of last edit to the page to rollback.
* @param $reason Reason to rollback. If null, default is generated. (Default null)
* @param $token Rollback token to use. If null, it is fetched. (Default null)
* @param $bot Whether or not to mark as bot. (Default true)
* @return HTML or false if failure.
* @deprecated
* @see wikipediaapi::rollback
**/
function rollback ($title,$user,$reason = null,$token = null,$bot = true) {
if (($token == null) or (!$token)) {
$wpapi = new wikipediaapi; $wpapi-&gt;apiurl = str_replace('index.php','api.php',$this-&gt;indexurl);
$token = $wpapi-&gt;revisions($title,1,'older',false,null,true,true);
if ($token == $user) {
// echo 'Token: '; print_r($token); echo &quot;\n\n&quot;;
$token = $token;
} else {
return false;
}
}
$x = $this-&gt;http-&gt;get($this-&gt;indexurl.'?title='.urlencode($title).'&amp;action=rollback&amp;from='.urlencode($user).'&amp;token='.urlencode($token).(($reason != null)?'&amp;summary='.urlencode($reason):'').'&amp;bot='.(($bot == true)?'1':'0'));
global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'Rollback return: '.$x.&quot;\n&quot;);
if (!preg_match('/action complete/iS',$x)) return false;
return $x;
}

/**
* Move a page.
* @param $old Page title to move.
* @param $new New title to move to.
* @param $reason Move page summary.
* @return HTML page.
* @deprecated
* @see wikipediaapi::move
**/
function move ($old,$new,$reason) {
$wpapi = new wikipediaapi; $wpapi-&gt;apiurl = str_replace('index.php','api.php',$this-&gt;indexurl);
if ((!$this-&gt;edittoken) or ($this-&gt;edittoken == '')) $this-&gt;edittoken = $wpapi-&gt;getedittoken();

$token = htmlspecialchars($this-&gt;edittoken);

$post = array
(
'wpOldTitle' =&gt; $old,
'wpNewTitle' =&gt; $new,
'wpReason' =&gt; $reason,
'wpWatch' =&gt; '0',
'wpEditToken' =&gt; $token,
'wpMove' =&gt; 'Move page'
);
return $this-&gt;http-&gt;post($this-&gt;indexurl.'?title=Special:Movepage&amp;action=submit',$post);
}

/**
* Uploads a file.
* @param $page Name of page on the wiki to upload as.
* @param $file Name of local file to upload.
* @param $desc Content of the file description page.
* @return HTML content.
**/
function upload ($page,$file,$desc) {
$post = array
(
'wpUploadFile' =&gt; '@'.$file,
'wpSourceType' =&gt; 'file',
'wpDestFile' =&gt; $page,
'wpUploadDescription' =&gt; $desc,
'wpLicense' =&gt; '',
'wpWatchthis' =&gt; '0',
'wpIgnoreWarning' =&gt; '1',
'wpUpload' =&gt; 'Upload file'
);
return $this-&gt;http-&gt;post($this-&gt;indexurl.'?title=Special:Upload&amp;action=submit',$post);
}

/**
* Check if a user has email enabled.
* @param $user Username to check whether or not the user has email enabled.
* @return True or false depending on whether or not the user has email enabled.
**/
function hasemail ($user) {
$tmp = $this-&gt;http-&gt;get($this-&gt;indexurl.'?title=Special:EmailUser&amp;target='.urlencode($user));
if (stripos($tmp,&quot;No e-mail address&quot;) !== false) return false;
return true;
}

/**
* Sends an email to a user.
* @param $user Username to send email to.
* @param $subject Subject of email to send.
* @param $body Body of email to send.
* @return HTML content.
**/
function email ($user,$subject,$body) {
$wpapi = new wikipediaapi; $wpapi-&gt;apiurl = str_replace('index.php','api.php',$this-&gt;indexurl);
if ((!$this-&gt;edittoken) or ($this-&gt;edittoken == '')) $this-&gt;edittoken = $wpapi-&gt;getedittoken();

$post = array
(
'wpSubject' =&gt; $subject,
'wpText' =&gt; $body,
'wpCCMe' =&gt; 0,
'wpSend' =&gt; 'Send',
'wpEditToken' =&gt; $this-&gt;edittoken
);

return $this-&gt;http-&gt;post($this-&gt;indexurl.'?title=Special:EmailUser&amp;target='.urlencode($user).'&amp;action=submit',$post);
} }
} }
?&gt;
</pre>





==Diff function (diff.function.php)==

<pre>&lt;?PHP
function diff ($old,$new,$nret = true,$inline = false) {
// if ($inline) {
// return str_replace(array(&quot;\n&quot;,chr(92).chr(92),'\n'),array(' ',chr(92),&quot;\n&quot;),diff(implode(&quot;\n&quot;,explode(' ',str_replace(array(chr(92),&quot;\n&quot;),array(chr(92).chr(92),'\n'),$old))),implode(&quot;\n&quot;,explode(' ',str_replace(array(chr(92),&quot;\n&quot;),array(chr(92).chr(92),'\n'),$new))),$nret,false));
// }
$file1 = tempnam('/tmp','diff_');
$file2 = tempnam('/tmp','diff_');
file_put_contents($file1,$old);
file_put_contents($file2,$new);
$out = array();
if ($inline) {
// echo 'EXEC: wdiff -3'.(($nret)?'1':'2').' '.escapeshellarg($file1).' '.escapeshellarg($file2).&quot;\n&quot;;
@exec('wdiff -3'.(($nret)?'1':'2').' '.escapeshellarg($file1).' '.escapeshellarg($file2),$out);
foreach ($out as $key =&gt; $line) {
if ($line == '======================================================================') unset($out);
elseif ($nret) $out = '&gt; '.$line;
else $out = '&lt; '.$line;
}
} else {
@exec('diff -d --suppress-common-lines '.escapeshellarg($file1).' '.escapeshellarg($file2),$out);
}
$out2 = array();
foreach ($out as $line) {
if (
(
($nret)
and (preg_match('/^\&gt; .*$/',$line))
)
or (
(!$nret)
and (preg_match('/^\&lt; .*$/',$line))
)
) {
$out2 = substr($line,2);
}
}
$out = $out2;
unset($out2);
unlink($file1);
unlink($file2);
return implode(&quot;\n&quot;,$out);
}
?&gt;
</pre>



==Source to ClueBot==

<pre>&lt;?PHP
declare(ticks = 1);

function sig_handler($signo) {
switch ($signo) {
case SIGCHLD:
while (($x = pcntl_waitpid(0, $status, WNOHANG)) != -1) {
if ($x == 0) break;
$status = pcntl_wexitstatus($status);
}
break;
}
}

pcntl_signal(SIGCHLD, &quot;sig_handler&quot;);

function score ($list,$data,&amp;$matches = null) {
$ret = 0;
foreach ($list as $preg =&gt; $pts) {
if ($x = preg_match_all($preg.'S',$data,$m)) {
// echo $x.'*'.$pts.' ('.$preg.')'.&quot;\n&quot;;
$matches = $x;
$ret += $pts * $x;
}
}
// echo 'Score: '.$ret.&quot;\n&quot;;
return $ret;
}

function myfnmatch ($pattern,$string) {
if (strlen($string) &lt; 4000) {
return fnmatch($pattern,$string);
} else {
$pattern = strtr(preg_quote($pattern, '#'), array('\*' =&gt; '.*', '\?' =&gt; '.', '\' =&gt; ']'));
if (preg_match('#^'.$pattern.'$#',$string)) return true;
return false;
}
}

include '../diff.function.php'; /* The diff function. */
include '../wikibot.classes.php'; /* The wikipedia classes. */
include 'cluebot.config.php'; /* This file is very simple, but it contains sensitive information, we just define $user, $ircserver, $ircport, $ircchannel, $pass, $owner, and $status. */
include 'cluebot.scorelist.php'; /* This file is uploaded as well as the main file. */


$wpapi = new wikipediaapi; $wpapi = new wikipediaapi;
Line 155: Line 1,015:
$wpi = new wikipediaindex; $wpi = new wikipediaindex;
var_export($wpapi-&gt;login($user,$pass));
include 'wikibot.config.php';


$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
$wpapi->login($user,$pass);
if (!$mysql) { die('Could not connect: ' . mysql_error()); }
if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }


$ircconfig = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBChannels.js'));
$wpi->post('User:'.$user.'/Source','The following is automatically generated by ].\n\n\n\n==Source to ".$user."==\n\n<code>".file_get_contents(__FILE__)."</code>\n\n\n\n] 07:23, 24 July 2007 (UTC)",
$tmp = array();
'Automated source upload.'); /* Our source code */
foreach($ircconfig as $tmpline) { if (substr($tmpline,0,1) != '#') { $tmpline = explode('=',$tmpline,2); $tmp)] = trim($tmpline); } }


$ircchannel = $tmp;
$ircdebugchannel = $tmp;
$ircreportchannel = $tmp;
$ircvandalismchannel = $tmp;
$ircaivchannel = $tmp;
$irctechchannel = $tmp;
$ircproxychannels = $tmp;
$ircunrevertedchannels = $tmp;
$ircbagtrialchannels = $tmp;
$ircotherchannels = $tmp;


unset($tmp,$tmpline);
$rc = $wpapi->recentchanges(500);

$lasttime = $rc;
$stalkbots = array();
$trialbots = explode(&quot;\n&quot;,$wpq-&gt;getpage('Misplaced Pages:Bots/Requests for approval'));
foreach ($trialbots as $trialbot) if (preg_match('/\{\{BRFA\|(.*)\|.*\|Trial\}\}/',str_replace(array(&quot;\n&quot;,&quot;\r&quot;),'',$trialbot),$m)) $stalkbots] = 1;

$irc = fsockopen($ircserver,$ircport,$ircerrno,$ircerrstr,15);
$ircpid = pcntl_fork();
if ($ircpid == 0) {
fwrite($irc,'PASS '.$ircpass.&quot;\n&quot;);
fwrite($irc,'USER '.$user.' &quot;1&quot; &quot;1&quot; :ClueBot Misplaced Pages Bot.'.&quot;\n&quot;);
fwrite($irc,'NICK '.$user.&quot;\n&quot;);
while (!feof($irc)) {
$data = str_replace(array(&quot;\n&quot;,&quot;\r&quot;),'',fgets($irc,1024));
// echo 'IRC: '.$data.&quot;\n&quot;;
$d = explode(' ',$data);
if (strtolower($d) == 'ping') {
fwrite($irc,'PONG '.$d.&quot;\n&quot;);
} elseif (($d == '376') or ($d == '422')) {
// fwrite($irc,'PRIVMSG NickServ :identify '.$pass.&quot;\n&quot;);
// sleep(2);
fwrite($irc,'JOIN '.$ircchannel.','.$ircdebugchannel.','.$ircreportchannel.','.$ircvandalismchannel.','.$ircaivchannel.','.$irctechchannel.','.$ircproxychannels.','.$ircunrevertedchannels.','.$ircbagtrialchannels.','.$ircotherchannels.&quot;\n&quot;);
foreach (explode(',',$ircchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :IRC logging enabled.'.&quot;\n&quot;);
}
} elseif (strtolower($d) == 'privmsg') {
if (substr($d,0,2) == ':!') {
if (strtolower($d) == '#wikipedia-en') { $tmp = explode('!',substr($d,1)); $cmd = 'NOTICE '.$tmp; }
elseif (strtolower($d) == strtolower($user)) { $tmp = explode('!',substr($d,1)); $cmd = 'NOTICE '.$tmp; }
else { $cmd = 'PRIVMSG '.$d; }
switch (substr(strtolower($d),2)) {
case 'edit':
if (preg_match(&quot;/\\]/&quot;,$data,$m)) {
$rv = $wpapi-&gt;revisions($m,1,'older');
fwrite($irc,$cmd.' :.']] http://en.wikipedia.org/search/?title='.urlencode($m).'&amp;diff=prev' .
'&amp;oldid='.urlencode($rv).' * '.$rv.' * '.$rv.&quot;\n&quot;);
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'stalk':
if (preg_match(&quot;/\\]/&quot;,$data,$m)) {
$uc = $wpapi-&gt;usercontribs($m,1);
fwrite($irc,$cmd.' :.']] http://en.wikipedia.org/search/?title='.urlencode($uc).'&amp;diff=prev' .
'&amp;oldid='.urlencode($uc).' * '.$m.' * '.$uc.&quot;\n&quot;);
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'beaten':
if (preg_match(&quot;/\\]/&quot;,$data,$m)) {
if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
$x = mysql_fetch_assoc(mysql_query('SELECT COUNT(`id`) AS `count` FROM `beaten` WHERE `user` = \''.mysql_real_escape_string($m).'\' GROUP BY `user`'));
$y = mysql_fetch_assoc(mysql_query('SELECT SQL_CALC_FOUND_ROWS COUNT(`id`) AS `count2` FROM `beaten` GROUP BY `user` HAVING `count2` &gt; \''.mysql_real_escape_string($x).'\' LIMIT 1'));
$z = mysql_fetch_assoc(mysql_query('SELECT FOUND_ROWS() as `ahead`'));
fwrite($irc,$cmd.' :.']] has beaten me '.(($x != '')?$x:'0').' times. There are '.$z.' users who have beaten me more times.'.&quot;\n&quot;);
unset($x,$y);
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'vandalcount':
if (preg_match(&quot;/\\]/&quot;,$data,$m)) {
$tmp = unserialize(file_get_contents('oftenvandalized.txt'));
if (isset($tmp])) {
fwrite($irc,$cmd.' :.']] has been vandalized '.count($tmp]).' time(s) in the last 48 hours.'.&quot;\n&quot;);
} else {
fwrite($irc,$cmd.' :.']] has not been vandalized in the last 48 hours.'.&quot;\n&quot;);
}
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'heuristics':
include 'cluebot.heuristics.config.php';
$stats = unserialize(file_get_contents('cluebot.heuristics.stats.txt'));
fwrite($irc,$cmd.' :I have the following heuristics enabled: '.implode(', ',$heuristics).&quot;.\n&quot;);
foreach ($stats as $heuristic =&gt; $count) {
fwrite($irc,$cmd.' :The '.$heuristic.' heuristic has been matched '.$count.' times.'.&quot;\n&quot;);
}
unset($count,$heuristic,$stats,$heuristics);
break;
case 'status':
$ov = unserialize(file_get_contents('oftenvandalized.txt'));
foreach ($ov as $title =&gt; $array) {
if (count($array) == 0) unset($ov);
}
file_put_contents('oftenvandalized.txt',serialize($ov));
$count = count($ov);

$titles = unserialize(file_get_contents('titles.txt'));
foreach ($titles as $title =&gt; $time) {
if ((time() - $time) &gt; (24*60*60)) {
unset($titles);
}
}
file_put_contents('titles.txt',serialize($titles));
$tcount = count($titles);

foreach ($ov as $x =&gt; $y) {
$ocount = count($y);
}
arsort($ocount);
foreach ($ocount as $x =&gt; $y) {
$mova = $x;
$movacount = $y;
break;
}

preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq-&gt;getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfa);
$tfa = $tfa;
if (!preg_match('/(yes|enable|true)/i',$wpq-&gt;getpage('User:'.$user.'/Run'))) {
$run = false;
} else {
$run = true;
}

$top5beat = array();

if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
$q = mysql_query('SELECT `user`,COUNT(`id`) AS `count` FROM `cluebot_enwiki`.`beaten` WHERE `user` != \'\' GROUP BY `user` HAVING `count` &gt; 1 ORDER BY `count` DESC LIMIT 5');
while ($x = mysql_fetch_assoc($q)) {
$top5beat = $x.' ('.$x.')';
}
unset($x,$q);
$top5beat = implode(' - ',$top5beat);

fwrite($irc,$cmd.' :I am '.$user.'. I am currently '.($run?'enabled':'disabled').'. I currently have '.$wpq-&gt;contribcount($user).' contributions.'.&quot;\n&quot;);

fwrite($irc,$cmd.' :I have attempted to revert '.$tcount.' unique article/user combinations in the last 24 hours. ' .
'I know of '.$count.' different articles that have been vandalized in the last 48 hours.'.&quot;\n&quot;
);

fwrite($irc,$cmd.' :] is the most vandalized page with a total of '.$movacount.' vandalisms in the last 48 hours. ' .
'Today\'s featured article is: ].'.&quot;\n&quot;
);

fwrite($irc,$cmd.' :The following users have beat me to the revert the most: '.$top5beat.&quot;\n&quot;);

fwrite($irc,$cmd.' :I log all information to '.$ircchannel.'. This channel is '.$d.'.'.&quot;\n&quot;);

unset($x,$y,$count,$ov,$tcount,$ocount,$mova,$movacount,$tfa,$run,$title,$titles,$time,$top5beat);
break;
case 'warninglevel':
if (preg_match(&quot;/\\]/&quot;,$data,$n)) {
$warning = 0;
if (preg_match_all('/&lt;!-- Template:(uw-*(\d)(im)?|Blatantvandal \(serious warning\)) --&gt;.*(\d{2}):(\d{2}), (\d+) (+) (\d{4}) \(UTC\)/iU',
$wpq-&gt;getpage('User talk:'.$n),
$match,PREG_SET_ORDER)
) {
foreach ($match as $m) {
$month = array('January' =&gt; 1, 'February' =&gt; 2, 'March' =&gt; 3,
'April' =&gt; 4, 'May' =&gt; 5, 'June' =&gt; 6, 'July' =&gt; 7,
'August' =&gt; 8, 'September' =&gt; 9, 'October' =&gt; 10,
'November' =&gt; 11, 'December' =&gt; 12
);
if ($m == 'Blatantvandal (serious warning)') $m = 4;
if ((time() - gmmktime($m,$m,0,$month],$m,$m)) &lt;= (2*24*60*60)) {
if ($m &gt; $warning) { $warning = $m; }
}
}
}
fwrite($irc,$cmd.' :.']] is at warning level '.$warning.&quot;.\n&quot;);
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'count':
if (preg_match(&quot;/\\]/&quot;,$data,$n)) {
fwrite($irc,$cmd.' :.']] has '.$wpq-&gt;contribcount($n).&quot; contributions.\n&quot;);
} else {
fwrite($irc,$cmd.' :Couldn\'t find link.'.&quot;\n&quot;);
}
break;
case 'help':
fwrite($irc,$cmd.' :Please see ].'.&quot;\n&quot;);
break;
case 'eval':
$tmp = explode(' ',$data,6);
$tmp1 = explode('!',substr($d,1));
if ($d == md5($thesecret.$tmp1.$tmp)) {
eval($tmp);
} else {
fwrite($irc,$cmd.' :Code incorrect.'.&quot;\n&quot;);
}
break;
case 'cbproxy':
$tmp = explode(' ',$data,6);
$tmp1 = explode('!',substr($d,1));
if ($tmp1 == 'ClueBot-Bopm') {
foreach (explode(',',$ircproxychannels) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :!admin '.$tmp.&quot;\n&quot;);
}
$data = $wpq-&gt;getpage('Misplaced Pages:WikiProject on open proxies');
if (strpos($data,$tmp) === false) {
$header = explode('{{proxyip2|127.0.0.1}} || Example',$data,2);
$header .= '{{proxyip2|127.0.0.1}} || Example ';
$footer = $header;
$header = $header;
$data = &quot;\n&quot;.'{{proxyip2|'.$tmp.'}} || '.$tmp.' ~~~~'.&quot;\n&quot;;
$data = $header.$data.$footer;
unset($header,$footer);
$wpapi-&gt;edit('Misplaced Pages:WikiProject on open proxies',$data,'Adding '.$tmp.'.');
unset($data);
}
}
break;
}
}
}
}
die();
}

$heuristics = &quot;==Heuristics==\n\n===Config (cluebot.heuristics.config.php)===\n\n&lt;pre&gt;&quot;.htmlentities(file_get_contents('cluebot.heuristics.config.php')).&quot;&lt;/pre&gt;\n\n&quot;;
foreach (glob('heuristics/cluebot.*.heuristic.php') as $heuristic) $heuristics .= '==='.$heuristic.&quot;===\n\n&lt;pre&gt;&quot;.htmlentities(file_get_contents($heuristic)).&quot;&lt;/pre&gt;\n\n&quot;;
unset($heuristic);

$wpapi-&gt;edit('User:'.$user.'/Source',
'The following is automatically generated by ].\n\n\n\n==Classes (wikibot.classes.php)==\n\n&lt;pre&gt;&quot; .
htmlentities(file_get_contents('../wikibot.classes.php')).&quot;&lt;/pre&gt;\n\n\n\n==Diff function (diff.function.php)==\n\n&lt;pre&gt;&quot; .
htmlentities(file_get_contents('../diff.function.php')).&quot;&lt;/pre&gt;\n\n\n\n==Source to &quot;.$user .
&quot;==\n\n&quot;.'&lt;pre&gt;'.htmlentities(file_get_contents(__FILE__)).&quot;&lt;/pre&gt;\n\n\n\n&quot; .
$heuristics .
&quot;==Score list==\n\n&quot;.'&lt;pre&gt;'.htmlentities(file_get_contents('cluebot.scorelist.php')).&quot;&lt;/pre&gt;\n\n\n\n~~~~&quot;,
'Automated source upload.'); /* Our source code, we force post this because this is *our* page, and it triggers the nobots. */

unset($heuristics);

$wpapi-&gt;edit('User:'.$user,
&quot;{{User:ClueBot Commons/Userpage}}\n&quot;,
'Automated bot userpage set.'); /* Our page, we force post this because this is *our* page. */


$tfas = 0;
$pipe = fopen('thepipe','w'); $pipe = fopen('thepipe','w');
$stdin = fopen('php://stdin','r');
$run = $wpq-&gt;getpage('User:'.$user.'/Run');
$wl = $wpq-&gt;getpage('User:'.$user.'/Whitelist');
$optin = $wpq-&gt;getpage('User:'.$user.'/Optin');
$aoptin = $wpq-&gt;getpage('User:'.$user.'/AngryOptin');

unset($tmp,$tmp2,$tmp3);

$tmp = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBAutostalk.js'));
foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $stalk] = trim($tmp3); } }
$tmp = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBAutoedit.js'));
foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $edit] = trim($tmp3); } }
unset($tmp,$tmp2,$tmp3);

print_r($stalk); print_r($edit);

while (1) { while (1) {
$feed = fsockopen($feedhost,$feedport,$feederrno,$feederrstr,30);
$rc = $wpapi->recentchanges(500,0,'newer',$lasttime);

foreach ($rc as $change) {
if (!$feed) {
if (($change - $change) <= -5000) {
sleep(10);
echo 'Possible vandalism: '.$change.' changed by '.$change.' deleting '.($change - $change).' characters on '.$change.".\n";
$feed = fsockopen($feedhost,$feedport,$feederrno,$feederrstr,30);
fwrite($pipe,'http://en.wikipedia.org/search/?title='.urlencode($change).'&action=history'."\n");
if (!$feed) die($feederrstr.' ('.$feederrno.')');
}

fwrite($feed,'USER '.$user.' &quot;1&quot; &quot;1&quot; :ClueBot Misplaced Pages Bot.'.&quot;\n&quot;);
fwrite($feed,'NICK '.$user.&quot;\n&quot;);

while (!feof($feed)) {
$rawline = fgets($feed,1024);
$line = str_replace(array(&quot;\n&quot;,&quot;\r&quot;,&quot;\002&quot;),'',$rawline);
$line = preg_replace('/\003(\d\d?(,\d\d?)?)?/','',$line);
// echo 'FEED: '.$line.&quot;\n&quot;;
if (!$line) { fclose($feed); break; }
$linea= explode(' ',$line,4);

if (strtolower($linea) == 'ping') {
fwrite($feed,'PONG '.$linea.&quot;\n&quot;);
} elseif (($linea == '376') or ($linea == '422')) {
fwrite($feed,'JOIN '.$feedchannel.&quot;\n&quot;);
} elseif ((strtolower($linea) == 'privmsg') and (strtolower($linea) == strtolower($feedchannel))) {
$message = substr($linea,1);
if (preg_match('/^\*)\]\] (\S*) (http:\/\/en\.wikipedia\.org\/w\/index\.php\?diff=(\d*)&amp;oldid=(\d*).*|http:\/\/en\.wikipedia\.org\/wiki\/\S+)? \* (*) \* (\((*)\))? (.*)$/S',$message,$m)) {
$messagereceived = microtime(1);
$change = $m;
$change = $m;
$change = $m;
$change = $m;
$change = $m;
$change = $m;
$change = $m;
$change = $m;
$change = $m;

// include 'cluebot.stalk.config.php';

$stalkchannel = array();
foreach ($stalk as $key =&gt; $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
foreach ($stalkbots as $key =&gt; $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$ircbagtrialchannels));
foreach ($edit as $key =&gt; $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change.$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
// if ($change == $owner) $stalkchannel = $ircchannel;

$stalkchannel = array_unique($stalkchannel);

foreach ($stalkchannel as $y) {
fwrite($irc,'PRIVMSG '.$y.' :New edit: .$change.']] http://en.wikipedia.org/search/?title=' .
urlencode($change.$change).'&amp;diff=prev'.'&amp;oldid='.urlencode($change).' * '.$change .
' * '.$change.&quot;\n&quot;);
}
if (($change == 'User:') or ($change == 'User talk:')) {
if (strtolower($change) == strtolower($user.'/Run')) { $run = $wpq-&gt;getpage('User:'.$user.'/Run'); }
if (strtolower($change) == strtolower($user.'/Whitelist')) { $wl = $wpq-&gt;getpage('User:'.$user.'/Whitelist'); }
if (strtolower($change) == strtolower($user.'/Optin')) { $optin = $wpq-&gt;getpage('User:'.$user.'/Optin'); }
if (strtolower($change) == strtolower($user.'/AngryOptin')) { $aoptin = $wpq-&gt;getpage('User:'.$user.'/AngryOptin'); }
if (strtolower($change) == strtolower($owner.'/CBAutostalk.js')) {
unset($stalk);
$tmp = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBAutostalk.js'));
foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $stalk] = trim($tmp3); } }
unset($tmp,$tmp2,$tmp3);
print_r($stalk);
}
if (strtolower($change) == strtolower($owner.'/CBAutoedit.js')) {
unset($edit);
$tmp = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBAutoedit.js'));
foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $edit] = trim($tmp3); } }
unset($tmp,$tmp2,$tmp3);
print_r($edit);
}
if (strtolower($change) == strtolower($owner.'/CBChannels.js')) {
$ircconfig = explode(&quot;\n&quot;,$wpq-&gt;getpage('User:'.$owner.'/CBChannels.js'));
$tmp = array();
foreach($ircconfig as $tmpline) { if (substr($tmpline,0,1) != '#') { $tmpline = explode('=',$tmpline,2); $tmp)] = trim($tmpline); } }
print_r($tmp);

$tmpold = array();
$tmpnew = array();
foreach ($tmp as $tmp2) foreach (explode(',',$tmp2) as $tmp3) $tmpnew = 1;
foreach (explode(',',$ircchannel.','.$ircdebugchannel.','.$ircreportchannel.','.$ircvandalismchannel.','.$ircaivchannel.','.$irctechchannel.','.$ircproxychannels.','.$ircunrevertedchannels.','.$ircbagtrialchannels.','.$ircotherchannels) as $tmp3) $tmpold = 1;

foreach ($tmpold as $tmp2 =&gt; $tmp3) if (isset($tmpnew)) unset($tmpold,$tmpnew);
foreach ($tmpnew as $tmp2 =&gt; $tmp3) $tmpnew1 = $tmp2;
foreach ($tmpold as $tmp2 =&gt; $tmp3) $tmpold1 = $tmp2;

$tmpold = $tmpold1; $tmpnew = $tmpnew1; unset($tmpold1,$tmpnew1);

fwrite($irc,'JOIN '.implode(',',$tmpnew).&quot;\n&quot;);
fwrite($irc,'PART '.implode(',',$tmpold).&quot;\n&quot;);

$ircchannel = $tmp;
$ircdebugchannel = $tmp;
$ircreportchannel = $tmp;
$ircvandalismchannel = $tmp;
$ircaivchannel = $tmp;
$irctechchannel = $tmp;
$ircproxychannels = $tmp;
$ircunrevertedchannels = $tmp;
$ircbagtrialchannels = $tmp;
$ircotherchannels = $tmp;

unset($tmp,$tmpline,$tmpold,$tmpnew,$tmp2,$tmp3);
}
}
if ($change.$change == 'Misplaced Pages:Bots/Requests for approval') {
$stalkbots = array();
$trialbots = explode(&quot;\n&quot;,$wpq-&gt;getpage('Misplaced Pages:Bots/Requests for approval'));
foreach ($trialbots as $trialbot) if (preg_match('/\{\{BRFA\|(.*)\|.*\|Trial\}\}/',str_replace(array(&quot;\n&quot;,&quot;\r&quot;),'',$trialbot),$m)) $stalkbots] = 1;
}

if (($change != '') and ((!preg_match('/\* \.$change,'/').')\]\] \- .*/i',$optin))) and ($change != 'move') and ($change != 'Template:')) continue;

$change = $change;
$change = $change.$change;

if ($change == 'move') {
if (preg_match('/moved \\] to \\]( over redirect)?: (.*)$/',$change,$m)) {
$change = $m;
$change = $m;
$change = $change;
$change = $m;
echo &quot;\n\n\n&quot;.'Move!'.&quot;\n\n\n&quot;;
print_r($change);
echo &quot;\n\n\n&quot;.'Move!'.&quot;\n\n\n&quot;;
}
}

if (
((time() - $tfas) &gt;= 1800)
and (preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq-&gt;getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfam))
) {
$tfas = time();
$tfa = $tfam;
//echo &quot;TFA: &quot;.$tfa.&quot;\n&quot;;
}
$s = null;
$pid = @pcntl_fork();
if ($pid != 0) continue;

$hutime = microtime(1);

include 'cluebot.heuristics.config.php';
foreach ($heuristics as $heuristic) {
$heuristicret = false;
include 'heuristics/cluebot.'.$heuristic.'.heuristic.php';
if ($heuristicret == true) {
$stats = unserialize(file_get_contents('cluebot.heuristics.stats.txt'));
$stats++;
print_r($log);
file_put_contents('cluebot.heuristics.stats.txt',serialize($stats));
unset($stats);
break;
}
}

if ($heuristicret == true) { echo 'Heuristics time: '.(microtime(1) - $hutime).&quot;\n&quot;; /*file_put_contents('trainingdata.txt',$change.&quot;\0&quot;.$change.&quot;\0&quot;.'1'.&quot;\n&quot;,FILE_APPEND);*/ }
else {
$tmp = explode(' ',$rawline,4); $tmp = $tmp;
$udp = fsockopen('udp://localhost',3333);
fwrite($udp,substr(str_replace(array(&quot;\n&quot;,&quot;\r&quot;),'',$tmp),1).&quot;\n&quot;);
fclose($udp);
unset($tmp,$udp);
$d = $wpi-&gt;diff($change,$change,$change);
$s = score($obscenelist,$d,$log);
$s -= score($obscenelist,$d,$log);
// if ($s &gt; 15) file_put_contents('trainingdata.txt',$change.&quot;\0&quot;.$change.&quot;\0&quot;.'0'.&quot;\n&quot;,FILE_APPEND);
}
unset($hutime);

if (
($heuristicret == true)
) {
if (
(
( /* IP users with 250 contributions are fine .. */
(long2ip(ip2long($change)) == $change)
/* and ($uc = $wpapi-&gt;usercontribs($change,250))
and (!isset($uc)) */
)
or ( /* Users with 50 contributions are fine .. */
(long2ip(ip2long($change)) != $change)
and ($wpq-&gt;contribcount($change) &lt; 50)
)
)
and ( /* Whitelisted users are ok. */
/* ($wl = $wpq-&gt;getpage('User:'.$user.'/Whitelist'))
and */ (!preg_match('/^\* \,'/').')|\1\]\] \- .*/',$wl))
)
) {
// $vandalpage = $wpq-&gt;getpage('User:'.$user.'/PossibleVandalism');
// $x = explode(&quot;\n\n&quot;,$vandalpage);
// foreach ($x as $k =&gt; $y) {
// if (preg_match('/(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+)/',$y,$m)) {
// if ((time() - gmmktime($m,$m,$m,$m,$m,$m)) &gt; (5*60*60)) {
// unset($x);
// }
// }
// }
// $vandalpage = implode(&quot;\n\n&quot;,$x);
$diff = 'http://en.wikipedia.org/search/' .
'?title='.urlencode($change) .
'&amp;diff='.urlencode($change) .
'&amp;oldid='.urlencode($change);

$report = ').']] was ' .
(($change != 'move')?' by ':'moved to ).']] by ') .
'.'|'.$change.']] ' .
'.'|(u)]] ' .
'.'|(t)]] ' .
$reason.' on '.gmdate('c');
// $datatopost = $vandalpage.&quot;\n\n&quot;.'Possible ]: '.$report.&quot; ~~~~\n&quot;;


if ($s == null) {
// $rv = $wpapi-&gt;revisions($change,2,'older',true,$change);
// $s = score($scorelist,diff($rv,$rv));
// $s += (score($scorelist,diff($rv,$rv,false))) * -1;
$s = 'N/A';
}

$tmp = unserialize(file_get_contents('oftenvandalized.txt'));
if (rand(1,50) == 2) {
foreach ($tmp as $key1 =&gt; $tmp2) {
foreach ($tmp2 as $key2 =&gt; $time) {
if ((time() - $time) &gt; (2*24*60*60)) { unset($tmp); }
}
}
}
$tmp] = time();
if (count($tmp]) &gt;= 30) {
foreach (explode(',',$ircreportchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :!admin .']] has been vandalized '.(count($tmp])).' times in the last 2 days.'.&quot;\n&quot;);
}
}
file_put_contents('oftenvandalized.txt',serialize($tmp));

if (
(
($rv1 = $wpapi-&gt;revisions($change,1,'older'))
and ($rv1 == $change)
)
or ($change == 'move')
) { /* No need to continue further if it has been reverted */
echo 'Possible vandalism: '.$change.' changed by '.$change.' '.$reason.' on '.$rv.'('.$s.&quot;).\n&quot;;
foreach (explode(',',$ircdebugchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :Possible vandalism: '.$change.' changed by '.$change.' '.$reason.' on '.$rv.'('.$s.&quot;).\n&quot;);
fwrite($irc,'PRIVMSG '.$y.' :( http://en.wikipedia.org/search/?title='.urlencode($change).'&amp;action=history | '.$change.' )'.&quot;\n&quot;);
}
fwrite($pipe,'http://en.wikipedia.org/search/?title='.urlencode($change).'&amp;action=history'.&quot;\n&quot;); /* Tell owner */

$mqtime = microtime(1);

if (is_array($log)) {
$logt = '';
foreach ($log as $k =&gt; $v) {
$logt .= '* '.$v.' * &quot;'.$k.'&quot;'.&quot;\n&quot;;
}
}

$query = 'INSERT INTO `vandalism` ' .
'(`id`,`user`,`article`,`heuristic`'.((is_array($log))?',`regex`':'').',`reason`,`diff`,`old_id`,`new_id`,`reverted`) ' .
'VALUES ' .
'(NULL,\''.mysql_real_escape_string($change).'\',' .
'\''.mysql_real_escape_string($change).'\',' .
'\''.mysql_real_escape_string($heuristic).'\',' .
((is_array($log))?'\''.mysql_real_escape_string($logt).'\',':'') .
'\''.mysql_real_escape_string($reason).'\',' .
'\''.mysql_real_escape_string($change).'\',' .
'\''.mysql_real_escape_string($change).'\',' .
'\''.mysql_real_escape_string($change).'\',0)';
//echo 'Mysql query: '.$query.&quot;\n&quot;;
if (!mysql_ping($mysql)) {
$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
if (!$mysql) { die('Could not connect: ' . mysql_error()); }
if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }
}
mysql_query($query);
//echo 'Mysql error: '.mysql_error().&quot;\n&quot;;
$mysqlid = mysql_insert_id();
echo 'MySQL time: '.(microtime(1) - $mqtime).' MySQL id: '.$mysqlid.&quot;\n&quot;;
unset($mqtime);

if (
(
(
(preg_match('/(assisted|manual)/iS',$status))
and (print('Revert ? '))
and (strtolower(substr(fgets($stdin,3),0,1)) == 'y')
)
or (
(preg_match('/(read-write|rw|go|approved|trial)/iS',$status))
)
)
and ( /*ANGRY MODE*/ false or
(
(
((time() - $tfas) &lt; 1800)
or (
(preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq-&gt;getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfam))
and ($tfas = time())
and ($tfa = $tfam)
and ((print(&quot;TFA: &quot;.$tfa.&quot;\n&quot;)) or (true))
)
)
and ($tfa == $change)
)
or (
(preg_match('/\* \,'/').')\]\] \- .*/i',$aoptin))
and ((fwrite($irc,'PRIVMSG '.$ircdebugchannel.' :Angry-reverting .']].'.&quot;\n&quot;)) or (true))
)
or (
(($tmp = unserialize(file_get_contents('titles.txt'))) or true)
and ((!isset($tmp.$change])) or ((time() - $tmp.$change]) &gt; (24*60*60)))
and ($tmp.$change] = time())
and ((file_put_contents('titles.txt',serialize($tmp))) or true)
)
)
) {
echo 'Reverting ...'.&quot;\n&quot;;
if ($change != 'move') {
$rev = $wpapi-&gt;revisions($change,5,'older',false,null,true,true);
$revid = 0;
$rbtok = $rev;
foreach ($rev as $revdata) {
if ($revdata != $change) {
$revid = $revdata;
break;
}
}
if (($revdata == $user) or (in_array($revdata,explode(',',$botfriends)))) { die(); /* Do not revert to us. */ }
}
// if ($revid == 0) { die(); }
foreach (explode(',',$ircdebugchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :Reverting ...'.&quot;\n&quot;);
}
// $revisiondata = $wpapi-&gt;revisions($change,1,'older',true,$revid);
// if (!$revisiondata) die();
// if (!$rv1) $rv1 = $rv;
// $wpi-&gt;post(
// $change,
// $revisiondata,
// 'Reverting possible vandalism by .'|'.$change.']] ' .
// 'to version by '.$revisiondata.'. ' .
// 'False positive? ]. '.
// 'Thanks, ]. ('.$mysqlid.') (Bot)',
// false,
// $rv1
// ); /* Revert the page */
if ($change != 'move') {
if (!$rbtok) {
$d = $wpi-&gt;diff($change,$change,$change);
$rbtok = $d;
}
$rbret = $wpapi-&gt;rollback(
$change,
$change,
'Reverting possible vandalism by .'|'.$change.']] ' .
'to '.(($revid == 0)?'older version':'version by '.$revdata).'. ' .
'False positive? ]. '.
'Thanks, ]. ('.$mysqlid.') (Bot)',
$rbtok
);
} else {
$rbret = $wpapi-&gt;move(
$change,
$change,
'Reverting possible vandalism by .'|'.$change.']] ' .
'to '.(($revid == 0)?'older version':'version by '.$revdata).'. ' .
'False positive? ]. '.
'Thanks, ]. ('.$mysqlid.') (Bot)'
);
}
//
// $rv2 = $wpapi-&gt;revisions($change,1);
// if ($rv2 == $user) {
if ($rbret !== false) {
foreach (explode(',',$ircdebugchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :Reverted. ('.(microtime(1) - $messagereceived).' s)'.&quot;\n&quot;);
}
$warning = 0;
$tpcontent = $wpq-&gt;getpage('User talk:'.$change);
if (preg_match_all('/&lt;!-- Template:(uw-*(\d)(im)?|Blatantvandal \(serious warning\)) --&gt;.*(\d{2}):(\d{2}), (\d+) (+) (\d{4}) \(UTC\)/iU',
$tpcontent,
$match,PREG_SET_ORDER)
) {
foreach ($match as $m) {
$month = array('January' =&gt; 1, 'February' =&gt; 2, 'March' =&gt; 3,
'April' =&gt; 4, 'May' =&gt; 5, 'June' =&gt; 6, 'July' =&gt; 7,
'August' =&gt; 8, 'September' =&gt; 9, 'October' =&gt; 10,
'November' =&gt; 11, 'December' =&gt; 12
);
if ($m == 'Blatantvandal (serious warning)') $m = 4;
if ((time() - gmmktime($m,$m,0,$month],$m,$m)) &lt;= (2*24*60*60)) {
if ($m &gt; $warning) { $warning = $m; }
}
}
}
$warning++;
if ($warning == 5) { /* Report them if they have been warned 4 times. */
$aivdata = $wpq-&gt;getpage('Misplaced Pages:Administrator_intervention_against_vandalism/TB2');
if (!preg_match('/'.preg_quote($change,'/').'/i',$aivdata)) {
foreach(explode(',',$ircaivchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :!admin Reporting .']] to ]. Contributions: .']] Block: .']]'.&quot;\n&quot;);
}
foreach (explode(',',$ircvandalismchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :rcbot bl add '.$change.' x='.(24*$warning).' r=Vandalism to .']] (#'.$warning.&quot;).\n&quot;);
}
$wpapi-&gt;edit(
'Misplaced Pages:Administrator_intervention_against_vandalism/TB2',
$aivdata .
&quot;\n\n* {{&quot;.((long2ip(ip2long($change)) == $change)?'IPvandal':'Vandal').'|'.$change.'}}' .
' - '.$report.&quot; (Automated) ~~~~\n&quot;,
'Automatically reporting .']]. (bot)',
false,
false
);
} else {
foreach (explode(',',$ircreportchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :!admin .']] has vandalized at least one time while being listed on ]. Contributions: .']] Block: .']]'.&quot;\n&quot;);
}
}
} elseif ($warning &lt; 5) { /* Warn them if they haven't been warned 4 times. */
foreach (explode(',',$ircvandalismchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :rcbot bl add '.$change.' x='.(24*$warning).' r=Vandalism to .']] (#'.$warning.').'.&quot;\n&quot;);
}
$wpapi-&gt;edit(
'User talk:'.$change,
$tpcontent.&quot;\n\n&quot; .
'{{subst:User:'.$user.'/Warnings/Warning|1='.$warning.'|2='.str_replace('File:',':File:',$change).'|3='.$report.' &lt;!{{subst:ns:0}}-- MySQL ID: '.$mysqlid.' --{{subst:ns:0}}&gt;}} ~~~~'.&quot;\n&quot;,
'Warning .'|'.$change.']] - #'.$warning,
false,
false
); /* Warn the user */
} else { /* They have already been reported ... do nothing */

}
if (!mysql_ping($mysql)) {
$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
if (!$mysql) { die('Could not connect: ' . mysql_error()); }
if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }
}
mysql_query('UPDATE `vandalism` SET `reverted` = 1 WHERE `id` = \''.mysql_real_escape_string($mysqlid).'\'');
} else {
$rv2 = $wpapi-&gt;revisions($change,1);
if ($change != $rv2) {
echo 'Grr! Beaten by '.$rv2.&quot;.\n&quot;;
foreach(explode(',',$ircdebugchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :Grr! Beaten by '.$rv2.&quot;.\n&quot;);
}
if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
mysql_query('INSERT INTO `beaten` (`id`,`article`,`diff`,`user`) VALUES (NULL,\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($rv2).'\')');
}
}
} else {
foreach (explode(',',$ircunrevertedchannels) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :'.&quot;\002\00304Possible ignored vandalism: \002\003.&quot;\003]]\00304 changed by \003.&quot;\003]] \00303&quot;.$reason.&quot;\00304 on \00307&quot;.$rv.&quot;\003(\002\00313&quot;.$s.&quot;\003).\n&quot;);
fwrite($irc,'PRIVMSG '.$y.' :'.&quot;\002(\002\00312 http://en.wikipedia.org/search/?title=&quot;.urlencode($change).&quot;&amp;action=history \003\002|\002\00312 &quot;.$change.&quot; \003\002)\002&quot;.&quot;\n&quot;);
}
$vandalpage = $wpq-&gt;getpage('User:'.$user.'/PossibleVandalism');
$x = explode(&quot;\n\n&quot;,$vandalpage);
foreach ($x as $k =&gt; $y) {
if (preg_match('(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z',$y,$m)) {
if ((gmtime() - gmmktime($m,$m,$m,$m,$m,$m)) &gt; (5*60*60)) {
unset($x);
}
}
}
$vandalpage = implode(&quot;\n\n&quot;,$vandalpage);
$wpapi-&gt;edit('User:'.$user.'/PossibleVandalism',$vandalpage.&quot;\n\n&quot;.'Possible ignored vandalism: .']] changed by .']] '.$reason.' on '.$rv.' ('.$s.')'.' ).'&amp;action=history hist] .' diff] ~~~~','Adding possible vandalism',true,true);
}

} else {
$rev = $wpapi-&gt;revisions($change,1);
$rev = $rev;

echo 'Possible corrected vandalism: '.$change.' changed by '.$change.' '.$reason.'('.$s.&quot;)\n\tReverted by &quot;.$rev.&quot; before I saw it.\n&quot;;
foreach (explode(',',$ircdebugchannel) as $y) {
fwrite($irc,'PRIVMSG '.$y.' :Possible corrected vandalism: '.$change.' changed by '.$change.' '.$reason.'('.$s.&quot;)\n&quot;);
fwrite($irc,'PRIVMSG '.$y.' :Reverted by '.$rev.&quot; before I saw it.\n&quot;);
}
if (($rev != $user) and ($rev != $change)) {
if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
mysql_query('INSERT INTO `beaten` (`id`,`article`,`diff`,`user`) VALUES (NULL,\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($rev).'\')');
}
}
}
}
die();
}
} }
$lasttime = $change;
} }
sleep(30);
} }
?&gt;
</pre>



==Heuristics==

===Config (cluebot.heuristics.config.php)===

<pre>&lt;?PHP
$heuristics = 'grawp';
$heuristics = 'evolution';
$heuristics = 'avrillavigne';
$heuristics = 'editsummary';
$heuristics = 'pagereplace';
$heuristics = 'pageblank';
$heuristics = 'massdelete';
$heuristics = 'massadd';
$heuristics = 'tables';
$heuristics = 'smallchange';
$heuristics = 'claimjumperpete';
$heuristics = 'sneaky';
$heuristics = 'redirect';
?&gt;
</pre>

===heuristics/cluebot.avrillavigne.heuristic.php===

<pre>&lt;?PHP
if ( /* The Avril Lavigne vandal */
(
(preg_match('/avril\s*lavigne\s*roc*k**\s*my\s*soc*k**/i',$change))
or (preg_match('/she\s*(is\s*)*(so\s*)*.*hot\!.*using.*TW/i',$change))
// or (
// ($change == 'Template:')
// and (preg_match('/\{\{user((\s|_)talk)?:/i',$wpq-&gt;getpage($change)))
// )
)
and ($reason = 'Avril Lavigne vandal?')
) {
$heuristicret = true;
foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Avril Lavigne vandal? .&quot;]] .\n&quot;);
}
?&gt;
</pre>

===heuristics/cluebot.claimjumperpete.heuristic.php===

<pre>&lt;?PHP
if ( /* The ClaimJumperPete vandal */
(($change &gt;= 100) and ($change &lt;= 400))
and (
($change &lt;= 200)
or ($d = $wpi-&gt;diff($change,$change,$change))
)
and (
(fnmatch(&quot;&lt;!--*howdy y'all*--&gt;*&quot;,trim(strtolower($d))))
or (fnmatch(&quot;&lt;!--*hello der*--&gt;*&quot;,trim(strtolower($d))))
or (fnmatch(&quot;&lt;!--*happy editin' y'all*--&gt;*&quot;,trim(strtolower($d))))
)
and ($reason = 'ClaimJumperPete?')
) {
$heuristicret = true;
foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin ClaimJumperPete vandal? http://en.wikipedia.org/search/?title='.urlencode($change).'&amp;diff=prev'.'&amp;oldid='.urlencode($change).&quot; .\n&quot;);
}
?&gt;
</pre>

===heuristics/cluebot.editsummary.heuristic.php===

<pre>&lt;?PHP
if (
(
(fnmatch('*nimp*org*',strtolower($change)))
)
and ($reason = 'obscenities in edit summary')
) {
$heuristicret = true;
}
?&gt;
</pre>

===heuristics/cluebot.evolution.heuristic.php===

<pre>&lt;?PHP
if ( /* The Evolution vandal */
($change == 'Evolution')
and (($pagedata = $wpq-&gt;getpage($change)) or true)
and (fnmatch('*Genesis 1*The beginning*',$pagedata))
and ($reason = 'replacing article with the Bible')
) {
$heuristicret = true;
foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Evolution vandal? http://en.wikipedia.org/search/?title='.urlencode($change).'&amp;diff=prev'.'&amp;oldid='.urlencode($change).&quot; .\n&quot;);
}
?&gt;
</pre>

===heuristics/cluebot.grawp.heuristic.php===

<pre>&lt;?PHP
if ( /* The Grawp vandal */
(
(fnmatch('*epic*lulz*on*nimp*org*',strtolower($change)))
or (fnmatch('*on*nimp*org*epic*lulz*',strtolower($change)))
or (fnmatch('*punishing*wikipedia*',strtolower($change)))
or (fnmatch('*anti*avril*hate*campaign*',strtolower($change)))
or (fnmatch('*HAGGER*',$change))
or (fnmatch('*H?A?G?G?E?R*',$change))
or (fnmatch('*h??a??g??g??e??r*',strtolower($change)))
or (fnmatch('*grawp*cock*',strtolower($change)))
or (fnmatch('*massive*cock*',strtolower($change)))
or (fnmatch('*grawp*dick*',strtolower($change)))
or (fnmatch('*massive*dick*',strtolower($change)))
or (fnmatch('*H?A?G?E?R*',$change))
or (fnmatch('*hgger*',strtolower($change)))
)
and ($reason = 'Grawp?')
) {
$heuristicret = true;
foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Grawp vandal? .&quot;]] .\n&quot;);
}
?&gt;
</pre>

===heuristics/cluebot.massadd.heuristic.php===

<pre>&lt;?PHP
if ( /* Massive additions */
($change &gt;= 7500)
and ($rv = $wpapi-&gt;revisions($change,2,'older',true,$change))
and ($pagedata = $wpq-&gt;getpage($change))
and ($s = score($scorelist,$rv))
and ($s += (score($scorelist,$rv)) * -1)
and ($s &lt; -1000)
and ($reason = 'score equals '.$s)
) $heuristicret = true;
?&gt;
</pre>

===heuristics/cluebot.massdelete.heuristic.php===

<pre>&lt;?PHP
if ( /* Massive deletes */
($change &lt;= -7500)
and ($pagedata = $wpq-&gt;getpage($change))
and (!fnmatch('*#REDIRECT*',strtoupper(substr($pagedata,0,9))))
and ($rv = $wpapi-&gt;revisions($change,2,'older',true,$change))
and ($s = score($scorelist,$rv))
and ($s += (score($scorelist,$rv)) * -1)
and ($s &lt; -50) /* There are times when massive deletes are ok. */
and ($reason = 'deleting '.($change * -1).' characters')
) $heuristicret = true;
?&gt;
</pre>

===heuristics/cluebot.pageblank.heuristic.php===

<pre>&lt;?PHP
if ( /* Page blanks */
(preg_match('/\,$m))
and (($pagedata = $wpq-&gt;getpage($change)) or true)
and ($fc = $wpapi-&gt;revisions($change,1,'newer'))
and ($fc != $change) /* The creator is allowed to blank the page. */
and ($reason = 'blanking the page')
) $heuristicret = true;
?&gt;
</pre>

===heuristics/cluebot.pagereplace.heuristic.php===

<pre>&lt;?PHP
if ( /* Page replaces */
(preg_match('/\\]Replaced page with (.*)$/',$change,$m))
and ($pagedata = $wpq-&gt;getpage($change))
and ($fc = $wpapi-&gt;revisions($change,1,'newer'))
and ($fc != $change) /* The creator is allowed to replace the page. */
and ($reason = 'replacing entire content with something else')
) $heuristicret = true;
?&gt;
</pre>

===heuristics/cluebot.redirect.heuristic.php===

<pre>&lt;?PHP
if ( /* The Redirect vandals */
(
($tfa == $change)
and (fnmatch('*#redirect *',strtolower($wpq-&gt;getpage($change))))
and ($reason = 'redirecting featured article to new title')
)
or (
($pagedata = $wpq-&gt;getpage($change))
and (substr(trim(strtolower($pagedata)),0,10) == '#redirect ')
and (preg_match('/\\]/',$pagedata,$m))
and (!$wpq-&gt;getpage($m))
and ($reason = 'redirecting article to non-existant page')
)
) {
$heuristicret = true;
// fwrite($irc,'PRIVMSG #cvn-wp-en :!admin Grawp vandal? http://en.wikipedia.org/Special:Contributions/'.$change.&quot; .\n&quot;);
}
?&gt;
</pre>

===heuristics/cluebot.smallchange.heuristic.php===

<pre>&lt;?PHP
unset($log,$log2);
if ( /* Small changes with obscenities. */
(($change &gt;= -200) and ($change &lt;= 200))
and (($d = $wpi-&gt;diff($change,$change,$change)) or true)
and ((($change == 'User:ClueBot/Sandbox') and print_r($rv)) or true)
and (($s = score($obscenelist,$d,$log)) or true)
and (($s -= score($obscenelist,$d,$log2)) or true)
and (
(
($s &lt; -5) /* There are times when small changes are ok. */
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and (!fnmatch('*SEX*',strtoupper($rv)))
and (!fnmatch('*BDSM*',strtoupper($rv)))
and (score($obscenelist,$change) &gt;= 0)
and (score($obscenelist,$rv) &gt;= 0)
and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and ($heuristic .= '/obscenities')
and ($reason = 'making a minor change with obscenities')
)
or (
($s &gt; 5)
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and (preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and ($heuristic .= '/censor')
and ($reason = 'making a minor change censoring content (])')
)
or (
(preg_match('/\!\!\!/S',$d))
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!preg_match('/\!\!\!/S',$rv))
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and ($heuristic .= '/exclamation')
and ($reason = 'making a minor change adding &quot;!!!&quot;')
)
)
) { $heuristicret = true; if (isset($log2) and is_array($log2)) foreach ($log2 as $k =&gt; $v) $log -= $v; if (isset($log) and is_array($log)) foreach ($log as $k =&gt; $v) if ($v == 0) unset($log); unset($log2); /* fwrite($irc,'PRIVMSG #wikipedia-BAG/ClueBot :Would revert http://en.wikipedia.org/search/?title='.urlencode($change.$change).'&amp;diff=prev'.'&amp;oldid='.urlencode($change).&quot; .\n&quot;); */ }
?&gt;
</pre>

===heuristics/cluebot.sneaky.heuristic.php===

<pre>&lt;?PHP
unset($log,$log2);
if ( /* Small changes with obscenities. */
(($change &gt;= -200) and ($change &lt;= 200))
and (($d = $wpi-&gt;diff($change,$change,$change)) or true)
and ((($change == 'User:ClueBot/Sandbox') and print_r($rv)) or true)
and (($s = score($obscenelist,$d,$log)) or true)
and (($s -= score($obscenelist,$d,$log2)) or true)
and (
(
($s &lt; -5) /* There are times when small changes are ok. */
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and (!fnmatch('*SEX*',strtoupper($rv)))
and (!fnmatch('*BDSM*',strtoupper($rv)))
and (score($obscenelist,$change) &gt;= 0)
and (score($obscenelist,$rv) &gt;= 0)
and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and ($heuristic .= '/obscenities')
and ($reason = 'making a minor change with obscenities')
)
or (
($s &gt; 5)
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and (preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\&lt;|\()?censored(\&gt;|\))?)(ing?|ed)?(\s|$)/iS',$rv))
and ($heuristic .= '/censor')
and ($reason = 'making a minor change censoring content (])')
)
or (
(preg_match('/\!\!\!/S',$d))
and (($rv = $wpapi-&gt;revisions($change,2,'older',true,$change)) or true)
and (!preg_match('/\!\!\!/S',$rv))
and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
and ($heuristic .= '/exclamation')
and ($reason = 'making a minor change adding &quot;!!!&quot;')
)
)
) { $heuristicret = true; if (isset($log2) and is_array($log2)) foreach ($log2 as $k =&gt; $v) $log -= $v; if (isset($log) and is_array($log)) foreach ($log as $k =&gt; $v) if ($v == 0) unset($log); unset($log2); /* fwrite($irc,'PRIVMSG #wikipedia-BAG/ClueBot :Would revert http://en.wikipedia.org/search/?title='.urlencode($change.$change).'&amp;diff=prev'.'&amp;oldid='.urlencode($change).&quot; .\n&quot;); */ }
?&gt;
</pre>

===heuristics/cluebot.tables.heuristic.php===

<pre>&lt;?PHP
if ( /* Massive tables */
($change &gt;= 7500)
and ($rv = $wpapi-&gt;revisions($change,2,'older',true,$change))
and (substr_count(strtolower($rv),'&lt;td') &gt; 300)
and ($reason = 'adding huge, browser-crashing tables')
) $heuristicret = true;
?&gt;
</pre>

==Score list==


<pre>&lt;?PHP
?>
/*
</source>
* This page contains bad words out of necessity.
* Here is 50 lines of whitespace before the actual list:
* (scroll down to see the list)
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* Here is the list:
*/
$obscenelist = Array
(
/* 'preg' =&gt; points, */
'/suck/i' =&gt; -5, /* Usually bad words */
'/stupid/i' =&gt; -3,
'/haha/i' =&gt; -5,
'/\bomg/i' =&gt; -3,
'/\bpimp\b/i' =&gt; -7,
'/1337/i' =&gt; -5,
'/leet/i' =&gt; -5,
'/dumb/i' =&gt; -5,
'/\bputa\b/i' =&gt; -7,
'/\bhomo\b/i' =&gt; -7,
'/\bGAY\b/' =&gt; -10,
'/\bslut/i' =&gt; -5,
'/damn/i' =&gt; -5,
'/\bass\b/i' =&gt; -10,
'/\brape\b/i' =&gt; -7,
'/\bpoop\b/i' =&gt; -10,
'/\bcock\b/i' =&gt; -10,
'/\blol\b/i' =&gt; -7,
'/\bcrap\b/i' =&gt; -5,
'/\bsex\b/i' =&gt; -5,
'/noob/i' =&gt; -5,
'/\bnazi\b/i' =&gt; -3,
'/\bneo-nazi\b/i' =&gt; +3, /* False-positive */
'/fuck/i' =&gt; -20, /* Stronger bad words */
'/\\]/' =&gt; +20, /* This one is a false positive */
'/bitch/i' =&gt; -20,
'/\bpussy\b/i' =&gt; -20,
'/penis/i' =&gt; -20,
'/Penisula/' =&gt; +20, /* False Positive */
'/vagina/i' =&gt; -20,
'/whore/i' =&gt; -15,
'/\bshit\b/i' =&gt; -20,
'/nigger/i' =&gt; -20,
'/\bnigga\b/i' =&gt; -20,
'/cocksucker/i' =&gt; -20,
'/assrape/i' =&gt; -15,
'/motherfucker/i' =&gt; -20,
'/wanker/i' =&gt; -20,
'/\bcunt\b/i' =&gt; -20,
'/faggot/i' =&gt; -20,
'/fags/i' =&gt; -20,
'/asshole/i' =&gt; -15,
'/fuck ((yo)?u|h(er|im)|them|it)/i' =&gt; -100, /* This looks like a personal attack */
'/((yo)?u|s?he|we|they|it) sucks?/i' =&gt; -100, /* This looks like a personal attack */
'/666+\b/i' =&gt; -50 /* Though this has uses, it is commonly used by vandals */
);
$grammarlist = Array
(
'/(.{1,4})\1{30}/' =&gt; -10, /* Ugg .. the same letter(s) several times in a row. */
'/\b.*\b/U' =&gt; +2, /* This looks to be a correct sentence */
'/\b{30,}\b/U' =&gt; -10, /* All capitals? Looks like vandal activity */
'/\b{1500,}\b/U' =&gt; -10, /* No capitals? Looks like vandal activity */
'/!{5,}/i' =&gt; -10, /* No wikipedia article needs '!!!!!' in it */
'/!!+1+(one)*/i' =&gt; -30, /* No wikipedia article needs '!!!11one' in it */
'/\\]/U' =&gt; +1, /* Wiki links are good. */
'/\{\{.*\}\}/U' =&gt; +5, /* Wiki transcludes are good. */
'/\{\{nfobox .*\}\}/U' =&gt; +20, /* Wiki infoboxes are good. */
'/\\]/iU' =&gt; +3 /* Wiki categories are good. */
);
$scorelist = array_merge($obscenelist,$grammarlist);
?&gt;
</pre>






] 07:23, 24 July 2007 (UTC) ] (]) 13:01, 27 November 2010 (UTC)

Latest revision as of 13:01, 27 November 2010

The following is automatically generated by ClueBot.


Classes (wikibot.classes.php)

<?PHP
	/**
	 * @author Cobi Carter
	 **/
	/**
	 * This class is designed to provide a simplified interface to cURL which maintains cookies.
	 * @author Cobi
	 **/
	class http {
		private $ch;
		private $uid;
		public $postfollowredirs;
		public $getfollowredirs;
		/**
		 * Our constructor function.  This just does basic cURL initialization.
		 * @return void
		 **/
		function __construct () {
			global $proxyhost, $proxyport, $proxytype;
			$this->ch = curl_init();
			$this->uid = dechex(rand(0,99999999));
			curl_setopt($this->ch,CURLOPT_COOKIEJAR,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
			curl_setopt($this->ch,CURLOPT_COOKIEFILE,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
			curl_setopt($this->ch,CURLOPT_MAXCONNECTS,100);
			curl_setopt($this->ch,CURLOPT_CLOSEPOLICY,CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
			curl_setopt($this->ch,CURLOPT_USERAGENT,'ClueBot/1.1');
			if (isset($proxyhost) and isset($proxyport) and ($proxyport != null) and ($proxyhost != null)) {
				curl_setopt($this->ch,CURLOPT_PROXYTYPE,isset( $proxytype ) ? $proxytype : CURLPROXY_HTTP);
				curl_setopt($this->ch,CURLOPT_PROXY,$proxyhost);
				curl_setopt($this->ch,CURLOPT_PROXYPORT,$proxyport);
			}
			$this->postfollowredirs = 0;
			$this->getfollowredirs = 1;
		}
		/**
		 * Post to a URL.
		 * @param $url The URL to post to.
		 * @param $data The post-data to post, should be an array of key => value pairs.
		 * @return Data retrieved from the POST request.
		 **/
		function post ($url,$data) {
			$time = microtime(1);
			curl_setopt($this->ch,CURLOPT_URL,$url);
			curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,$this->postfollowredirs);
			curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
			curl_setopt($this->ch,CURLOPT_HEADER,0);
			curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
			curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
			curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
			curl_setopt($this->ch,CURLOPT_POST,1);
			curl_setopt($this->ch,CURLOPT_POSTFIELDS, $data);
			curl_setopt($this->ch,CURLOPT_HTTPHEADER, array('Expect:'));
			$data = curl_exec($this->ch);
			global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'POST: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n");
			return $data;
		}
		/**
		 * Get a URL.
		 * @param $url The URL to get.
		 * @return Data retrieved from the GET request.
		 **/
		function get ($url) {
			$time = microtime(1);
			curl_setopt($this->ch,CURLOPT_URL,$url);
			curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,$this->getfollowredirs);
			curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
			curl_setopt($this->ch,CURLOPT_HEADER,0);
			curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
			curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
			curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
			curl_setopt($this->ch,CURLOPT_HTTPGET,1);
			$data = curl_exec($this->ch);
			global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'GET: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n");
			return $data;
		}
		/**
		 * Our destructor.  Cleans up cURL and unlinks temporary files.
		 **/
		function __destruct () {
			curl_close($this->ch);
			@unlink('/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
		}
	}
	/**
	 * This class is a deprecated wrapper class which allows legacy code written for Misplaced Pages's query.php API to still work with wikipediaapi::.
	 **/
	class wikipediaquery {
		private $http;
		private $api;
		public $queryurl = 'http://en.wikipedia.org/w/query.php'; //Obsolete, but kept for compatibility purposes.
		/**
		 * This is our constructor.
		 * @return void
		 **/
		function __construct () {
			global $__wp__http;
			if (!isset($__wp__http)) {
				$__wp__http = new http;
			}
			$this->http = &$__wp__http;
			$this->api = new wikipediaapi;
		}
		/**
		 * Reinitializes the queryurl.
		 * @private
		 * @return void
		 **/
		private function checkurl() {
			$this->api->apiurl = str_replace('query.php','api.php',$this->queryurl);
		}
		/**
		 * Gets the content of a page.
		 * @param $page The wikipedia page to fetch.
		 * @return The wikitext for the page.
		 **/
		function getpage ($page) {
			$this->checkurl();
			$ret = $this->api->revisions($page,1,'older',true,null,true,false,false,false);
			return $ret;
		}
		/**
		 * Gets the page id for a page.
		 * @param $page The wikipedia page to get the id for.
		 * @return The page id of the page.
		 **/
		function getpageid ($page) {
			$this->checkurl();
			$ret = $this->api->revisions($page,1,'older',false,null,true,false,false,false);
			return $ret;
		}
		/**
		 * Gets the number of contributions a user has.
		 * @param $user The username for which to get the edit count.
		 * @return The number of contributions the user has.
		 **/
		function contribcount ($user) {
			$this->checkurl();
			$ret = $this->api->users($user,1,null,true);
			if ($ret !== false) return $ret;
			return false;
		}
	}
	/**
	 * This class is for interacting with Misplaced Pages's api.php API.
	 **/
	class wikipediaapi {
		private $http;
		private $edittoken;
		private $tokencache;
		private $user, $pass;
		public $apiurl = 'http://en.wikipedia.org/w/api.php';
		/**
		 * This is our constructor.
		 * @return void
		 **/
		function __construct () {
			global $__wp__http;
			if (!isset($__wp__http)) {
				$__wp__http = new http;
			}
			$this->http = &$__wp__http;
		}
		/**
		 * This function takes a username and password and logs you into wikipedia.
		 * @param $user Username to login as.
		 * @param $pass Password that corrisponds to the username.
		 * @return void
		 **/
		function login ($user,$pass) {
			$this->user = $user;
			$this->pass = $pass;
			$x = unserialize($this->http->post($this->apiurl.'?action=login&format=php',array('lgname' => $user, 'lgpassword' => $pass)));
			if($x == 'Success')
				return true;
			if($x == 'NeedToken') {
				$x = unserialize($this->http->post($this->apiurl.'?action=login&format=php',array('lgname' => $user, 'lgpassword' => $pass, 'lgtoken' => $x)));
				if($x == 'Success')
					return true;
			}
			return false;
		}
		/**
		 * This function returns the edit token.
		 * @return Edit token.
		 **/
		function getedittoken () {
			$tokens = $this->gettokens('Main Page');
			if ($tokens == '') $tokens = $this->gettokens('Main Page',true);
			$this->edittoken = $tokens;
			return $tokens;
		}
		/**
		 * This function returns the various tokens for a certain page.
		 * @param $title Page to get the tokens for.
		 * @param $flush Optional - internal use only.  Flushes the token cache.
		 * @return An associative array of tokens for the page.
		 **/
		function gettokens ($title,$flush = false) {
			if (!is_array($this->tokencache)) $this->tokencache = array();
			foreach ($this->tokencache as $t => $data) if (time() - $data > 6*60*60) unset($this->tokencache);
			if (isset($this->tokencache) && (!$flush)) {
				return $this->tokencache;
			} else {
				$tokens = array();
				$x = $this->http->get($this->apiurl.'?action=query&format=php&prop=info&intoken=edit|delete|protect|move|block|unblock|email&titles='.urlencode($title));
				$x = unserialize($x);
				foreach ($x as $y) {
					$tokens = $y;
					$tokens = $y;
					$tokens = $y;
					$tokens = $y;
					$tokens = $y;
					$tokens = $y;
					$tokens = $y;
					$this->tokencache = array(
							'timestamp' => time(),
							'tokens' => $tokens
									 );
					return $tokens;
				}
			}
		}
		/**
		 * This function returns the recent changes for the wiki.
		 * @param $count The number of items to return. (Default 10)
		 * @param $namespace The namespace ID to filter items on. Null for no filtering. (Default null)
		 * @param $dir The direction to pull items.  "older" or "newer".  (Default 'older')
		 * @param $ts The timestamp to start at.  Null for the beginning/end (depending on direction).  (Default null)
		 * @return Associative array of recent changes metadata.
		 **/
		function recentchanges ($count = 10,$namespace = null,$dir = 'older',$ts = null) {
			$append = '';
			if ($ts !== null) { $append .= '&rcstart='.urlencode($ts); }
			$append .= '&rcdir='.urlencode($dir);
			if ($namespace !== null) { $append .= '&rcnamespace='.urlencode($namespace); }
			$x = $this->http->get($this->apiurl.'?action=query&list=recentchanges&rcprop=user|comment|flags|timestamp|title|ids|sizes&format=php&rclimit='.$count.$append);
			$x = unserialize($x);
			return $x;
		}
		/**
		 * This function returns search results from Misplaced Pages's internal search engine.
		 * @param $search The query string to search for.
		 * @param $limit The number of results to return. (Default 10)
		 * @param $offset The number to start at.  (Default 0)
		 * @param $namespace The namespace ID to filter by.  Null means no filtering.  (Default 0)
		 * @param $what What to search, 'text' or 'title'.  (Default 'text')
		 * @param $redirs Whether or not to list redirects.  (Default false)
		 * @return Associative array of search result metadata.
		 **/
		function search ($search,$limit = 10,$offset = 0,$namespace = 0,$what = 'text',$redirs = false) {
			$append = '';
			if ($limit != null) $append .= '&srlimit='.urlencode($limit);
			if ($offset != null) $append .= '&sroffset='.urlencode($offset);
			if ($namespace != null) $append .= '&srnamespace='.urlencode($namespace);
			if ($what != null) $append .= '&srwhat='.urlencode($what);
			if ($redirs == true) $append .= '&srredirects=1';
			else $append .= '&srredirects=0';
			$x = $this->http->get($this->apiurl.'?action=query&list=search&format=php&srsearch='.urlencode($search).$append);
			$x = unserialize($x);
			return $x;
		}
		/**
		 * Retrieve entries from the WikiLog.
		 * @param $user Username who caused the entry.  Null means anyone.  (Default null)
		 * @param $title Object to which the entry refers.  Null means anything.  (Default null)
		 * @param $limit Number of entries to return.  (Default 50)
		 * @param $type Type of logs.  Null means any type.  (Default null)
		 * @param $start Date to start enumerating logs.  Null means beginning/end depending on $dir.  (Default null)
		 * @param $end Where to stop enumerating logs.  Null means whenever limit is satisfied or there are no more logs.  (Default null)
		 * @param $dir Direction to enumerate logs.  "older" or "newer".  (Default 'older')
		 * @return Associative array of logs metadata.
		 **/
		function logs ($user = null,$title = null,$limit = 50,$type = null,$start = null,$end = null,$dir = 'older') {
			$append = '';
			if ($user != null) $append.= '&leuser='.urlencode($user);
			if ($title != null) $append.= '&letitle='.urlencode($title);
			if ($limit != null) $append.= '&lelimit='.urlencode($limit);
			if ($type != null) $append.= '&letype='.urlencode($type);
			if ($start != null) $append.= '&lestart='.urlencode($start);
			if ($end != null) $append.= '&leend='.urlencode($end);
			if ($dir != null) $append.= '&ledir='.urlencode($dir);
			$x = $this->http->get($this->apiurl.'?action=query&format=php&list=logevents&leprop=ids|title|type|user|timestamp|comment|details'.$append);
			$x = unserialize($x);
			return $x;
		}
		/**
		 * Retrieves metadata about a user's contributions.
		 * @param $user Username whose contributions we want to retrieve.
		 * @param $count Number of entries to return.  (Default 50)
		 * @param $continue Where to continue enumerating if part of a larger, split request.  This is filled with the next logical continuation value.  (Default null)
		 * @param $dir Which direction to enumerate from, "older" or "newer".  (Default 'older')
		 * @return Associative array of contributions metadata.
		 **/
		function usercontribs ($user,$count = 50,&$continue = null,$dir = 'older') {
			if ($continue != null) {
				$append = '&ucstart='.urlencode($continue);
			} else {
				$append = '';
			}
			$x = $this->http->get($this->apiurl.'?action=query&format=php&list=usercontribs&ucuser='.urlencode($user).'&uclimit='.urlencode($count).'&ucdir='.urlencode($dir).$append);
			$x = unserialize($x);
			$continue = $x;
			return $x;
		}
		/**
		 * Returns revision data (meta and/or actual).
		 * @param $page Page for which to return revision data for.
		 * @param $count Number of revisions to return. (Default 1)
		 * @param $dir Direction to start enumerating multiple revisions from, "older" or "newer". (Default 'older')
		 * @param $content Whether to return actual revision content, true or false.  (Default false)
		 * @param $revid Revision ID to start at.  (Default null)
		 * @param $wait Whether or not to wait a few seconds for the specific revision to become available.  (Default true)
		 * @param $getrbtok Whether or not to retrieve a rollback token for the revision.  (Default false)
		 * @param $dieonerror Whether or not to kill the process with an error if an error occurs.  (Default false)
		 * @param $redirects Whether or not to follow redirects.  (Default false)
		 * @return Associative array of revision data.
		 **/
		function revisions ($page,$count = 1,$dir = 'older',$content = false,$revid = null,$wait = true,$getrbtok = false,$dieonerror = true,$redirects = false) {
			$x = $this->http->get($this->apiurl.'?action=query&prop=revisions&titles='.urlencode($page).'&rvlimit='.urlencode($count).'&rvprop=timestamp|ids|user|comment'.(($content)?'|content':'').'&format=php&meta=userinfo&rvdir='.urlencode($dir).(($revid !== null)?'&rvstartid='.urlencode($revid):'').(($getrbtok == true)?'&rvtoken=rollback':'').(($redirects == true)?'&redirects':''));
			$x = unserialize($x);
			if ($revid !== null) {
				$found = false;
				if (!isset($x) or !is_array($x)) {
					if ($dieonerror == true) die('No such page.'."\n");
					else return false;
				}
				foreach ($x as $data) {
					if (!isset($data) or !is_array($data)) {
						if ($dieonerror == true) die('No such page.'."\n");
						else return false;
					}
					foreach ($data as $data2) if ($data2 == $revid) $found = true;
					unset($data,$data2);
					break;
				}
				if ($found == false) {
					if ($wait == true) {
						sleep(1);
						return $this->revisions($page,$count,$dir,$content,$revid,false,$getrbtok,$dieonerror);
					} else {
						if ($dieonerror == true) die('Revision error.'."\n");
					}
				}
			}
			foreach ($x as $key => $data) {
				$data = $data;
				$data = $data;
				$data = $x;
//				$data = $x;
				$data = $x;
				$data = $key;
				return $data;
			}
		}
		/**
		 * Enumerates user metadata.
		 * @param $start The username to start enumerating from.  Null means from the beginning.  (Default null)
		 * @param $limit The number of users to enumerate.  (Default 1)
		 * @param $group The usergroup to filter by.  Null means no filtering.  (Default null)
		 * @param $requirestart Whether or not to require that $start be a valid username.  (Default false)
		 * @param $continue This is filled with the name to continue from next query.  (Default null)
		 * @return Associative array of user metadata.
		 **/
		function users ($start = null,$limit = 1,$group = null,$requirestart = false,&$continue = null) {
			$append = '';
			if ($start != null) $append .= '&aufrom='.urlencode($start);
			if ($group != null) $append .= '&augroup='.urlencode($group);
			$x = $this->http->get($this->apiurl.'?action=query&list=allusers&format=php&auprop=blockinfo|editcount|registration|groups&aulimit='.urlencode($limit).$append);
			$x = unserialize($x);
			$continue = $x;
			if (($requirestart == true) and ($x != $start)) return false;
			return $x;
		}
		/**
		 * Get members of a category.
		 * @param $category Category to enumerate from.
		 * @param $count Number of members to enumerate.  (Default 500)
		 * @param $continue Where to continue enumerating from.  This is automatically filled in when run.  (Default null)
		 * @return Associative array of category member metadata.
		 **/
		function categorymembers ($category,$count = 500,&$continue = null) {
			if ($continue != null) {
				$append = '&cmcontinue='.urlencode($continue);
			} else {
				$append = '';
			}
			$category = 'Category:'.str_ireplace('category:','',$category);
			$x = $this->http->get($this->apiurl.'?action=query&list=categorymembers&cmtitle='.urlencode($category).'&format=php&cmlimit='.$count.$append);
			$x = unserialize($x);
			$continue = $x;
			return $x;
		}
		/**
		 * Enumerate all categories.
		 * @param $start Where to start enumerating.  This is updated automatically with the value to continue from.  (Default null)
		 * @param $limit Number of categories to enumerate.  (Default 50)
		 * @param $dir Direction to enumerate in.  'ascending' or 'descending'.  (Default 'ascending')
		 * @param $prefix Only enumerate categories with this prefix.  (Default null)
		 * @return Associative array of category list metadata.
		 **/
		function listcategories (&$start = null,$limit = 50,$dir = 'ascending',$prefix = null) {
			$append = '';
			if ($start != null) $append .= '&acfrom='.urlencode($start);
			if ($limit != null) $append .= '&aclimit='.urlencode($limit);
			if ($dir != null) $append .= '&acdir='.urlencode($dir);
			if ($prefix != null) $append .= '&acprefix='.urlencode($prefix);
			$x = $this->http->get($this->apiurl.'?action=query&list=allcategories&acprop=size&format=php'.$append);
			$x = unserialize($x);
			$start = $x;
			return $x;
		}
		/**
		 * Enumerate all backlinks to a page.
		 * @param $page Page to search for backlinks to.
		 * @param $count Number of backlinks to list.  (Default 500)
		 * @param $continue Where to start enumerating from.  This is automatically filled in.  (Default null)
		 * @param $filter Whether or not to include redirects.  Acceptible values are 'all', 'redirects', and 'nonredirects'.  (Default null)
		 * @return Associative array of backlink metadata.
		 **/
		function backlinks ($page,$count = 500,&$continue = null,$filter = null) {
			if ($continue != null) {
				$append = '&blcontinue='.urlencode($continue);
			} else {
				$append = '';
			}
			if ($filter != null) {
				$append .= '&blfilterredir='.urlencode($filter);
			}
			$x = $this->http->get($this->apiurl.'?action=query&list=backlinks&bltitle='.urlencode($page).'&format=php&bllimit='.$count.$append);
			$x = unserialize($x);
			$continue = $x;
			return $x;
		}
		/**
		 * Gets a list of transcludes embedded in a page.
		 * @param $page Page to look for transcludes in.
		 * @param $count Number of transcludes to list.  (Default 500)
		 * @param $continue Where to start enumerating from.  This is automatically filled in.  (Default null)
		 * @return Associative array of transclude metadata.
		 **/
		function embeddedin ($page,$count = 500,&$continue = null) {
			if ($continue != null) {
				$append = '&eicontinue='.urlencode($continue);
			} else {
				$append = '';
			}
			$x = $this->http->get($this->apiurl.'?action=query&list=embeddedin&eititle='.urlencode($page).'&format=php&eilimit='.$count.$append);
			$x = unserialize($x);
			$continue = $x;
			return $x;
		}
		/**
		 * Gets a list of pages with a common prefix.
		 * @param $prefix Common prefix to search for.
		 * @param $namespace Numeric namespace to filter on.  (Default 0)
		 * @param $count Number of pages to list.  (Default 500)
		 * @param $continue Where to start enumerating from.  This is automatically filled in.  (Default null)
		 * @return Associative array of page metadata.
		 **/
		function listprefix ($prefix,$namespace = 0,$count = 500,&$continue = null) {
			$append = '&apnamespace='.urlencode($namespace);
			if ($continue != null) {
				$append .= '&apfrom='.urlencode($continue);
			}
			$x = $this->http->get($this->apiurl.'?action=query&list=allpages&apprefix='.urlencode($prefix).'&format=php&aplimit='.$count.$append);
			$x = unserialize($x);
			$continue = $x;
			return $x;
		}
		/**
		 * Edits a page.
		 * @param $page Page name to edit.
		 * @param $data Data to post to page.
		 * @param $summary Edit summary to use.
		 * @param $minor Whether or not to mark edit as minor.  (Default false)
		 * @param $bot Whether or not to mark edit as a bot edit.  (Default true)
		 * @param $wpStarttime Time in MW TS format of beginning of edit.  (Default now)
		 * @param $wpEdittime Time in MW TS format of last edit to that page.  (Default correct)
		 * @return boolean True on success, false on failure.
		 **/
		function edit ($page,$data,$summary = '',$minor = false,$bot = true,$wpStarttime = null,$wpEdittime = null,$checkrun = true) {
			global $run, $user;
			$wpq = new wikipediaquery; $wpq->queryurl = str_replace('api.php','query.php',$this->apiurl);
			if ($checkrun == true)
				if (!preg_match('/(yes|enable|true)/iS',((isset($run))?$run:$wpq->getpage('User:'.$user.'/Run'))))
					return false; /* Check /Run page */
			$params = Array(
				'action' => 'edit',
				'format' => 'php',
				'assert' => 'bot',
				'title' => $page,
				'text' => $data,
				'token' => $this->getedittoken(),
				'summary' => $summary,
				($minor?'minor':'notminor') => '1',
				($bot?'bot':'notbot') => '1'
			);
			if ($wpStarttime !== null) $params = $wpStarttime;
			if ($wpEdittime !== null) $params = $wpEdittime;
			$x = $this->http->post($this->apiurl,$params);
			$x = unserialize($x);
			var_export($x);
			if ($x == 'Success') return true;
			if ($x == 'badtoken') {
				if($this->login($this->user,$this->pass)) {
					$this->gettokens('Main Page',true);
					return $this->edit($page,$data,$summary,$minor,$bot,$wpStarttime,$wpEdittime,$checkrun);
				} else
					return false;
			}
			else return false;
		}
		/**
		 * Moves a page.
		 * @param $old Name of page to move.
		 * @param $new New page title.
		 * @param $reason Move summary to use.
		 * @return void
		 **/
		function move ($old,$new,$reason) {
			$tokens = $this->gettokens($old);
			$params = array(
				'action' => 'move',
				'format' => 'php',
				'from' => $old,
				'to' => $new,
				'token' => $tokens,
				'reason' => $reason
			);
			$x = $this->http->post($this->apiurl,$params);
			$x = unserialize($x);
			var_export($x);
		}
		/**
		 * Rollback an edit.
		 * @param $title Title of page to rollback.
		 * @param $user Username of last edit to the page to rollback.
		 * @param $reason Edit summary to use for rollback.
		 * @param $token Rollback token.  If not given, it will be fetched.  (Default null)
		 * @return void
		 **/
		function rollback ($title,$user,$reason,$token = null) {
			if (($token == null) or ($token == '')) {
				$token = $this->revisions($title,1,'older',false,null,true,true);
				print_r($token);
				if ($token == $user) {
					$token = $token;
				} else {
					return false;
				}
			}
			$params = array(
				'action' => 'rollback',
				'format' => 'php',
				'title' => $title,
				'user' => $user,
				'summary' => $reason,
				'token' => $token,
				'markbot' => 0
			);
			echo 'Posting to API: ';
			var_export($params);
			$x = $this->http->post($this->apiurl,$params);
			$x = unserialize($x);
			var_export($x);
			return ( isset($x) and isset( $x ) and $x )
				? true
				: false;
		}
	}
	/**
	 * This class is for interacting with Misplaced Pages's browser interface, index.php.
	 * Many of these functions are deprecated.
	 **/
	class wikipediaindex {
		private $http;
		public $indexurl = 'http://en.wikipedia.org/search/';
		private $postinterval = 0;
		private $lastpost;
		private $edittoken;
		/**
		 * This is our constructor.
		 * @return void
		 **/
		function __construct () {
			global $__wp__http;
			if (!isset($__wp__http)) {
				$__wp__http = new http;
			}
			$this->http = &$__wp__http;
		}
		/**
		 * Post data to a page, nicely.
		 * @param $page Page title.
		 * @param $data Data to post to page.
		 * @param $summery Edit summary.  (Default '')
		 * @param $minor Whether to mark edit as minor.  (Default false)
		 * @param $rv Revision data.  If not given, it will be fetched.  (Default null)
		 * @param $bot Whether to mark edit as bot.  (Default true)
		 * @return HTML data from the page.
		 * @deprecated
		 * @see wikipediaapi::edit
		 **/
		function post ($page,$data,$summery = '',$minor = false,$rv = null,$bot = true) {
			global $user;
			global $maxlag;
			global $irc;
			global $irctechchannel;
			global $run;
			global $maxlagkeepgoing;
			$wpq = new wikipediaquery; $wpq->queryurl = str_replace('index.php','query.php',$this->indexurl);
			$wpapi = new wikipediaapi; $wpapi->apiurl = str_replace('index.php','api.php',$this->indexurl);
			if ((!$this->edittoken) or ($this->edittoken == '')) $this->edittoken = $wpapi->getedittoken();
			if ($rv == null) $rv = $wpapi->revisions($page,1,'older',true);
			if (!$rv) $rv = $wpq->getpage($page);
			//Fake the edit form.
			$now = gmdate('YmdHis', time());
			$token = htmlspecialchars($this->edittoken);
			$tmp = date_parse($rv);
			$edittime = gmdate('YmdHis', gmmktime($tmp,$tmp,$tmp,$tmp,$tmp,$tmp));
			$html = "<input type='hidden' value=\"{$now}\" name=\"wpStarttime\" />\n";
			$html.= "<input type='hidden' value=\"{$edittime}\" name=\"wpEdittime\" />\n";
			$html.= "<input type='hidden' value=\"{$token}\" name=\"wpEditToken\" />\n";
			$html.= '<input name="wpAutoSummary" type="hidden" value="'.md5('').'" />'."\n";
			if (preg_match('/'.preg_quote('{{nobots}}','/').'/iS',$rv)) { return false; }		/* Honor the bots flags */
			if (preg_match('/'.preg_quote('{{bots|allow=none}}','/').'/iS',$rv)) { return false; }
			if (preg_match('/'.preg_quote('{{bots|deny=all}}','/').'/iS',$rv)) { return false; }
			if (preg_match('/'.preg_quote('{{bots|deny=','/').'(.*)'.preg_quote('}}','/').'/iS',$rv,$m)) { if (in_array(explode(',',$m),$user)) { return false; } } /* /Honor the bots flags */
			if (!preg_match('/'.preg_quote($user,'/').'/iS',$rv)) { return false; } /* We need to be logged in */
//			if (preg_match('/'.preg_quote('You have new messages','/').'/iS',$rv)) { return false; } /* Check talk page */
			if (!preg_match('/(yes|enable|true)/iS',((isset($run))?$run:$wpq->getpage('User:'.$user.'/Run')))) { return false; } /* Check /Run page */
			$x = $this->forcepost($page,$data,$summery,$minor,$html,$maxlag,$maxlagkeepgoing,$bot); /* Go ahead and post. */
			$this->lastpost = time();
			return $x;
		}
		/**
		 * Post data to a page.
		 * @param $page Page title.
		 * @param $data Data to post to page.
		 * @param $summery Edit summary.  (Default '')
		 * @param $minor Whether to mark edit as minor.  (Default false)
		 * @param $edithtml HTML from the edit form.  If not given, it will be fetched.  (Default null)
		 * @param $maxlag Maxlag for posting.  (Default null)
		 * @param $mlkg Whether to keep going after encountering a maxlag error and sleeping or not.  (Default null)
		 * @param $bot Whether to mark edit as bot.  (Default true)
		 * @return HTML data from the page.
		 * @deprecated
		 * @see wikipediaapi::edit
		 **/
		function forcepost ($page,$data,$summery = '',$minor = false,$edithtml = null,$maxlag = null,$mlkg = null,$bot = true) {
			$post = '';
			$post = '';
			if ($minor == true) { $post = 1; }
			$post = $data;
			$post = $summery;
			if ($edithtml == null) {
				$html = $this->http->get($this->indexurl.'?title='.urlencode($page).'&action=edit');
			} else {
				$html = $edithtml;
			}
			preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpStarttime\" /\>|U',$html,$m);
			$post = $m;
			preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEdittime\" /\>|U',$html,$m);
			$post = $m;
			preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEditToken\" /\>|U',$html,$m);
			$post = $m;
			preg_match('|\<input name\=\"wpAutoSummary\" type\=\"hidden\" value\=\"(.*)\" /\>|U',$html,$m);
			$post = $m;
			if ($maxlag != null) {
				$x = $this->http->post($this->indexurl.'?title='.urlencode($page).'&action=submit&maxlag='.urlencode($maxlag).'&bot='.(($bot == true)?'1':'0'),$post);
				if (preg_match('/Waiting for (*): (+) seconds lagged/S',$x,$lagged)) {
					global $irc;
					if (is_resource($irc)) {
						global $irctechchannel;
						foreach(explode(',',$irctechchannel) as $y) {
							fwrite($irc,'PRIVMSG '.$y.' :'.$lagged.' is lagged out by '.$lagged.' seconds. ('.$lagged.')'."\n");
						}
					}
					sleep(10);
					if ($mlkg != true) { return false; }
					else { $x = $this->http->post($this->indexurl.'?title='.urlencode($page).'&action=submit&bot='.(($bot == true)?'1':'0'),$post); }
				}
				return $x;
			} else {
				return $this->http->post($this->indexurl.'?title='.urlencode($page).'&action=submit&bot='.(($bot == true)?'1':'0'),$post);
			}
		}
		/**
		 * Get a diff.
		 * @param $title Page title to get the diff of.
		 * @param $oldid Old revision ID.
		 * @param $id New revision ID.
		 * @param $wait Whether or not to wait for the diff to become available.  (Default true)
		 * @return Array of added data, removed data, and a rollback token if one was fetchable.
		 **/
		function diff ($title,$oldid,$id,$wait = true) {
			$deleted = '';
			$added = '';
			$html = $this->http->get($this->indexurl.'?title='.urlencode($title).'&action=render&diff='.urlencode($id).'&oldid='.urlencode($oldid).'&diffonly=1');
			if (preg_match_all('/\&amp\;(oldid\=)(\d*)\\\'\>(Revision as of|Current revision as of)/USs', $html, $m, PREG_SET_ORDER)) {
				//print_r($m);
				if ((($oldid != $m) and (is_numeric($oldid))) or (($id != $m) and (is_numeric($id)))) {
					if ($wait == true) {
						sleep(1);
						return $this->diff($title,$oldid,$id,false);
					} else {
						echo 'OLDID as detected: '.$m.' Wanted: '.$oldid."\n";
						echo 'NEWID as detected: '.$m.' Wanted: '.$id."\n";
						echo $html;
						die('Revision error.'."\n");
					}
				}
			}
			if (preg_match_all('/\<td class\=(\"|\\\')diff-addedline\1\>\<div\>(.*)\<\/div\>\<\/td\>/USs', $html, $m, PREG_SET_ORDER)) {
				//print_r($m);
				foreach ($m as $x) {
					$added .= htmlspecialchars_decode(strip_tags($x))."\n";
				}
			}
			if (preg_match_all('/\<td class\=(\"|\\\')diff-deletedline\1\>\<div\>(.*)\<\/div\>\<\/td\>/USs', $html, $m, PREG_SET_ORDER)) {
				//print_r($m);
				foreach ($m as $x) {
					$deleted .= htmlspecialchars_decode(strip_tags($x))."\n";
				}
			}
			//echo $added."\n".$deleted."\n";
			if (preg_match('/action\=rollback\&amp\;from\=.*\&amp\;token\=(.*)\"/US', $html, $m)) {
				$rbtoken = $m;
				$rbtoken = urldecode($rbtoken);
//				echo 'rbtoken: '.$rbtoken.' -- '; print_r($m); echo "\n\n";
				return array($added,$deleted,$rbtoken);
			}
			return array($added,$deleted);
		}
		/**
		 * Rollback an edit.
		 * @param $title Page title to rollback.
		 * @param $user Username of last edit to the page to rollback.
		 * @param $reason Reason to rollback.  If null, default is generated.  (Default null)
		 * @param $token Rollback token to use.  If null, it is fetched.  (Default null)
		 * @param $bot Whether or not to mark as bot.  (Default true)
		 * @return HTML or false if failure.
		 * @deprecated
		 * @see wikipediaapi::rollback
		 **/
		function rollback ($title,$user,$reason = null,$token = null,$bot = true) {
			if (($token == null) or (!$token)) {
				$wpapi = new wikipediaapi; $wpapi->apiurl = str_replace('index.php','api.php',$this->indexurl);
				$token = $wpapi->revisions($title,1,'older',false,null,true,true);				
				if ($token == $user) {
//					echo 'Token: '; print_r($token); echo "\n\n";
					$token = $token;
				} else {
					return false;
				}
			}
			$x = $this->http->get($this->indexurl.'?title='.urlencode($title).'&action=rollback&from='.urlencode($user).'&token='.urlencode($token).(($reason != null)?'&summary='.urlencode($reason):'').'&bot='.(($bot == true)?'1':'0'));
			global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'Rollback return: '.$x."\n");
			if (!preg_match('/action complete/iS',$x)) return false;
			return $x;
		}
		/**
		 * Move a page.
		 * @param $old Page title to move.
		 * @param $new New title to move to.
		 * @param $reason Move page summary.
		 * @return HTML page.
		 * @deprecated
		 * @see wikipediaapi::move
		 **/
		function move ($old,$new,$reason) {
			$wpapi = new wikipediaapi; $wpapi->apiurl = str_replace('index.php','api.php',$this->indexurl);
			if ((!$this->edittoken) or ($this->edittoken == '')) $this->edittoken = $wpapi->getedittoken();
			$token = htmlspecialchars($this->edittoken);
			$post = array
				(
					'wpOldTitle'	=> $old,
					'wpNewTitle'	=> $new,
					'wpReason'	=> $reason,
					'wpWatch'	=> '0',
					'wpEditToken'	=> $token,
					'wpMove'	=> 'Move page'
				);
			return $this->http->post($this->indexurl.'?title=Special:Movepage&action=submit',$post);
		}
		/**
		 * Uploads a file.
		 * @param $page Name of page on the wiki to upload as.
		 * @param $file Name of local file to upload.
		 * @param $desc Content of the file description page.
		 * @return HTML content.
		 **/
		function upload ($page,$file,$desc) {
			$post = array
				(
					'wpUploadFile'		=> '@'.$file,
					'wpSourceType'		=> 'file',
					'wpDestFile'		=> $page,
					'wpUploadDescription'	=> $desc,
					'wpLicense'		=> '',
					'wpWatchthis'		=> '0',
					'wpIgnoreWarning'	=> '1',
					'wpUpload'		=> 'Upload file'
				);
			return $this->http->post($this->indexurl.'?title=Special:Upload&action=submit',$post);
		}
		/**
		 * Check if a user has email enabled.
		 * @param $user Username to check whether or not the user has email enabled.
		 * @return True or false depending on whether or not the user has email enabled.
		 **/
		function hasemail ($user) {
			$tmp = $this->http->get($this->indexurl.'?title=Special:EmailUser&target='.urlencode($user));
			if (stripos($tmp,"No e-mail address") !== false) return false;
			return true;
		}
		/**
		 * Sends an email to a user.
		 * @param $user Username to send email to.
		 * @param $subject Subject of email to send.
		 * @param $body Body of email to send.
		 * @return HTML content.
		 **/
		function email ($user,$subject,$body) {
			$wpapi = new wikipediaapi; $wpapi->apiurl = str_replace('index.php','api.php',$this->indexurl);
			if ((!$this->edittoken) or ($this->edittoken == '')) $this->edittoken = $wpapi->getedittoken();
			$post = array
				(
					'wpSubject'	=> $subject,
					'wpText'	=> $body,
					'wpCCMe'	=> 0,
					'wpSend'	=> 'Send',
					'wpEditToken'	=> $this->edittoken
				);
			return $this->http->post($this->indexurl.'?title=Special:EmailUser&target='.urlencode($user).'&action=submit',$post);
		}
	}
?>


Diff function (diff.function.php)

<?PHP
	function diff ($old,$new,$nret = true,$inline = false) {
//		if ($inline) {
//			return str_replace(array("\n",chr(92).chr(92),'\n'),array(' ',chr(92),"\n"),diff(implode("\n",explode(' ',str_replace(array(chr(92),"\n"),array(chr(92).chr(92),'\n'),$old))),implode("\n",explode(' ',str_replace(array(chr(92),"\n"),array(chr(92).chr(92),'\n'),$new))),$nret,false));
//		}
		$file1 = tempnam('/tmp','diff_');
		$file2 = tempnam('/tmp','diff_');
		file_put_contents($file1,$old);
		file_put_contents($file2,$new);
		$out = array();
		if ($inline) {
//			echo 'EXEC: wdiff -3'.(($nret)?'1':'2').' '.escapeshellarg($file1).' '.escapeshellarg($file2)."\n";
			@exec('wdiff -3'.(($nret)?'1':'2').' '.escapeshellarg($file1).' '.escapeshellarg($file2),$out);
			foreach ($out as $key => $line) {
				if ($line == '======================================================================') unset($out);
				elseif ($nret) $out = '> '.$line;
				else $out = '< '.$line;
			}
		} else {
			@exec('diff -d --suppress-common-lines '.escapeshellarg($file1).' '.escapeshellarg($file2),$out);
		}
		$out2 = array();
		foreach ($out as $line) {
			if (
				(
					($nret)
					and (preg_match('/^\> .*$/',$line))
				)
				or (
					(!$nret)
					and (preg_match('/^\< .*$/',$line))
				)
			) {
				$out2 = substr($line,2);
			}
		}
		$out = $out2;
		unset($out2);
		unlink($file1);
		unlink($file2);
		return implode("\n",$out);
	}
?>


Source to ClueBot

<?PHP
	declare(ticks = 1);
	function sig_handler($signo) {
		switch ($signo) {
			case SIGCHLD:
				while (($x = pcntl_waitpid(0, $status, WNOHANG)) != -1) {
					if ($x == 0) break;
					$status = pcntl_wexitstatus($status);
				}
				break;
		}
	}
	pcntl_signal(SIGCHLD,   "sig_handler");
	function score ($list,$data,&$matches = null) {
		$ret = 0;
		foreach ($list as $preg => $pts) {
			if ($x = preg_match_all($preg.'S',$data,$m)) {
//				echo $x.'*'.$pts.' ('.$preg.')'."\n";
				$matches = $x;
				$ret += $pts * $x;
			}
		}
//		echo 'Score: '.$ret."\n";
		return $ret;
	}
	function myfnmatch ($pattern,$string) {
		if (strlen($string) < 4000) {
			return fnmatch($pattern,$string);
		} else {
			$pattern = strtr(preg_quote($pattern, '#'), array('\*' => '.*', '\?' => '.', '\' => ']'));
			if (preg_match('#^'.$pattern.'$#',$string)) return true;
			return false;
		}
	}
	include '../diff.function.php'; /* The diff function. */
	include '../wikibot.classes.php'; /* The wikipedia classes. */
	include 'cluebot.config.php'; /* This file is very simple, but it contains sensitive information, we just define $user, $ircserver, $ircport, $ircchannel, $pass, $owner, and $status. */
	include 'cluebot.scorelist.php'; /* This file is uploaded as well as the main file. */
	$wpapi	= new wikipediaapi;
	$wpq	= new wikipediaquery;
	$wpi	= new wikipediaindex;
	var_export($wpapi->login($user,$pass));
	$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
	if (!$mysql) { die('Could not connect: ' . mysql_error()); }
	if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }
	$ircconfig = explode("\n",$wpq->getpage('User:'.$owner.'/CBChannels.js'));
	$tmp = array();
	foreach($ircconfig as $tmpline) { if (substr($tmpline,0,1) != '#') { $tmpline = explode('=',$tmpline,2); $tmp)] = trim($tmpline); } }
	$ircchannel = $tmp;
	$ircdebugchannel = $tmp;
	$ircreportchannel = $tmp;
	$ircvandalismchannel = $tmp;
	$ircaivchannel = $tmp;
	$irctechchannel = $tmp;
	$ircproxychannels = $tmp;
	$ircunrevertedchannels = $tmp;
	$ircbagtrialchannels = $tmp;
	$ircotherchannels = $tmp;
	unset($tmp,$tmpline);
	$stalkbots = array();
	$trialbots = explode("\n",$wpq->getpage('Misplaced Pages:Bots/Requests for approval'));
	foreach ($trialbots as $trialbot) if (preg_match('/\{\{BRFA\|(.*)\|.*\|Trial\}\}/',str_replace(array("\n","\r"),'',$trialbot),$m)) $stalkbots] = 1;
	$irc = fsockopen($ircserver,$ircport,$ircerrno,$ircerrstr,15);
	$ircpid = pcntl_fork();
	if ($ircpid == 0) {
		fwrite($irc,'PASS '.$ircpass."\n");
		fwrite($irc,'USER '.$user.' "1" "1" :ClueBot Misplaced Pages Bot.'."\n");
		fwrite($irc,'NICK '.$user."\n");
		while (!feof($irc)) {
			$data = str_replace(array("\n","\r"),'',fgets($irc,1024));
//			echo 'IRC: '.$data."\n";
			$d = explode(' ',$data);
			if (strtolower($d) == 'ping') {
				fwrite($irc,'PONG '.$d."\n");
			} elseif (($d == '376') or ($d == '422')) {
//				fwrite($irc,'PRIVMSG NickServ :identify '.$pass."\n");
//				sleep(2);
				fwrite($irc,'JOIN '.$ircchannel.','.$ircdebugchannel.','.$ircreportchannel.','.$ircvandalismchannel.','.$ircaivchannel.','.$irctechchannel.','.$ircproxychannels.','.$ircunrevertedchannels.','.$ircbagtrialchannels.','.$ircotherchannels."\n");
				foreach (explode(',',$ircchannel) as $y) {
					fwrite($irc,'PRIVMSG '.$y.' :IRC logging enabled.'."\n");
				}
			} elseif (strtolower($d) == 'privmsg') {
				if (substr($d,0,2) == ':!') {
					if (strtolower($d) == '#wikipedia-en') { $tmp = explode('!',substr($d,1)); $cmd = 'NOTICE '.$tmp; }
					elseif (strtolower($d) == strtolower($user)) { $tmp = explode('!',substr($d,1)); $cmd = 'NOTICE '.$tmp; }
					else { $cmd = 'PRIVMSG '.$d; }
					switch (substr(strtolower($d),2)) {
						case 'edit':
							if (preg_match("/\\]/",$data,$m)) {
								$rv = $wpapi->revisions($m,1,'older');
								fwrite($irc,$cmd.' :.']] http://en.wikipedia.org/search/?title='.urlencode($m).'&diff=prev' .
									'&oldid='.urlencode($rv).' * '.$rv.' * '.$rv."\n");
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'stalk':
							if (preg_match("/\\]/",$data,$m)) {
								$uc = $wpapi->usercontribs($m,1);
								fwrite($irc,$cmd.' :.']] http://en.wikipedia.org/search/?title='.urlencode($uc).'&diff=prev' .
									'&oldid='.urlencode($uc).' * '.$m.' * '.$uc."\n");
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'beaten':
							if (preg_match("/\\]/",$data,$m)) {
								if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
								$x = mysql_fetch_assoc(mysql_query('SELECT COUNT(`id`) AS `count` FROM `beaten` WHERE `user` = \''.mysql_real_escape_string($m).'\' GROUP BY `user`'));
								$y = mysql_fetch_assoc(mysql_query('SELECT SQL_CALC_FOUND_ROWS COUNT(`id`) AS `count2` FROM `beaten` GROUP BY `user` HAVING `count2` > \''.mysql_real_escape_string($x).'\' LIMIT 1'));
								$z = mysql_fetch_assoc(mysql_query('SELECT FOUND_ROWS() as `ahead`'));
								fwrite($irc,$cmd.' :.']] has beaten me '.(($x != '')?$x:'0').' times.  There are '.$z.' users who have beaten me more times.'."\n");
								unset($x,$y);
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'vandalcount':
							if (preg_match("/\\]/",$data,$m)) {
								$tmp = unserialize(file_get_contents('oftenvandalized.txt'));
								if (isset($tmp])) {
									fwrite($irc,$cmd.' :.']] has been vandalized '.count($tmp]).' time(s) in the last 48 hours.'."\n");
								} else {
									fwrite($irc,$cmd.' :.']] has not been vandalized in the last 48 hours.'."\n");
								}
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'heuristics':
							include 'cluebot.heuristics.config.php';
							$stats = unserialize(file_get_contents('cluebot.heuristics.stats.txt'));
							fwrite($irc,$cmd.' :I have the following heuristics enabled: '.implode(', ',$heuristics).".\n");
							foreach ($stats as $heuristic => $count) {
								fwrite($irc,$cmd.' :The '.$heuristic.' heuristic has been matched '.$count.' times.'."\n");
							}
							unset($count,$heuristic,$stats,$heuristics);
							break;
						case 'status':
							$ov = unserialize(file_get_contents('oftenvandalized.txt'));
							foreach ($ov as $title => $array) {
								if (count($array) == 0) unset($ov);
							}
							file_put_contents('oftenvandalized.txt',serialize($ov));
							$count = count($ov);
							$titles = unserialize(file_get_contents('titles.txt'));
							foreach ($titles as $title => $time) {
								if ((time() - $time) > (24*60*60)) {
									unset($titles);
								}
							}
							file_put_contents('titles.txt',serialize($titles));
							$tcount = count($titles);
							foreach ($ov as $x => $y) {
								$ocount = count($y);
							}
							arsort($ocount);
							foreach ($ocount as $x => $y) {
								$mova = $x;
								$movacount = $y;
								break;
							}
							preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq->getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfa);
							$tfa = $tfa;
							if (!preg_match('/(yes|enable|true)/i',$wpq->getpage('User:'.$user.'/Run'))) {
								$run = false;
							} else {
								$run = true;
							}
							$top5beat = array();
							if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
							$q = mysql_query('SELECT `user`,COUNT(`id`) AS `count` FROM `cluebot_enwiki`.`beaten` WHERE `user` != \'\' GROUP BY `user` HAVING `count` > 1 ORDER BY `count` DESC LIMIT 5');
							while ($x = mysql_fetch_assoc($q)) {
								$top5beat = $x.' ('.$x.')';
							}
							unset($x,$q);
							$top5beat = implode(' - ',$top5beat);
							fwrite($irc,$cmd.' :I am '.$user.'.  I am currently '.($run?'enabled':'disabled').'.  I currently have '.$wpq->contribcount($user).' contributions.'."\n");
							fwrite($irc,$cmd.' :I have attempted to revert '.$tcount.' unique article/user combinations in the last 24 hours.  ' .
								'I know of '.$count.' different articles that have been vandalized in the last 48 hours.'."\n"
								);
							fwrite($irc,$cmd.' :] is the most vandalized page with a total of '.$movacount.' vandalisms in the last 48 hours.  ' .
								'Today\'s featured article is: ].'."\n"
								);
							fwrite($irc,$cmd.' :The following users have beat me to the revert the most: '.$top5beat."\n");
							fwrite($irc,$cmd.' :I log all information to '.$ircchannel.'.  This channel is '.$d.'.'."\n");
							unset($x,$y,$count,$ov,$tcount,$ocount,$mova,$movacount,$tfa,$run,$title,$titles,$time,$top5beat);
							break;
						case 'warninglevel':
							if (preg_match("/\\]/",$data,$n)) {
								$warning = 0;
								if (preg_match_all('/<!-- Template:(uw-*(\d)(im)?|Blatantvandal \(serious warning\)) -->.*(\d{2}):(\d{2}), (\d+) (+) (\d{4}) \(UTC\)/iU',
									$wpq->getpage('User talk:'.$n),
									$match,PREG_SET_ORDER)
								) {
									foreach ($match as $m) {
										$month = array('January' => 1, 'February' => 2, 'March' => 3,
											'April' => 4, 'May' => 5, 'June' => 6, 'July' => 7,
											'August' => 8, 'September' => 9, 'October' => 10,
											'November' => 11, 'December' => 12
										);
										if ($m == 'Blatantvandal (serious warning)') $m = 4;
										if ((time() - gmmktime($m,$m,0,$month],$m,$m)) <= (2*24*60*60)) {
											if ($m > $warning) { $warning = $m; }
										}
									}
								}
								fwrite($irc,$cmd.' :.']] is at warning level '.$warning.".\n");
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'count':
							if (preg_match("/\\]/",$data,$n)) {
								fwrite($irc,$cmd.' :.']] has '.$wpq->contribcount($n)." contributions.\n");
							} else {
								fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
							}
							break;
						case 'help':
							fwrite($irc,$cmd.' :Please see ].'."\n");
							break;
						case 'eval':
							$tmp = explode(' ',$data,6);
							$tmp1 = explode('!',substr($d,1));
							if ($d == md5($thesecret.$tmp1.$tmp)) {
								eval($tmp);
							} else {
								fwrite($irc,$cmd.' :Code incorrect.'."\n");
							}
							break;
						case 'cbproxy':
							$tmp = explode(' ',$data,6);
							$tmp1 = explode('!',substr($d,1));
							if ($tmp1 == 'ClueBot-Bopm') {
								foreach (explode(',',$ircproxychannels) as $y) {
									fwrite($irc,'PRIVMSG '.$y.' :!admin '.$tmp."\n");
								}
								$data = $wpq->getpage('Misplaced Pages:WikiProject on open proxies');
								if (strpos($data,$tmp) === false) {
									$header = explode('{{proxyip2|127.0.0.1}} || Example',$data,2);
									$header .= '{{proxyip2|127.0.0.1}} || Example ';
									$footer = $header;
									$header = $header;
									$data = "\n".'{{proxyip2|'.$tmp.'}} || '.$tmp.' ~~~~'."\n";
									$data = $header.$data.$footer;
									unset($header,$footer);
									$wpapi->edit('Misplaced Pages:WikiProject on open proxies',$data,'Adding '.$tmp.'.');
									unset($data);
								}
							}
							break;
					}
				}
			}
		}
		die();
	}
	$heuristics = "==Heuristics==\n\n===Config (cluebot.heuristics.config.php)===\n\n<pre>".htmlentities(file_get_contents('cluebot.heuristics.config.php'))."</pre>\n\n";
	foreach (glob('heuristics/cluebot.*.heuristic.php') as $heuristic) $heuristics .= '==='.$heuristic."===\n\n<pre>".htmlentities(file_get_contents($heuristic))."</pre>\n\n";
	unset($heuristic);
	$wpapi->edit('User:'.$user.'/Source',
		'The following is automatically generated by ].\n\n\n\n==Classes (wikibot.classes.php)==\n\n<pre>" .
		htmlentities(file_get_contents('../wikibot.classes.php'))."</pre>\n\n\n\n==Diff function (diff.function.php)==\n\n<pre>" .
		htmlentities(file_get_contents('../diff.function.php'))."</pre>\n\n\n\n==Source to ".$user .
		"==\n\n".'<pre>'.htmlentities(file_get_contents(__FILE__))."</pre>\n\n\n\n" .
		$heuristics .
		"==Score list==\n\n".'<pre>'.htmlentities(file_get_contents('cluebot.scorelist.php'))."</pre>\n\n\n\n~~~~",
		'Automated source upload.'); /* Our source code, we force post this because this is *our* page, and it triggers the nobots. */
	unset($heuristics);
	$wpapi->edit('User:'.$user,
		"{{User:ClueBot Commons/Userpage}}\n",
		'Automated bot userpage set.'); /* Our page, we force post this because this is *our* page. */
	$tfas = 0;
	$pipe = fopen('thepipe','w');
	$stdin = fopen('php://stdin','r');
	$run = $wpq->getpage('User:'.$user.'/Run');
	$wl = $wpq->getpage('User:'.$user.'/Whitelist');
	$optin = $wpq->getpage('User:'.$user.'/Optin');
	$aoptin = $wpq->getpage('User:'.$user.'/AngryOptin');
	unset($tmp,$tmp2,$tmp3);
	$tmp = explode("\n",$wpq->getpage('User:'.$owner.'/CBAutostalk.js'));
	foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $stalk] = trim($tmp3); } }
	$tmp = explode("\n",$wpq->getpage('User:'.$owner.'/CBAutoedit.js'));
	foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $edit] = trim($tmp3); } }
	unset($tmp,$tmp2,$tmp3);
	print_r($stalk); print_r($edit);
	while (1) {
		$feed = fsockopen($feedhost,$feedport,$feederrno,$feederrstr,30);
		if (!$feed) {
			sleep(10);
			$feed = fsockopen($feedhost,$feedport,$feederrno,$feederrstr,30);
			if (!$feed) die($feederrstr.' ('.$feederrno.')');
		}
		fwrite($feed,'USER '.$user.' "1" "1" :ClueBot Misplaced Pages Bot.'."\n");
		fwrite($feed,'NICK '.$user."\n");
		while (!feof($feed)) {
			$rawline = fgets($feed,1024);
			$line = str_replace(array("\n","\r","\002"),'',$rawline);
			$line = preg_replace('/\003(\d\d?(,\d\d?)?)?/','',$line);
//			echo 'FEED: '.$line."\n";
			if (!$line) { fclose($feed); break; }
			$linea= explode(' ',$line,4);
			if (strtolower($linea) == 'ping') {
				fwrite($feed,'PONG '.$linea."\n");
			} elseif (($linea == '376') or ($linea == '422')) {
				fwrite($feed,'JOIN '.$feedchannel."\n");
			} elseif ((strtolower($linea) == 'privmsg') and (strtolower($linea) == strtolower($feedchannel))) {
				$message = substr($linea,1);
				if (preg_match('/^\*)\]\] (\S*) (http:\/\/en\.wikipedia\.org\/w\/index\.php\?diff=(\d*)&oldid=(\d*).*|http:\/\/en\.wikipedia\.org\/wiki\/\S+)? \* (*) \* (\((*)\))? (.*)$/S',$message,$m)) {
					$messagereceived = microtime(1);
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
					$change = $m;
//					include 'cluebot.stalk.config.php';
					$stalkchannel = array();
					foreach ($stalk as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
					foreach ($stalkbots as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$ircbagtrialchannels));
					foreach ($edit as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change.$change))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
//					if ($change == $owner) $stalkchannel = $ircchannel;
					$stalkchannel = array_unique($stalkchannel);
					foreach ($stalkchannel as $y) {
						fwrite($irc,'PRIVMSG '.$y.' :New edit: .$change.']] http://en.wikipedia.org/search/?title=' .
							urlencode($change.$change).'&diff=prev'.'&oldid='.urlencode($change).' * '.$change .
							' * '.$change."\n");
					}
					if (($change == 'User:') or ($change == 'User talk:')) {
						if (strtolower($change) == strtolower($user.'/Run')) { $run = $wpq->getpage('User:'.$user.'/Run'); }
						if (strtolower($change) == strtolower($user.'/Whitelist')) { $wl = $wpq->getpage('User:'.$user.'/Whitelist'); }
						if (strtolower($change) == strtolower($user.'/Optin')) { $optin = $wpq->getpage('User:'.$user.'/Optin'); }
						if (strtolower($change) == strtolower($user.'/AngryOptin')) { $aoptin = $wpq->getpage('User:'.$user.'/AngryOptin'); }
						if (strtolower($change) == strtolower($owner.'/CBAutostalk.js')) {
							unset($stalk);
							$tmp = explode("\n",$wpq->getpage('User:'.$owner.'/CBAutostalk.js'));
							foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $stalk] = trim($tmp3); } }
							unset($tmp,$tmp2,$tmp3);
							print_r($stalk);
						}
						if (strtolower($change) == strtolower($owner.'/CBAutoedit.js')) {
							unset($edit);
							$tmp = explode("\n",$wpq->getpage('User:'.$owner.'/CBAutoedit.js'));
							foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $edit] = trim($tmp3); } }
							unset($tmp,$tmp2,$tmp3);
							print_r($edit);
						}
						if (strtolower($change) == strtolower($owner.'/CBChannels.js')) {
							$ircconfig = explode("\n",$wpq->getpage('User:'.$owner.'/CBChannels.js'));
							$tmp = array();
							foreach($ircconfig as $tmpline) { if (substr($tmpline,0,1) != '#') { $tmpline = explode('=',$tmpline,2); $tmp)] = trim($tmpline); } }
							print_r($tmp);
							$tmpold = array();
							$tmpnew = array();
							foreach ($tmp as $tmp2) foreach (explode(',',$tmp2) as $tmp3) $tmpnew = 1;
							foreach (explode(',',$ircchannel.','.$ircdebugchannel.','.$ircreportchannel.','.$ircvandalismchannel.','.$ircaivchannel.','.$irctechchannel.','.$ircproxychannels.','.$ircunrevertedchannels.','.$ircbagtrialchannels.','.$ircotherchannels) as $tmp3) $tmpold = 1;
							foreach ($tmpold as $tmp2 => $tmp3) if (isset($tmpnew)) unset($tmpold,$tmpnew);
							foreach ($tmpnew as $tmp2 => $tmp3) $tmpnew1 = $tmp2;
							foreach ($tmpold as $tmp2 => $tmp3) $tmpold1 = $tmp2;
							$tmpold = $tmpold1; $tmpnew = $tmpnew1; unset($tmpold1,$tmpnew1);
							fwrite($irc,'JOIN '.implode(',',$tmpnew)."\n");
							fwrite($irc,'PART '.implode(',',$tmpold)."\n");
							$ircchannel = $tmp;
							$ircdebugchannel = $tmp;
							$ircreportchannel = $tmp;
							$ircvandalismchannel = $tmp;
							$ircaivchannel = $tmp;
							$irctechchannel = $tmp;
							$ircproxychannels = $tmp;
							$ircunrevertedchannels = $tmp;
							$ircbagtrialchannels = $tmp;
							$ircotherchannels = $tmp;
							unset($tmp,$tmpline,$tmpold,$tmpnew,$tmp2,$tmp3);
						}
					}
					if ($change.$change == 'Misplaced Pages:Bots/Requests for approval') {
						$stalkbots = array();
						$trialbots = explode("\n",$wpq->getpage('Misplaced Pages:Bots/Requests for approval'));
						foreach ($trialbots as $trialbot) if (preg_match('/\{\{BRFA\|(.*)\|.*\|Trial\}\}/',str_replace(array("\n","\r"),'',$trialbot),$m)) $stalkbots] = 1;
					}
					if (($change != '') and ((!preg_match('/\* \.$change,'/').')\]\] \- .*/i',$optin))) and ($change != 'move') and ($change != 'Template:')) continue;
					$change = $change;
					$change = $change.$change;
					if ($change == 'move') {
						if (preg_match('/moved \\] to \\]( over redirect)?: (.*)$/',$change,$m)) {
							$change = $m;
							$change = $m;
							$change = $change;
							$change = $m;
							echo "\n\n\n".'Move!'."\n\n\n";
							print_r($change);
							echo "\n\n\n".'Move!'."\n\n\n";
						}
					}
					if (
						((time() - $tfas) >= 1800)
						and (preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq->getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfam))
					) {
						$tfas = time();
						$tfa = $tfam;
						//echo "TFA: ".$tfa."\n";
					}
					$s = null;
					$pid = @pcntl_fork();
					if ($pid != 0) continue;
					$hutime = microtime(1);
					include 'cluebot.heuristics.config.php';
					foreach ($heuristics as $heuristic) {
						$heuristicret = false;
						include 'heuristics/cluebot.'.$heuristic.'.heuristic.php';
						if ($heuristicret == true) {
							$stats = unserialize(file_get_contents('cluebot.heuristics.stats.txt'));
							$stats++;
							print_r($log);
							file_put_contents('cluebot.heuristics.stats.txt',serialize($stats));
							unset($stats);
							break;
						}
					}
					if ($heuristicret == true) { echo 'Heuristics time: '.(microtime(1) - $hutime)."\n"; /*file_put_contents('trainingdata.txt',$change."\0".$change."\0".'1'."\n",FILE_APPEND);*/ }
					else {
						$tmp = explode(' ',$rawline,4); $tmp = $tmp;
						$udp = fsockopen('udp://localhost',3333);
						fwrite($udp,substr(str_replace(array("\n","\r"),'',$tmp),1)."\n");
						fclose($udp);
						unset($tmp,$udp);
						$d = $wpi->diff($change,$change,$change);
						$s = score($obscenelist,$d,$log);
						$s -= score($obscenelist,$d,$log);
//						if ($s > 15) file_put_contents('trainingdata.txt',$change."\0".$change."\0".'0'."\n",FILE_APPEND);
					}
					unset($hutime);
					if (
						($heuristicret == true)
					) {
						if (
							(
								( /* IP users with 250 contributions are fine .. */
									(long2ip(ip2long($change)) == $change)
									/* and ($uc = $wpapi->usercontribs($change,250))
									and (!isset($uc)) */
								)
								or ( /* Users with 50 contributions are fine .. */
									(long2ip(ip2long($change)) != $change)
									and ($wpq->contribcount($change) < 50)
								)
							)
							and ( /* Whitelisted users are ok. */
								/* ($wl = $wpq->getpage('User:'.$user.'/Whitelist'))
								and */ (!preg_match('/^\* \,'/').')|\1\]\] \- .*/',$wl))
							)
						) {
//							$vandalpage = $wpq->getpage('User:'.$user.'/PossibleVandalism');
//							$x = explode("\n\n",$vandalpage);
//							foreach ($x as $k => $y) {
//								if (preg_match('/(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+)/',$y,$m)) {
//									if ((time() - gmmktime($m,$m,$m,$m,$m,$m)) > (5*60*60)) {
//										unset($x);
//									}
//								}
//							}
//							$vandalpage = implode("\n\n",$x);
							$diff = 'http://en.wikipedia.org/search/' .
								'?title='.urlencode($change) .
								'&diff='.urlencode($change) .
								'&oldid='.urlencode($change);
							$report = ').']] was ' .
								(($change != 'move')?' by ':'moved to ).']] by ') .
								'.'|'.$change.']] ' .
								'.'|(u)]] ' .
								'.'|(t)]] ' .
								$reason.' on '.gmdate('c');
//							$datatopost = $vandalpage."\n\n".'Possible ]: '.$report." ~~~~\n";
							if ($s == null) {
//								$rv = $wpapi->revisions($change,2,'older',true,$change);
//								$s = score($scorelist,diff($rv,$rv));
//								$s += (score($scorelist,diff($rv,$rv,false))) * -1;
								$s = 'N/A';
							}
							$tmp = unserialize(file_get_contents('oftenvandalized.txt'));
							if (rand(1,50) == 2) {
								foreach ($tmp as $key1 => $tmp2) {
									foreach ($tmp2 as $key2 => $time) {
										if ((time() - $time) > (2*24*60*60)) { unset($tmp); }
									}
								}
							}
							$tmp] = time();
							if (count($tmp]) >= 30) {
								foreach (explode(',',$ircreportchannel) as $y) {
									fwrite($irc,'PRIVMSG '.$y.' :!admin .']] has been vandalized '.(count($tmp])).' times in the last 2 days.'."\n");
								}
							}
							file_put_contents('oftenvandalized.txt',serialize($tmp));
							if (
								(
									($rv1 = $wpapi->revisions($change,1,'older'))
									and ($rv1 == $change)
								)
								or ($change == 'move')
							) { /* No need to continue further if it has been reverted */
								echo 'Possible vandalism: '.$change.' changed by '.$change.' '.$reason.' on '.$rv.'('.$s.").\n";
								foreach (explode(',',$ircdebugchannel) as $y) {
									fwrite($irc,'PRIVMSG '.$y.' :Possible vandalism: '.$change.' changed by '.$change.' '.$reason.' on '.$rv.'('.$s.").\n");
									fwrite($irc,'PRIVMSG '.$y.' :( http://en.wikipedia.org/search/?title='.urlencode($change).'&action=history | '.$change.' )'."\n");
								}
								fwrite($pipe,'http://en.wikipedia.org/search/?title='.urlencode($change).'&action=history'."\n"); /* Tell owner */
								$mqtime = microtime(1);
								if (is_array($log)) {
									$logt = '';
									foreach ($log as $k => $v) {
										$logt .= '* '.$v.' * "'.$k.'"'."\n";
									}
								}
								$query = 'INSERT INTO `vandalism` ' .
									'(`id`,`user`,`article`,`heuristic`'.((is_array($log))?',`regex`':'').',`reason`,`diff`,`old_id`,`new_id`,`reverted`) ' .
									'VALUES ' .
									'(NULL,\''.mysql_real_escape_string($change).'\',' .
									'\''.mysql_real_escape_string($change).'\',' .
									'\''.mysql_real_escape_string($heuristic).'\',' .
									((is_array($log))?'\''.mysql_real_escape_string($logt).'\',':'') .
									'\''.mysql_real_escape_string($reason).'\',' .
									'\''.mysql_real_escape_string($change).'\',' .
									'\''.mysql_real_escape_string($change).'\',' .
									'\''.mysql_real_escape_string($change).'\',0)';
								//echo 'Mysql query: '.$query."\n";
								if (!mysql_ping($mysql)) {
									$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
									if (!$mysql) { die('Could not connect: ' . mysql_error()); }
									if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }
								}
								mysql_query($query);
								//echo 'Mysql error: '.mysql_error()."\n";
								$mysqlid = mysql_insert_id();
								echo 'MySQL time: '.(microtime(1) - $mqtime).' MySQL id: '.$mysqlid."\n";
								unset($mqtime);
								if (
									(
										(
											(preg_match('/(assisted|manual)/iS',$status))
											and (print('Revert ? '))
											and (strtolower(substr(fgets($stdin,3),0,1)) == 'y')
										)
										or (
											(preg_match('/(read-write|rw|go|approved|trial)/iS',$status))
										)
									)
									and ( /*ANGRY MODE*/ false or
										(
											(
												((time() - $tfas) < 1800)
												or (
													(preg_match('/\(\'\'\'\*)\|more...\]\]\'\'\'\)/iU',$wpq->getpage('Misplaced Pages:Today\'s featured article/'.date('F j, Y')),$tfam))
													and ($tfas = time())
													and ($tfa = $tfam)
													and ((print("TFA: ".$tfa."\n")) or (true))
												)
											)
											and ($tfa == $change)
										)
										or (
											(preg_match('/\* \,'/').')\]\] \- .*/i',$aoptin))
											and ((fwrite($irc,'PRIVMSG '.$ircdebugchannel.' :Angry-reverting .']].'."\n")) or (true))
										)
										or (
											(($tmp = unserialize(file_get_contents('titles.txt'))) or true)
											and ((!isset($tmp.$change])) or ((time() - $tmp.$change]) > (24*60*60)))
											and ($tmp.$change] = time())
											and ((file_put_contents('titles.txt',serialize($tmp))) or true)
										)
									)
								) {
									echo 'Reverting ...'."\n";
									if ($change != 'move') {
										$rev = $wpapi->revisions($change,5,'older',false,null,true,true);
										$revid = 0;
										$rbtok = $rev;
										foreach ($rev as $revdata) {
											if ($revdata != $change) {
												$revid = $revdata;
												break;
											}
										}
										if (($revdata == $user) or (in_array($revdata,explode(',',$botfriends)))) { die(); /* Do not revert to us. */ }
									}
//									if ($revid == 0) { die(); }
									foreach (explode(',',$ircdebugchannel) as $y) {
										fwrite($irc,'PRIVMSG '.$y.' :Reverting ...'."\n");
									}
//									$revisiondata = $wpapi->revisions($change,1,'older',true,$revid);
//									if (!$revisiondata) die();
//									if (!$rv1) $rv1 = $rv;
//									$wpi->post(
//										$change,
//										$revisiondata,
//										'Reverting possible vandalism by .'|'.$change.']] ' .
//										'to version by '.$revisiondata.'. ' .
//										'False positive? ]. '.
//										'Thanks, ]. ('.$mysqlid.') (Bot)',
//										false,
//										$rv1
//									); /* Revert the page */
									if ($change != 'move') {
										if (!$rbtok) {
											$d = $wpi->diff($change,$change,$change);
											$rbtok = $d;
										}
										$rbret = $wpapi->rollback(
											$change,
											$change,
											'Reverting possible vandalism by .'|'.$change.']] ' .
											'to '.(($revid == 0)?'older version':'version by '.$revdata).'. ' .
											'False positive? ]. '.
											'Thanks, ]. ('.$mysqlid.') (Bot)',
											$rbtok
										);
									} else {
										$rbret = $wpapi->move(
											$change,
											$change,
											'Reverting possible vandalism by .'|'.$change.']] ' .
											'to '.(($revid == 0)?'older version':'version by '.$revdata).'. ' .
											'False positive? ]. '.
											'Thanks, ]. ('.$mysqlid.') (Bot)'
										);
									}
//
// 									$rv2 = $wpapi->revisions($change,1);
//									if ($rv2 == $user) {
									if ($rbret !== false) {
										foreach (explode(',',$ircdebugchannel) as $y) {
											fwrite($irc,'PRIVMSG '.$y.' :Reverted. ('.(microtime(1) - $messagereceived).' s)'."\n");
										}
 										$warning = 0;
										$tpcontent = $wpq->getpage('User talk:'.$change);
										if (preg_match_all('/<!-- Template:(uw-*(\d)(im)?|Blatantvandal \(serious warning\)) -->.*(\d{2}):(\d{2}), (\d+) (+) (\d{4}) \(UTC\)/iU',
											$tpcontent,
											$match,PREG_SET_ORDER)
										) {
											foreach ($match as $m) {
												$month = array('January' => 1, 'February' => 2, 'March' => 3, 
													'April' => 4, 'May' => 5, 'June' => 6, 'July' => 7, 
													'August' => 8, 'September' => 9, 'October' => 10, 
													'November' => 11, 'December' => 12
												);
												if ($m == 'Blatantvandal (serious warning)') $m = 4;
												if ((time() - gmmktime($m,$m,0,$month],$m,$m)) <= (2*24*60*60)) {
													if ($m > $warning) { $warning = $m; }
												}
											}
										}
										$warning++;
										if ($warning == 5) { /* Report them if they have been warned 4 times. */
											$aivdata = $wpq->getpage('Misplaced Pages:Administrator_intervention_against_vandalism/TB2');
											if (!preg_match('/'.preg_quote($change,'/').'/i',$aivdata)) {
												foreach(explode(',',$ircaivchannel) as $y) {
													fwrite($irc,'PRIVMSG '.$y.' :!admin Reporting .']] to ]. Contributions: .']] Block: .']]'."\n");
												}
												foreach (explode(',',$ircvandalismchannel) as $y) {
													fwrite($irc,'PRIVMSG '.$y.' :rcbot bl add '.$change.' x='.(24*$warning).' r=Vandalism to .']] (#'.$warning.").\n");
												}
												$wpapi->edit(
													'Misplaced Pages:Administrator_intervention_against_vandalism/TB2',
													$aivdata .
													"\n\n* {{".((long2ip(ip2long($change)) == $change)?'IPvandal':'Vandal').'|'.$change.'}}' .
													' - '.$report." (Automated) ~~~~\n",
													'Automatically reporting .']]. (bot)',
													false,
													false
												);
											} else {
												foreach (explode(',',$ircreportchannel) as $y) {
													fwrite($irc,'PRIVMSG '.$y.' :!admin .']] has vandalized at least one time while being listed on ].  Contributions: .']] Block: .']]'."\n");
												}
											}
										} elseif ($warning < 5) { /* Warn them if they haven't been warned 4 times. */
											foreach (explode(',',$ircvandalismchannel) as $y) {
												fwrite($irc,'PRIVMSG '.$y.' :rcbot bl add '.$change.' x='.(24*$warning).' r=Vandalism to .']] (#'.$warning.').'."\n");
											}
											$wpapi->edit(
												'User talk:'.$change,
												$tpcontent."\n\n" .
												'{{subst:User:'.$user.'/Warnings/Warning|1='.$warning.'|2='.str_replace('File:',':File:',$change).'|3='.$report.' <!{{subst:ns:0}}-- MySQL ID: '.$mysqlid.' --{{subst:ns:0}}>}} ~~~~'."\n",
												'Warning .'|'.$change.']] - #'.$warning,
												false,
												false
											); /* Warn the user */
										} else { /* They have already been reported ... do nothing */
										}
										if (!mysql_ping($mysql)) {
											$mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass);
											if (!$mysql) { die('Could not connect: ' . mysql_error()); }
											if (!mysql_select_db($mysqldb, $mysql)) { die ('Can\'t use database : ' . mysql_error()); }
										}
										mysql_query('UPDATE `vandalism` SET `reverted` = 1 WHERE `id` = \''.mysql_real_escape_string($mysqlid).'\'');
									} else {
										$rv2 = $wpapi->revisions($change,1);
										if ($change != $rv2) {
											echo 'Grr! Beaten by '.$rv2.".\n";
											foreach(explode(',',$ircdebugchannel) as $y) {
												fwrite($irc,'PRIVMSG '.$y.' :Grr! Beaten by '.$rv2.".\n");
											}
											if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
											mysql_query('INSERT INTO `beaten` (`id`,`article`,`diff`,`user`) VALUES (NULL,\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($rv2).'\')');
										}
									}
								} else {
									foreach (explode(',',$ircunrevertedchannels) as $y) {
										fwrite($irc,'PRIVMSG '.$y.' :'."\002\00304Possible ignored vandalism: \002\003."\003]]\00304 changed by \003."\003]] \00303".$reason."\00304 on \00307".$rv."\003(\002\00313".$s."\003).\n");
										fwrite($irc,'PRIVMSG '.$y.' :'."\002(\002\00312 http://en.wikipedia.org/search/?title=".urlencode($change)."&action=history \003\002|\002\00312 ".$change." \003\002)\002"."\n");
									}
									$vandalpage = $wpq->getpage('User:'.$user.'/PossibleVandalism');
									$x = explode("\n\n",$vandalpage);
									foreach ($x as $k => $y) {
										if (preg_match('(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z',$y,$m)) {
											if ((gmtime() - gmmktime($m,$m,$m,$m,$m,$m)) > (5*60*60)) {
												unset($x);
											}
										}
									}
									$vandalpage = implode("\n\n",$vandalpage);
									$wpapi->edit('User:'.$user.'/PossibleVandalism',$vandalpage."\n\n".'Possible ignored vandalism: .']] changed by .']] '.$reason.' on '.$rv.' ('.$s.')'.' ).'&action=history hist] .' diff] ~~~~','Adding possible vandalism',true,true);
								}
							} else {
								$rev = $wpapi->revisions($change,1);
								$rev = $rev;
								echo 'Possible corrected vandalism: '.$change.' changed by '.$change.' '.$reason.'('.$s.")\n\tReverted by ".$rev." before I saw it.\n";
								foreach (explode(',',$ircdebugchannel) as $y) {
									fwrite($irc,'PRIVMSG '.$y.' :Possible corrected vandalism: '.$change.' changed by '.$change.' '.$reason.'('.$s.")\n");
									fwrite($irc,'PRIVMSG '.$y.' :Reverted by '.$rev." before I saw it.\n");
								}
								if (($rev != $user) and ($rev != $change)) {
									if (!mysql_ping($mysql)) { $mysql = mysql_pconnect($mysqlhost.':'.$mysqlport,$mysqluser,$mysqlpass); mysql_select_db($mysqldb, $mysql); }
									mysql_query('INSERT INTO `beaten` (`id`,`article`,`diff`,`user`) VALUES (NULL,\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($change).'\',\''.mysql_real_escape_string($rev).'\')');
								}
							}
						}
					}
					die();
				}
			}
		}
	}
?>


Heuristics

Config (cluebot.heuristics.config.php)

<?PHP
	$heuristics = 'grawp';
	$heuristics = 'evolution';
	$heuristics = 'avrillavigne';
	$heuristics = 'editsummary';
	$heuristics = 'pagereplace';
	$heuristics = 'pageblank';
	$heuristics = 'massdelete';
	$heuristics = 'massadd';
	$heuristics = 'tables';
	$heuristics = 'smallchange';
	$heuristics = 'claimjumperpete';
	$heuristics = 'sneaky';
	$heuristics = 'redirect';
?>

heuristics/cluebot.avrillavigne.heuristic.php

<?PHP
	if ( /* The Avril Lavigne vandal */
		(
			(preg_match('/avril\s*lavigne\s*roc*k**\s*my\s*soc*k**/i',$change))
			or (preg_match('/she\s*(is\s*)*(so\s*)*.*hot\!.*using.*TW/i',$change))
//			or (
//				($change == 'Template:')
//				and (preg_match('/\{\{user((\s|_)talk)?:/i',$wpq->getpage($change)))
//			)
		)
		and ($reason = 'Avril Lavigne vandal?')
	) {
		$heuristicret = true;
		foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Avril Lavigne vandal? ."]] .\n");
	}
?>

heuristics/cluebot.claimjumperpete.heuristic.php

<?PHP
	if ( /* The ClaimJumperPete vandal */
		(($change >= 100) and ($change <= 400))
		and (
			($change <= 200)
			or ($d = $wpi->diff($change,$change,$change))
		)
		and (
			(fnmatch("<!--*howdy y'all*-->*",trim(strtolower($d))))
			or (fnmatch("<!--*hello der*-->*",trim(strtolower($d))))
			or (fnmatch("<!--*happy editin' y'all*-->*",trim(strtolower($d))))
		)
		and ($reason = 'ClaimJumperPete?')
	) {
		$heuristicret = true;
		foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin ClaimJumperPete vandal? http://en.wikipedia.org/search/?title='.urlencode($change).'&diff=prev'.'&oldid='.urlencode($change)." .\n");
	}
?>

heuristics/cluebot.editsummary.heuristic.php

<?PHP
	if (
		(
			(fnmatch('*nimp*org*',strtolower($change)))
		)
		and ($reason = 'obscenities in edit summary')
	) {
		$heuristicret = true;
	}
?>

heuristics/cluebot.evolution.heuristic.php

<?PHP
	if ( /* The Evolution vandal */
		($change == 'Evolution')
		and (($pagedata = $wpq->getpage($change)) or true)
		and (fnmatch('*Genesis 1*The beginning*',$pagedata))
		and ($reason = 'replacing article with the Bible')
	) {
		$heuristicret = true;
		foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Evolution vandal? http://en.wikipedia.org/search/?title='.urlencode($change).'&diff=prev'.'&oldid='.urlencode($change)." .\n");
	}
?>

heuristics/cluebot.grawp.heuristic.php

<?PHP
	if ( /* The Grawp vandal */
		(
			(fnmatch('*epic*lulz*on*nimp*org*',strtolower($change)))
			or (fnmatch('*on*nimp*org*epic*lulz*',strtolower($change)))
			or (fnmatch('*punishing*wikipedia*',strtolower($change)))
			or (fnmatch('*anti*avril*hate*campaign*',strtolower($change)))
			or (fnmatch('*HAGGER*',$change))
			or (fnmatch('*H?A?G?G?E?R*',$change))
			or (fnmatch('*h??a??g??g??e??r*',strtolower($change)))
			or (fnmatch('*grawp*cock*',strtolower($change)))
			or (fnmatch('*massive*cock*',strtolower($change)))
			or (fnmatch('*grawp*dick*',strtolower($change)))
			or (fnmatch('*massive*dick*',strtolower($change)))
			or (fnmatch('*H?A?G?E?R*',$change))
			or (fnmatch('*hgger*',strtolower($change)))
		)
		and ($reason = 'Grawp?')
	) {
		$heuristicret = true;
		foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Grawp vandal? ."]] .\n");
	}
?>

heuristics/cluebot.massadd.heuristic.php

<?PHP
	if ( /* Massive additions */
		($change >= 7500)
		and ($rv = $wpapi->revisions($change,2,'older',true,$change))
		and ($pagedata = $wpq->getpage($change))
		and ($s = score($scorelist,$rv))
		and ($s += (score($scorelist,$rv)) * -1)
		and ($s < -1000)
		and ($reason = 'score equals '.$s)
	) $heuristicret = true;
?>

heuristics/cluebot.massdelete.heuristic.php

<?PHP
	if ( /* Massive deletes */
		($change <= -7500)
		and ($pagedata = $wpq->getpage($change))
		and (!fnmatch('*#REDIRECT*',strtoupper(substr($pagedata,0,9))))
		and ($rv = $wpapi->revisions($change,2,'older',true,$change))
		and ($s = score($scorelist,$rv))
		and ($s += (score($scorelist,$rv)) * -1)
		and ($s < -50) /* There are times when massive deletes are ok. */
		and ($reason = 'deleting '.($change * -1).' characters')
	) $heuristicret = true;
?>

heuristics/cluebot.pageblank.heuristic.php

<?PHP
	if ( /* Page blanks */
		(preg_match('/\,$m))
		and (($pagedata = $wpq->getpage($change)) or true)
		and ($fc = $wpapi->revisions($change,1,'newer'))
		and ($fc != $change) /* The creator is allowed to blank the page. */
		and ($reason = 'blanking the page')
	) $heuristicret = true;
?>

heuristics/cluebot.pagereplace.heuristic.php

<?PHP
	if ( /* Page replaces */
		(preg_match('/\\]Replaced page with (.*)$/',$change,$m))
		and ($pagedata = $wpq->getpage($change))
		and ($fc = $wpapi->revisions($change,1,'newer'))
		and ($fc != $change) /* The creator is allowed to replace the page. */
		and ($reason = 'replacing entire content with something else')
	) $heuristicret = true;
?>

heuristics/cluebot.redirect.heuristic.php

<?PHP
	if ( /* The Redirect vandals */
		(
			($tfa == $change)
			and (fnmatch('*#redirect *',strtolower($wpq->getpage($change))))
			and ($reason = 'redirecting featured article to new title')
		)
		or (
			($pagedata = $wpq->getpage($change))
			and (substr(trim(strtolower($pagedata)),0,10) == '#redirect ')
			and (preg_match('/\\]/',$pagedata,$m))
			and (!$wpq->getpage($m))
			and ($reason = 'redirecting article to non-existant page')
		)
	) {
		$heuristicret = true;
//		fwrite($irc,'PRIVMSG #cvn-wp-en :!admin Grawp vandal? http://en.wikipedia.org/Special:Contributions/'.$change." .\n");
	}
?>

heuristics/cluebot.smallchange.heuristic.php

<?PHP
	unset($log,$log2);
	if ( /* Small changes with obscenities. */
		(($change >= -200) and ($change <= 200))
		and (($d = $wpi->diff($change,$change,$change)) or true)
		and ((($change == 'User:ClueBot/Sandbox') and print_r($rv)) or true)
		and (($s = score($obscenelist,$d,$log)) or true)
		and (($s -= score($obscenelist,$d,$log2)) or true)
		and (
			(
				($s < -5) /* There are times when small changes are ok. */
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and (!fnmatch('*SEX*',strtoupper($rv)))
				and (!fnmatch('*BDSM*',strtoupper($rv)))
				and (score($obscenelist,$change) >= 0)
				and (score($obscenelist,$rv) >= 0)
				and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and ($heuristic .= '/obscenities')
				and ($reason = 'making a minor change with obscenities')
			)
			or (
				($s > 5)
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and (preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and ($heuristic .= '/censor')
				and ($reason = 'making a minor change censoring content (])')
			)
			or (
				(preg_match('/\!\!\!/S',$d))
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!preg_match('/\!\!\!/S',$rv))
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and ($heuristic .= '/exclamation')
				and ($reason = 'making a minor change adding "!!!"')
			)
		)
	) { $heuristicret = true; if (isset($log2) and is_array($log2)) foreach ($log2 as $k => $v) $log -= $v; if (isset($log) and is_array($log)) foreach ($log as $k => $v) if ($v == 0) unset($log); unset($log2); /* fwrite($irc,'PRIVMSG #wikipedia-BAG/ClueBot :Would revert http://en.wikipedia.org/search/?title='.urlencode($change.$change).'&diff=prev'.'&oldid='.urlencode($change)." .\n"); */ }
?>

heuristics/cluebot.sneaky.heuristic.php

<?PHP
	unset($log,$log2);
	if ( /* Small changes with obscenities. */
		(($change >= -200) and ($change <= 200))
		and (($d = $wpi->diff($change,$change,$change)) or true)
		and ((($change == 'User:ClueBot/Sandbox') and print_r($rv)) or true)
		and (($s = score($obscenelist,$d,$log)) or true)
		and (($s -= score($obscenelist,$d,$log2)) or true)
		and (
			(
				($s < -5) /* There are times when small changes are ok. */
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and (!fnmatch('*SEX*',strtoupper($rv)))
				and (!fnmatch('*BDSM*',strtoupper($rv)))
				and (score($obscenelist,$change) >= 0)
				and (score($obscenelist,$rv) >= 0)
				and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and ($heuristic .= '/obscenities')
				and ($reason = 'making a minor change with obscenities')
			)
			or (
				($s > 5)
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and (!preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and (preg_match('/(^|\s)({1,2}(\*+|\-{3,}){0,2}|\*{4}|\-{4}|(\<|\()?censored(\>|\))?)(ing?|ed)?(\s|$)/iS',$rv))
				and ($heuristic .= '/censor')
				and ($reason = 'making a minor change censoring content (])')
			)
			or (
				(preg_match('/\!\!\!/S',$d))
				and (($rv = $wpapi->revisions($change,2,'older',true,$change)) or true)
				and (!preg_match('/\!\!\!/S',$rv))
				and (!fnmatch('*#REDIRECT*',strtoupper(substr($rv,0,9))))
				and ($heuristic .= '/exclamation')
				and ($reason = 'making a minor change adding "!!!"')
			)
		)
	) { $heuristicret = true; if (isset($log2) and is_array($log2)) foreach ($log2 as $k => $v) $log -= $v; if (isset($log) and is_array($log)) foreach ($log as $k => $v) if ($v == 0) unset($log); unset($log2); /* fwrite($irc,'PRIVMSG #wikipedia-BAG/ClueBot :Would revert http://en.wikipedia.org/search/?title='.urlencode($change.$change).'&diff=prev'.'&oldid='.urlencode($change)." .\n"); */ }
?>

heuristics/cluebot.tables.heuristic.php

<?PHP
	if ( /* Massive tables */
		($change >= 7500)
		and ($rv = $wpapi->revisions($change,2,'older',true,$change))
		and (substr_count(strtolower($rv),'<td') > 300)
		and ($reason = 'adding huge, browser-crashing tables')
	) $heuristicret = true;
?>

Score list

<?PHP
	/*
	 * This page contains bad words out of necessity.
	 * Here is 50 lines of whitespace before the actual list:
	 * (scroll down to see the list)
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 *
	 * Here is the list:
	 */
	$obscenelist = Array
		(
			/* 'preg'				=> points, */
			'/suck/i'				=> -5,   /* Usually bad words */
			'/stupid/i'				=> -3,
			'/haha/i'				=> -5,
			'/\bomg/i'				=> -3,
			'/\bpimp\b/i'				=> -7,
			'/1337/i'				=> -5,
			'/leet/i'				=> -5,
			'/dumb/i'				=> -5,
			'/\bputa\b/i'				=> -7,
			'/\bhomo\b/i'				=> -7,
			'/\bGAY\b/'				=> -10,
			'/\bslut/i'				=> -5,
			'/damn/i'				=> -5,
			'/\bass\b/i'				=> -10,
			'/\brape\b/i'				=> -7,
			'/\bpoop\b/i'				=> -10,
			'/\bcock\b/i'				=> -10,
			'/\blol\b/i'				=> -7,
			'/\bcrap\b/i'				=> -5,
			'/\bsex\b/i'				=> -5,
			'/noob/i'				=> -5,
			'/\bnazi\b/i'				=> -3,
			'/\bneo-nazi\b/i'			=> +3,   /* False-positive */
			'/fuck/i'				=> -20,  /* Stronger bad words */
			'/\\]/'			=> +20,  /* This one is a false positive */
			'/bitch/i'				=> -20,
			'/\bpussy\b/i'				=> -20,
			'/penis/i'				=> -20,
			'/Penisula/'				=> +20,  /* False Positive */
			'/vagina/i'				=> -20,
			'/whore/i'				=> -15,
			'/\bshit\b/i'				=> -20,
			'/nigger/i'				=> -20,
			'/\bnigga\b/i'				=> -20,
			'/cocksucker/i'				=> -20,
			'/assrape/i'				=> -15,
			'/motherfucker/i'			=> -20,
			'/wanker/i'				=> -20,
			'/\bcunt\b/i'				=> -20,
			'/faggot/i'				=> -20,
			'/fags/i'				=> -20,
			'/asshole/i'				=> -15,
			'/fuck ((yo)?u|h(er|im)|them|it)/i'	=> -100, /* This looks like a personal attack */
			'/((yo)?u|s?he|we|they|it) sucks?/i'	=> -100, /* This looks like a personal attack */
			'/666+\b/i'				=> -50   /* Though this has uses, it is commonly used by vandals */
		);
	$grammarlist = Array
		(
			'/(.{1,4})\1{30}/'			=> -10,  /* Ugg .. the same letter(s) several times in a row. */
			'/\b.*\b/U'			=> +2,   /* This looks to be a correct sentence */
			'/\b{30,}\b/U'		=> -10,  /* All capitals? Looks like vandal activity */
			'/\b{1500,}\b/U'			=> -10,  /* No capitals? Looks like vandal activity */
			'/!{5,}/i'				=> -10,  /* No wikipedia article needs '!!!!!' in it */
			'/!!+1+(one)*/i'			=> -30,  /* No wikipedia article needs '!!!11one' in it */
			'/\\]/U'				=> +1,   /* Wiki links are good. */
			'/\{\{.*\}\}/U'				=> +5,   /* Wiki transcludes are good. */
			'/\{\{nfobox .*\}\}/U'		=> +20,  /* Wiki infoboxes are good. */
			'/\\]/iU'		=> +3    /* Wiki categories are good. */
		);
	$scorelist = array_merge($obscenelist,$grammarlist);
?>


ClueBot (talk) 13:01, 27 November 2010 (UTC)