Jump to content
 







Main menu
   


Navigation  



Main page
Contents
Current events
Random article
About Wikipedia
Contact us
Donate
 




Contribute  



Help
Learn to edit
Community portal
Recent changes
Upload file
 








Search  

































Create account

Log in
 









Create account
 Log in
 




Pages for logged out editors learn more  



Contributions
Talk
 



















Contents

   



(Top)
 


1 Classes (wikibot.classes.php)  





2 Diff function (diff.function.php)  





3 Source to ClueBot  





4 Heuristics  



4.1  Config (cluebot.heuristics.config.php)  





4.2  heuristics/cluebot.avrillavigne.heuristic.php  





4.3  heuristics/cluebot.claimjumperpete.heuristic.php  





4.4  heuristics/cluebot.editsummary.heuristic.php  





4.5  heuristics/cluebot.evolution.heuristic.php  





4.6  heuristics/cluebot.grawp.heuristic.php  





4.7  heuristics/cluebot.massadd.heuristic.php  





4.8  heuristics/cluebot.massdelete.heuristic.php  





4.9  heuristics/cluebot.pageblank.heuristic.php  





4.10  heuristics/cluebot.pagereplace.heuristic.php  





4.11  heuristics/cluebot.redirect.heuristic.php  





4.12  heuristics/cluebot.smallchange.heuristic.php  





4.13  heuristics/cluebot.sneaky.heuristic.php  





4.14  heuristics/cluebot.tables.heuristic.php  







5 Score list  














User:ClueBot/Source

















User page
Talk
 

















Read
View source
View history
 








Tools
   


Actions  



Read
View source
View history
 




General  



What links here
Related changes
User contributions
User logs
View user groups
Upload file
Special pages
Permanent link
Page information
Get shortened URL
Download QR code
 




Print/export  



Download as PDF
Printable version
 
















Appearance
   

 






From Wikipedia, the free encyclopedia
 

< User:ClueBot

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 Wikipedia'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[0]['*'];
  }

  /**
   * 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['pageid'];
  }

  /**
   * 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[0]['editcount'];
   return false;
  }
 }

 /**
  * This class is for interacting with Wikipedia'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['login']['result'] == 'Success')
    return true;
   if($x['login']['result'] == 'NeedToken') {
    $x = unserialize($this->http->post($this->apiurl.'?action=login&format=php',array('lgname' => $user, 'lgpassword' => $pass, 'lgtoken' => $x['login']['token'])));
    if($x['login']['result'] == 'Success')
     return true;
   }
   return false;
  }

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

  /**
   * 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['timestamp'] > 6*60*60) unset($this->tokencache[$t]);
   if (isset($this->tokencache[$title]) && (!$flush)) {
    return $this->tokencache[$title]['tokens'];
   } 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['query']['pages'] as $y) {
     $tokens['edittoken'] = $y['edittoken'];
     $tokens['deletetoken'] = $y['deletetoken'];
     $tokens['protecttoken'] = $y['protecttoken'];
     $tokens['movetoken'] = $y['movetoken'];
     $tokens['blocktoken'] = $y['blocktoken'];
     $tokens['unblocktoken'] = $y['unblocktoken'];
     $tokens['emailtoken'] = $y['emailtoken'];
     $this->tokencache[$title] = 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['query']['recentchanges'];
  }

  /**
   * This function returns search results from Wikipedia'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['query']['search'];
  }

  /**
   * 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['query']['logevents'];
  }

  /**
   * 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[in,out] $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['query-continue']['usercontribs']['ucstart'];
   return $x['query']['usercontribs'];
  }

  /**
   * 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['query']['pages']) or !is_array($x['query']['pages'])) {
     if ($dieonerror == true) die('No such page.'."\n");
     else return false;
    }
    foreach ($x['query']['pages'] as $data) {
     if (!isset($data['revisions']) or !is_array($data['revisions'])) {
      if ($dieonerror == true) die('No such page.'."\n");
      else return false;
     }
     foreach ($data['revisions'] as $data2) if ($data2['revid'] == $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['query']['pages'] as $key => $data) {
    $data['revisions']['ns'] = $data['ns'];
    $data['revisions']['title'] = $data['title'];
    $data['revisions']['currentuser'] = $x['query']['userinfo']['name'];
//    $data['revisions']['currentuser'] = $x['query']['userinfo']['currentuser']['name'];
    $data['revisions']['continue'] = $x['query-continue']['revisions']['rvstartid'];
    $data['revisions']['pageid'] = $key;
    return $data['revisions'];
   }
  }

  /**
   * 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[out] $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['query-continue']['allusers']['aufrom'];
   if (($requirestart == true) and ($x['query']['allusers'][0]['name'] != $start)) return false;
   return $x['query']['allusers'];
  }

  /**
   * Get members of a category.
   * @param $category Category to enumerate from.
   * @param $count Number of members to enumerate.  (Default 500)
   * @param[in,out] $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['query-continue']['categorymembers']['cmcontinue'];
   return $x['query']['categorymembers'];
  }

  /**
   * Enumerate all categories.
   * @param[in,out] $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['query-continue']['allcategories']['acfrom'];

   return $x['query']['allcategories'];
  }

  /**
   * Enumerate all backlinks to a page.
   * @param $page Page to search for backlinks to.
   * @param $count Number of backlinks to list.  (Default 500)
   * @param[in,out] $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['query-continue']['backlinks']['blcontinue'];
   return $x['query']['backlinks'];
  }

  /**
   * 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[in,out] $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['query-continue']['embeddedin']['eicontinue'];
   return $x['query']['embeddedin'];
  }

  /**
   * 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[in,out] $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['query-continue']['allpages']['apfrom'];
   return $x['query']['allpages'];
  }

  /**
   * 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['starttimestamp'] = $wpStarttime;
   if ($wpEdittime !== null) $params['basetimestamp'] = $wpEdittime;

   $x = $this->http->post($this->apiurl,$params);
   $x = unserialize($x);
   var_export($x);
   if ($x['edit']['result'] == 'Success') return true;
   if ($x['error']['code'] == '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['movetoken'],
    '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[0]['user'] == $user) {
     $token = $token[0]['rollbacktoken'];
    } 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['rollback']['summary']) and isset( $x[ 'rollback' ][ 'revid' ] ) and $x[ 'rollback' ][ 'revid' ] )
    ? true
    : false;
  }
 }

 /**
  * This class is for interacting with Wikipedia's browser interface, index.php.
  * Many of these functions are deprecated.
  **/
 class wikipediaindex {
  private $http;
  public $indexurl = 'http://en.wikipedia.org/w/index.php';
  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[0]['*']) $rv[0]['*'] = $wpq->getpage($page);

   //Fake the edit form.
   $now = gmdate('YmdHis', time());
   $token = htmlspecialchars($this->edittoken);
   $tmp = date_parse($rv[0]['timestamp']);
   $edittime = gmdate('YmdHis', gmmktime($tmp['hour'],$tmp['minute'],$tmp['second'],$tmp['month'],$tmp['day'],$tmp['year']));
   $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[0]['*'])) { return false; }  /* Honor the bots flags */
   if (preg_match('/'.preg_quote('{{bots|allow=none}}','/').'/iS',$rv[0]['*'])) { return false; }
   if (preg_match('/'.preg_quote('{{bots|deny=all}}','/').'/iS',$rv[0]['*'])) { return false; }
   if (preg_match('/'.preg_quote('{{bots|deny=','/').'(.*)'.preg_quote('}}','/').'/iS',$rv[0]['*'],$m)) { if (in_array(explode(',',$m[1]),$user)) { return false; } } /* /Honor the bots flags */
   if (!preg_match('/'.preg_quote($user,'/').'/iS',$rv['currentuser'])) { return false; } /* We need to be logged in */
//   if (preg_match('/'.preg_quote('You have new messages','/').'/iS',$rv[0]['*'])) { 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['wpSection'] = '';
   $post['wpScrolltop'] = '';
   if ($minor == true) { $post['wpMinoredit'] = 1; }
   $post['wpTextbox1'] = $data;
   $post['wpSummary'] = $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['wpStarttime'] = $m[1];
   preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEdittime\" /\>|U',$html,$m);
   $post['wpEdittime'] = $m[1];
   preg_match('|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEditToken\" /\>|U',$html,$m);
   $post['wpEditToken'] = $m[1];
   preg_match('|\<input name\=\"wpAutoSummary\" type\=\"hidden\" value\=\"(.*)\" /\>|U',$html,$m);
   $post['wpAutoSummary'] = $m[1];
   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 ([^ ]*): ([0-9.-]+) seconds lagged/S',$x,$lagged)) {
     global $irc;
     if (is_resource($irc)) {
      global $irctechchannel;
      foreach(explode(',',$irctechchannel) as $y) {
       fwrite($irc,'PRIVMSG '.$y.' :'.$lagged[1].' is lagged out by '.$lagged[2].' seconds. ('.$lagged[0].')'."\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[0][2]) and (is_numeric($oldid))) or (($id != $m[1][2]) and (is_numeric($id)))) {
     if ($wait == true) {
      sleep(1);
      return $this->diff($title,$oldid,$id,false);
     } else {
      echo 'OLDID as detected: '.$m[0][2].' Wanted: '.$oldid."\n";
      echo 'NEWID as detected: '.$m[1][2].' 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[2]))."\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[2]))."\n";
    }
   }

   //echo $added."\n".$deleted."\n";

   if (preg_match('/action\=rollback\&amp\;from\=.*\&amp\;token\=(.*)\"/US', $html, $m)) {
    $rbtoken = $m[1];
    $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[0]['user'] == $user) {
//     echo 'Token: '; print_r($token); echo "\n\n";
     $token = $token[0]['rollbacktoken'];
    } 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[$key]);
    elseif ($nret) $out[$key] = '> '.$line;
    else $out[$key] = '< '.$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[$preg] = $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[0])] = trim($tmpline[1]); } }

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

 unset($tmp,$tmpline);

 $stalkbots = array();
 $trialbots = explode("\n",$wpq->getpage('Wikipedia:Bots/Requests for approval'));
 foreach ($trialbots as $trialbot) if (preg_match('/\{\{BRFA\|(.*)\|.*\|Trial\}\}/',str_replace(array("\n","\r"),'',$trialbot),$m)) $stalkbots[$m[1]] = 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 Wikipedia 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[0]) == 'ping') {
    fwrite($irc,'PONG '.$d[1]."\n");
   } elseif (($d[1] == '376') or ($d[1] == '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[1]) == 'privmsg') {
    if (substr($d[3],0,2) == ':!') {
     if (strtolower($d[2]) == '#wikipedia-en') { $tmp = explode('!',substr($d[0],1)); $cmd = 'NOTICE '.$tmp[0]; }
     elseif (strtolower($d[2]) == strtolower($user)) { $tmp = explode('!',substr($d[0],1)); $cmd = 'NOTICE '.$tmp[0]; }
     else { $cmd = 'PRIVMSG '.$d[2]; }
     switch (substr(strtolower($d[3]),2)) {
      case 'edit':
       if (preg_match("/\[\[(.*)\]\]/",$data,$m)) {
        $rv = $wpapi->revisions($m[1],1,'older');
        fwrite($irc,$cmd.' :[['.$m[1].']] http://en.wikipedia.org/w/index.php?title='.urlencode($m[1]).'&diff=prev' .
         '&oldid='.urlencode($rv[0]['revid']).' * '.$rv[0]['user'].' * '.$rv[0]['comment']."\n");
       } else {
        fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
       }
       break;
      case 'stalk':
       if (preg_match("/\[\[User:(.*)\]\]/",$data,$m)) {
        $uc = $wpapi->usercontribs($m[1],1);
        fwrite($irc,$cmd.' :[['.$uc[0]['title'].']] http://en.wikipedia.org/w/index.php?title='.urlencode($uc[0]['title']).'&diff=prev' .
         '&oldid='.urlencode($uc[0]['revid']).' * '.$m[1].' * '.$uc[0]['comment']."\n");
       } else {
        fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
       }
       break;
      case 'beaten':
       if (preg_match("/\[\[User:(.*)\]\]/",$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[1]).'\' 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['count']).'\' LIMIT 1'));
        $z = mysql_fetch_assoc(mysql_query('SELECT FOUND_ROWS() as `ahead`'));
        fwrite($irc,$cmd.' :[[User:'.$m[1].']] has beaten me '.(($x['count'] != '')?$x['count']:'0').' times.  There are '.$z['ahead'].' 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[$m[1]])) {
         fwrite($irc,$cmd.' :[['.$m[1].']] has been vandalized '.count($tmp[$m[1]]).' time(s) in the last 48 hours.'."\n");
        } else {
         fwrite($irc,$cmd.' :[['.$m[1].']] 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[$title]);
       }
       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[$title]);
        }
       }
       file_put_contents('titles.txt',serialize($titles));
       $tcount = count($titles);

       foreach ($ov as $x => $y) {
        $ocount[$x] = count($y);
       }
       arsort($ocount);
       foreach ($ocount as $x => $y) {
        $mova = $x;
        $movacount = $y;
        break;
       }

       preg_match('/\(\'\'\'\[\[([^|]*)\|more...\]\]\'\'\'\)/iU',$wpq->getpage('Wikipedia:Today\'s featured article/'.date('F j, Y')),$tfa);
       $tfa = $tfa[1];
       
       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['user'].' ('.$x['count'].')';
       }
       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.' :[['.$mova.']] is the most vandalized page with a total of '.$movacount.' vandalisms in the last 48 hours.  ' .
        'Today\'s featured article is: [['.$tfa.']].'."\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[2].'.'."\n");

       unset($x,$y,$count,$ov,$tcount,$ocount,$mova,$movacount,$tfa,$run,$title,$titles,$time,$top5beat);
       break;
      case 'warninglevel':
       if (preg_match("/\[\[User:(.*)\]\]/",$data,$n)) {
        $warning = 0;
        if (preg_match_all('/<!-- Template:(uw-[a-z]*(\d)(im)?|Blatantvandal \(serious warning\)) -->.*(\d{2}):(\d{2}), (\d+) ([a-zA-Z]+) (\d{4}) \(UTC\)/iU',
         $wpq->getpage('User talk:'.$n[1]),
         $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[1] == 'Blatantvandal (serious warning)') $m[2] = 4;
          if ((time() - gmmktime($m[4],$m[5],0,$month[$m[7]],$m[6],$m[8])) <= (2*24*60*60)) {
           if ($m[2] > $warning) { $warning = $m[2]; }
          }
         }
        }
        fwrite($irc,$cmd.' :[[User:'.$n[1].']] is at warning level '.$warning.".\n");
       } else {
        fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
       }
       break;
      case 'count':
       if (preg_match("/\[\[User:(.*)\]\]/",$data,$n)) {
        fwrite($irc,$cmd.' :[[User:'.$n[1].']] has '.$wpq->contribcount($n[1])." contributions.\n");
       } else {
        fwrite($irc,$cmd.' :Couldn\'t find link.'."\n");
       }
       break;
      case 'help':
       fwrite($irc,$cmd.' :Please see [[User:'.$user.']].'."\n");
       break;
      case 'eval':
       $tmp = explode(' ',$data,6);
       $tmp1 = explode('!',substr($d[0],1));
       if ($d[4] == md5($thesecret.$tmp1[0].$tmp[5])) {
        eval($tmp[5]);
       } else {
        fwrite($irc,$cmd.' :Code incorrect.'."\n");
       }
       break;
      case 'cbproxy':
       $tmp = explode(' ',$data,6);
       $tmp1 = explode('!',substr($d[0],1));
       if ($tmp1[0] == 'ClueBot-Bopm') {
        foreach (explode(',',$ircproxychannels) as $y) {
         fwrite($irc,'PRIVMSG '.$y.' :!admin '.$tmp[5]."\n");
        }
        
        $data = $wpq->getpage('Wikipedia:WikiProject on open proxies');
        if (strpos($data,$tmp[4]) === false) {
         $header = explode('{{proxyip2|127.0.0.1}} || Example',$data,2);
         $header[0] .= '{{proxyip2|127.0.0.1}} || Example ';
         $footer = $header[1];
         $header = $header[0];
         $data = "\n".'{{proxyip2|'.$tmp[4].'}} || '.$tmp[5].' ~~~~'."\n";
         $data = $header.$data.$footer;
         unset($header,$footer);
         $wpapi->edit('Wikipedia:WikiProject on open proxies',$data,'Adding '.$tmp[4].'.');
         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 [[User:'.$user.'|'.$user."]].\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[$tmp3[0]] = trim($tmp3[1]); } }
 $tmp = explode("\n",$wpq->getpage('User:'.$owner.'/CBAutoedit.js'));
 foreach ($tmp as $tmp2) { if (substr($tmp2,0,1) != '#') { $tmp3 = explode('|',$tmp2,2); $edit[$tmp3[0]] = trim($tmp3[1]); } }
 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 Wikipedia 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[0]) == 'ping') {
    fwrite($feed,'PONG '.$linea[1]."\n");
   } elseif (($linea[1] == '376') or ($linea[1] == '422')) {
    fwrite($feed,'JOIN '.$feedchannel."\n");
   } elseif ((strtolower($linea[1]) == 'privmsg') and (strtolower($linea[2]) == strtolower($feedchannel))) {
    $message = substr($linea[3],1);
    if (preg_match('/^\[\[((Talk|User|Wikipedia|Image|MediaWiki|Template|Help|Category|Portal|Special)(( |_)talk)?:)?([^\x5d]*)\]\] (\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['namespace'] = $m[1];
     $change['title'] = $m[5];
     $change['flags'] = $m[6];
     $change['url'] = $m[7];
     $change['revid'] = $m[8];
     $change['old_revid'] = $m[9];
     $change['user'] = $m[10];
     $change['length'] = $m[12];
     $change['comment'] = $m[13];

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

     $stalkchannel = array();
     foreach ($stalk as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change['user']))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
     foreach ($stalkbots as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change['user']))) $stalkchannel = array_merge($stalkchannel,explode(',',$ircbagtrialchannels));
     foreach ($edit as $key => $value) if (myfnmatch(str_replace('_',' ',$key),str_replace('_',' ',$change['namespace'].$change['title']))) $stalkchannel = array_merge($stalkchannel,explode(',',$value));
//     if ($change['user'] == $owner) $stalkchannel[] = $ircchannel;

     $stalkchannel = array_unique($stalkchannel);

     foreach ($stalkchannel as $y) {
      fwrite($irc,'PRIVMSG '.$y.' :New edit: [['.$change['namespace'].$change['title'].']] http://en.wikipedia.org/w/index.php?title=' .
       urlencode($change['namespace'].$change['title']).'&diff=prev'.'&oldid='.urlencode($change['revid']).' * '.$change['user'] .
       ' * '.$change['comment']."\n");
     }
     
     if (($change['namespace'] == 'User:') or ($change['namespace'] == 'User talk:')) {
      if (strtolower($change['title']) == strtolower($user.'/Run')) { $run = $wpq->getpage('User:'.$user.'/Run'); }
      if (strtolower($change['title']) == strtolower($user.'/Whitelist')) { $wl = $wpq->getpage('User:'.$user.'/Whitelist'); }
      if (strtolower($change['title']) == strtolower($user.'/Optin')) { $optin = $wpq->getpage('User:'.$user.'/Optin'); }
      if (strtolower($change['title']) == strtolower($user.'/AngryOptin')) { $aoptin = $wpq->getpage('User:'.$user.'/AngryOptin'); }
      if (strtolower($change['title']) == 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[$tmp3[0]] = trim($tmp3[1]); } }
       unset($tmp,$tmp2,$tmp3);
       print_r($stalk);
      }
      if (strtolower($change['title']) == 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[$tmp3[0]] = trim($tmp3[1]); } }
       unset($tmp,$tmp2,$tmp3);
       print_r($edit);
      }
      if (strtolower($change['title']) == 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[0])] = trim($tmpline[1]); } }
       print_r($tmp);

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

       foreach ($tmpold as $tmp2 => $tmp3) if (isset($tmpnew[$tmp2])) unset($tmpold[$tmp2],$tmpnew[$tmp2]);
       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['ircchannel'];
       $ircdebugchannel = $tmp['ircdebugchannel'];
       $ircreportchannel = $tmp['ircreportchannel'];
       $ircvandalismchannel = $tmp['ircvandalismchannel'];
       $ircaivchannel = $tmp['ircaivchannel'];
       $irctechchannel = $tmp['irctechchannel'];
       $ircproxychannels = $tmp['ircproxychannels'];
       $ircunrevertedchannels = $tmp['ircunrevertedchannels'];
       $ircbagtrialchannels = $tmp['ircbagtrialchannels'];
       $ircotherchannels = $tmp['ircotherchannels'];

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

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

     $change['justtitle'] = $change['title'];
     $change['title'] = $change['namespace'].$change['title'];

     if ($change['flags'] == 'move') {
      if (preg_match('/moved \[\[(.*)\]\] to \[\[(.*)\]\]( over redirect)?: (.*)$/',$change['comment'],$m)) {
       $change['title'] = $m[1];
       $change['newtitle'] = $m[2];
       $change['realcomment'] = $change['comment'];
       $change['comment'] = $m[4];
       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('Wikipedia:Today\'s featured article/'.date('F j, Y')),$tfam))
     ) {
      $tfas = time();
      $tfa = $tfam[1];
      //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[$heuristic]++;
       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['title']."\0".$change['revid']."\0".'1'."\n",FILE_APPEND);*/ }
     else {
      $tmp = explode(' ',$rawline,4); $tmp = $tmp[3];
      $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['title'],$change['old_revid'],$change['revid']);
      $s = score($obscenelist,$d[0],$log);
      $s -= score($obscenelist,$d[1],$log);
//      if ($s >15) file_put_contents('trainingdata.txt',$change['title']."\0".$change['revid']."\0".'0'."\n",FILE_APPEND);
     }
     unset($hutime);

     if (
      ($heuristicret == true)
     ) {
      if (
       (
        ( /* IP users with 250 contributions are fine .. */
         (long2ip(ip2long($change['user'])) == $change['user'])
         /* and ($uc = $wpapi->usercontribs($change['user'],250))
         and (!isset($uc[249])) */
        )
        or ( /* Users with 50 contributions are fine .. */
         (long2ip(ip2long($change['user'])) != $change['user'])
         and ($wpq->contribcount($change['user']) <50)
        )
       )
       and ( /* Whitelisted users are ok. */
        /* ($wl = $wpq->getpage('User:'.$user.'/Whitelist'))
        and */ (!preg_match('/^\* \[\[User:('.preg_quote($change['user'],'/').')|\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[4],$m[5],$m[6],$m[2],$m[3],$m[1])) > (5*60*60)) {
//          unset($x[$k]);
//         }
//        }
//       }
//       $vandalpage = implode("\n\n",$x);
       $diff = 'http://en.wikipedia.org/w/index.php' .
        '?title='.urlencode($change['title']) .
        '&diff='.urlencode($change['revid']) .
        '&oldid='.urlencode($change['old_revid']);

       $report = '[['.str_replace('File:',':File:',$change['title']).']] was ' .
        (($change['flags'] != 'move')?'['.$diff.' changed] by ':'moved to [['.str_replace('File:',':File:',$change['newtitle']).']] by ') .
        '[[Special:Contributions/'.$change['user'].'|'.$change['user'].']] ' .
        '[[User:'.$change['user'].'|(u)]] ' .
        '[[User talk:'.$change['user'].'|(t)]] ' .
        $reason.' on '.gmdate('c');
//       $datatopost = $vandalpage."\n\n".'Possible [[WP:VAN|vandalism]]: '.$report." ~~~~\n";


       if ($s == null) {
//        $rv = $wpapi->revisions($change['title'],2,'older',true,$change['revid']);
//        $s = score($scorelist,diff($rv[1]['*'],$rv[0]['*']));
//        $s += (score($scorelist,diff($rv[1]['*'],$rv[0]['*'],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[$key1][$key2]); }
         }
        }
       }
       $tmp[$change['title']][] = time();
       if (count($tmp[$change['title']]) >= 30) {
        foreach (explode(',',$ircreportchannel) as $y) {
         fwrite($irc,'PRIVMSG '.$y.' :!admin [['.$change['title'].']] has been vandalized '.(count($tmp[$change['title']])).' times in the last 2 days.'."\n");
        }
       }
       file_put_contents('oftenvandalized.txt',serialize($tmp));

       if (
        (
         ($rv1 = $wpapi->revisions($change['title'],1,'older'))
         and ($rv1[0]['revid'] == $change['revid'])
        )
        or ($change['flags'] == 'move')
       ) { /* No need to continue further if it has been reverted */
        echo 'Possible vandalism: '.$change['title'].' changed by '.$change['user'].' '.$reason.' on '.$rv[0]['timestamp'].'('.$s.").\n";
        foreach (explode(',',$ircdebugchannel) as $y) {
         fwrite($irc,'PRIVMSG '.$y.' :Possible vandalism: '.$change['title'].' changed by '.$change['user'].' '.$reason.' on '.$rv[0]['timestamp'].'('.$s.").\n");
         fwrite($irc,'PRIVMSG '.$y.' :( http://en.wikipedia.org/w/index.php?title='.urlencode($change['title']).'&action=history | '.$change['url'].' )'."\n");
        }
        fwrite($pipe,'http://en.wikipedia.org/w/index.php?title='.urlencode($change['title']).'&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['user']).'\',' .
         '\''.mysql_real_escape_string($change['title']).'\',' .
         '\''.mysql_real_escape_string($heuristic).'\',' .
         ((is_array($log))?'\''.mysql_real_escape_string($logt).'\',':'') .
         '\''.mysql_real_escape_string($reason).'\',' .
         '\''.mysql_real_escape_string($change['url']).'\',' .
         '\''.mysql_real_escape_string($change['old_revid']).'\',' .
         '\''.mysql_real_escape_string($change['revid']).'\',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 [y/N]? '))
           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('Wikipedia:Today\'s featured article/'.date('F j, Y')),$tfam))
             and ($tfas = time())
             and ($tfa = $tfam[1])
             and ((print("TFA: ".$tfa."\n")) or (true))
            )
           )
           and ($tfa == $change['title'])
          )
          or (
           (preg_match('/\* \[\[('.preg_quote($change['title'],'/').')\]\] \- .*/i',$aoptin))
           and ((fwrite($irc,'PRIVMSG '.$ircdebugchannel.' :Angry-reverting [['.$change['title'].']].'."\n")) or (true))
          )
          or (
           (($tmp = unserialize(file_get_contents('titles.txt'))) or true)
           and ((!isset($tmp[$change['title'].$change['user']])) or ((time() - $tmp[$change['title'].$change['user']]) > (24*60*60)))
           and ($tmp[$change['title'].$change['user']] = time())
           and ((file_put_contents('titles.txt',serialize($tmp))) or true)
          )
         )
        ) {
         echo 'Reverting ...'."\n";
         if ($change['flags'] != 'move') {
          $rev = $wpapi->revisions($change['title'],5,'older',false,null,true,true);
          $revid = 0;
          $rbtok = $rev[0]['rollbacktoken'];
          foreach ($rev as $revdata) {
           if ($revdata['user'] != $change['user']) {
            $revid = $revdata['revid'];
            break;
           }
          }
          if (($revdata['user'] == $user) or (in_array($revdata['user'],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['title'],1,'older',true,$revid);
//         if (!$revisiondata[0]['*']) die();
//         if (!$rv1[0]['*']) $rv1[0]['*'] = $rv[0]['*'];
//         $wpi->post(
//          $change['title'],
//          $revisiondata[0]['*'],
//          'Reverting possible vandalism by [[Special:Contributions/'.$change['user'].'|'.$change['user'].']] ' .
//          'to version by '.$revisiondata[0]['user'].'. ' .
//          'False positive? [[User:'.$user.'/FalsePositives|Report it]]. '.
//          'Thanks, [[User:'.$user.'|]]. ('.$mysqlid.') (Bot)',
//          false,
//          $rv1
//         ); /* Revert the page */
         if ($change['flags'] != 'move') {
          if (!$rbtok) {
           $d = $wpi->diff($change['title'],$change['old_revid'],$change['revid']);
           $rbtok = $d[2];
          }
          $rbret = $wpapi->rollback(
           $change['title'],
           $change['user'],
           'Reverting possible vandalism by [[Special:Contributions/'.$change['user'].'|'.$change['user'].']] ' .
           'to '.(($revid == 0)?'older version':'version by '.$revdata['user']).'. ' .
           'False positive? [[User:'.$user.'/FalsePositives|Report it]]. '.
           'Thanks, [[User:'.$user.'|'.$user.']]. ('.$mysqlid.') (Bot)',
           $rbtok
          );
         } else {
          $rbret = $wpapi->move(
           $change['newtitle'],
           $change['title'],
           'Reverting possible vandalism by [[Special:Contributions/'.$change['user'].'|'.$change['user'].']] ' .
           'to '.(($revid == 0)?'older version':'version by '.$revdata['user']).'. ' .
           'False positive? [[User:'.$user.'/FalsePositives|Report it]]. '.
           'Thanks, [[User:'.$user.'|'.$user.']]. ('.$mysqlid.') (Bot)'
          );
         }
//
//          $rv2 = $wpapi->revisions($change['title'],1);
//         if ($rv2[0]['user'] == $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['user']);
          if (preg_match_all('/<!-- Template:(uw-[a-z]*(\d)(im)?|Blatantvandal \(serious warning\)) -->.*(\d{2}):(\d{2}), (\d+) ([a-zA-Z]+) (\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[1] == 'Blatantvandal (serious warning)') $m[2] = 4;
            if ((time() - gmmktime($m[4],$m[5],0,$month[$m[7]],$m[6],$m[8])) <= (2*24*60*60)) {
             if ($m[2] > $warning) { $warning = $m[2]; }
            }
           }
          }
          $warning++;
          if ($warning == 5) { /* Report them if they have been warned 4 times. */
           $aivdata = $wpq->getpage('Wikipedia:Administrator_intervention_against_vandalism/TB2');
           if (!preg_match('/'.preg_quote($change['user'],'/').'/i',$aivdata)) {
            foreach(explode(',',$ircaivchannel) as $y) {
             fwrite($irc,'PRIVMSG '.$y.' :!admin Reporting [[User:'.$change['user'].']] to [[WP:AIV]]. Contributions: [[Special:Contributions/'.$change['user'].']] Block: [[Special:Blockip/'.$change['user'].']]'."\n");
            }
            foreach (explode(',',$ircvandalismchannel) as $y) {
             fwrite($irc,'PRIVMSG '.$y.' :rcbot bl add '.$change['user'].' x='.(24*$warning).' r=Vandalism to [['.$change['title'].']] (#'.$warning.").\n");
            }
            $wpapi->edit(
             'Wikipedia:Administrator_intervention_against_vandalism/TB2',
             $aivdata .
             "\n\n* {{".((long2ip(ip2long($change['user'])) == $change['user'])?'IPvandal':'Vandal').'|'.$change['user'].'}}' .
             ' - '.$report." (Automated) ~~~~\n",
             'Automatically reporting [[Special:Contributions/'.$change['user'].']]. (bot)',
             false,
             false
            );
           } else {
            foreach (explode(',',$ircreportchannel) as $y) {
             fwrite($irc,'PRIVMSG '.$y.' :!admin [[User:'.$change['user'].']] has vandalized at least one time while being listed on [[WP:AIV]].  Contributions: [[Special:Contributions/'.$change['user'].']] Block: [[Special:Blockip/'.$change['user'].']]'."\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['user'].' x='.(24*$warning).' r=Vandalism to [['.$change['title'].']] (#'.$warning.').'."\n");
           }
           $wpapi->edit(
            'User talk:'.$change['user'],
            $tpcontent."\n\n" .
            '{{subst:User:'.$user.'/Warnings/Warning|1='.$warning.'|2='.str_replace('File:',':File:',$change['title']).'|3='.$report.' <!{{subst:ns:0}}-- MySQL ID: '.$mysqlid.' --{{subst:ns:0}}>}} ~~~~'."\n",
            'Warning [[Special:Contributions/'.$change['user'].'|'.$change['user'].']] - #'.$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['title'],1);
          if ($change['user'] != $rv2[0]['user']) {
           echo 'Grr! Beaten by '.$rv2[0]['user'].".\n";
           foreach(explode(',',$ircdebugchannel) as $y) {
            fwrite($irc,'PRIVMSG '.$y.' :Grr! Beaten by '.$rv2[0]['user'].".\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['title']).'\',\''.mysql_real_escape_string($change['url']).'\',\''.mysql_real_escape_string($rv2[0]['user']).'\')');
          }
         }
        } else {
         foreach (explode(',',$ircunrevertedchannels) as $y) {
          fwrite($irc,'PRIVMSG '.$y.' :'."\002\00304Possible ignored vandalism: \002\003[[\00312".$change['title']."\003]]\00304 changed by \003[[\00312User:".$change['user']."\003]] \00303".$reason."\00304 on \00307".$rv[0]['timestamp']."\003(\002\00313".$s."\003).\n");
          fwrite($irc,'PRIVMSG '.$y.' :'."\002(\002\00312 http://en.wikipedia.org/w/index.php?title=".urlencode($change['title'])."&action=history \003\002|\002\00312 ".$change['url']." \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[4],$m[5],$m[6],$m[2],$m[3],$m[1])) > (5*60*60)) {
            unset($x[$k]);
           }
          }
         }
         $vandalpage = implode("\n\n",$vandalpage);
         $wpapi->edit('User:'.$user.'/PossibleVandalism',$vandalpage."\n\n".'Possible ignored vandalism: [['.$change['title'].']] changed by [[User:'.$change['user'].']] '.$reason.' on '.$rv[0]['timestamp'].' ('.$s.')'.' [http://en.wikipedia.org/w/index.php?title='.urlencode($change['title']).'&action=history hist] ['.$change['url'].' diff] ~~~~','Adding possible vandalism',true,true);
        }

       } else {
        $rev = $wpapi->revisions($change['title'],1);
        $rev = $rev[0];

        echo 'Possible corrected vandalism: '.$change['title'].' changed by '.$change['user'].' '.$reason.'('.$s.")\n\tReverted by ".$rev['user']." before I saw it.\n";
        foreach (explode(',',$ircdebugchannel) as $y) {
         fwrite($irc,'PRIVMSG '.$y.' :Possible corrected vandalism: '.$change['title'].' changed by '.$change['user'].' '.$reason.'('.$s.")\n");
         fwrite($irc,'PRIVMSG '.$y.' :Reverted by '.$rev['user']." before I saw it.\n");
        }
        if (($rev['user'] != $user) and ($rev['user'] != $change['user'])) {
         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['title']).'\',\''.mysql_real_escape_string($change['url']).'\',\''.mysql_real_escape_string($rev['user']).'\')');
        }
       }
      }
     }
     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*[zs]*\s*my\s*soc*k*[zs]*/i',$change['comment']))
   or (preg_match('/she\s*(is\s*)*(so\s*)*.*hot\!.*using.*TW/i',$change['comment']))
//   or (
//    ($change['namespace'] == 'Template:')
//    and (preg_match('/\{\{user((\s|_)talk)?:/i',$wpq->getpage($change['title'])))
//   )
  )
  and ($reason = 'Avril Lavigne vandal?')
 ) {
  $heuristicret = true;
  foreach (explode(',',$ircreportchannel) as $y) fwrite($irc,'PRIVMSG '.$y.' :!admin Avril Lavigne vandal? [[Special:Contributions/'.$change['user']."]] .\n");
 }
?>

heuristics/cluebot.claimjumperpete.heuristic.php

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

heuristics/cluebot.editsummary.heuristic.php

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

heuristics/cluebot.evolution.heuristic.php

<?PHP
 if ( /* The Evolution vandal */
  ($change['title'] == 'Evolution')
  and (($pagedata = $wpq->getpage($change['title'])) 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/w/index.php?title='.urlencode($change['title']).'&diff=prev'.'&oldid='.urlencode($change['revid'])." .\n");
 }
?>

heuristics/cluebot.grawp.heuristic.php

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

heuristics/cluebot.massadd.heuristic.php

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

heuristics/cluebot.massdelete.heuristic.php

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

heuristics/cluebot.pageblank.heuristic.php

<?PHP
 if ( /* Page blanks */
  (preg_match('/\[\[WP:.*Blanked.*page/',$change['comment'],$m))
  and (($pagedata = $wpq->getpage($change['title'])) or true)
  and ($fc = $wpapi->revisions($change['title'],1,'newer'))
  and ($fc[0]['user'] != $change['user']) /* 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('/\[\[WP:.*\]\]Replaced page with (.*)$/',$change['comment'],$m))
  and ($pagedata = $wpq->getpage($change['title']))
  and ($fc = $wpapi->revisions($change['title'],1,'newer'))
  and ($fc[0]['user'] != $change['user']) /* 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['title'])
   and (fnmatch('*#redirect *',strtolower($wpq->getpage($change['title']))))
   and ($reason = 'redirecting featured article to new title')
  )
  or (
   ($pagedata = $wpq->getpage($change['title']))
   and (substr(trim(strtolower($pagedata)),0,10) == '#redirect ')
   and (preg_match('/\[\[(.*)\]\]/',$pagedata,$m))
   and (!$wpq->getpage($m[1]))
   and ($reason = 'redirecting article to non-existant page')
  )
 ) {
  $heuristicret = true;
//  fwrite($irc,'PRIVMSG #cvn-wp-en :!admin Grawp vandal? http://en.wikipedia.org/wiki/Special:Contributions/'.$change['user']." .\n");
 }
?>

heuristics/cluebot.smallchange.heuristic.php

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

heuristics/cluebot.sneaky.heuristic.php

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

heuristics/cluebot.tables.heuristic.php

<?PHP
 if ( /* Massive tables */
  ($change['length'] >= 7500)
  and ($rv = $wpapi->revisions($change['title'],2,'older',true,$change['revid']))
  and (substr_count(strtolower($rv[0]['*']),'<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 */
   '/\[\[Fucked\ Up\]\]/'   => +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[A-Z].*[.!?]\b/U'   => +2,   /* This looks to be a correct sentence */
   '/\b[A-Z][^a-z]{30,}\b/U'  => -10,  /* All capitals? Looks like vandal activity */
   '/\b[^A-Z]{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. */
   '/\{\{[iI]nfobox .*\}\}/U'  => +20,  /* Wiki infoboxes are good. */
   '/\[\[Category\:.*\]\]/iU'  => +3    /* Wiki categories are good. */
   
  );
 $scorelist = array_merge($obscenelist,$grammarlist);
?>


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


Retrieved from "https://en.wikipedia.org/w/index.php?title=User:ClueBot/Source&oldid=399131307"





This page was last edited on 27 November 2010, at 13:01 (UTC).

Text is available under the Creative Commons Attribution-ShareAlike License 4.0; additional terms may apply. By using this site, you agree to the Terms of Use and Privacy Policy. Wikipedia® is a registered trademark of the Wikimedia Foundation, Inc., a non-profit organization.



Privacy policy

About Wikipedia

Disclaimers

Contact Wikipedia

Code of Conduct

Developers

Statistics

Cookie statement

Mobile view



Wikimedia Foundation
Powered by MediaWiki