User:DYKBot/code
Appearance
This is the code for both DYKadminBot and DYKBot. It is a complete mess and very poorly coded, mostly because I had to make drastic changes in a very short space of time during its development. There are bugs that I haven't been able to resolve without causing new bugs. I am really hoping someone will pick up this code, fix it (or rewrite it) and run the bots. |
/var/www/DYKadminBot/
[edit]bot.php
[edit]<?php /******* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. *******/ $wpapi = new wikipediaapi; $wpq = new wikipediaquery; $wpi = new wikipediaindex; $whttp = new http; // login $wpapi->login($username,$password); // checks if the bot is enabled if not there is no point continuing $shutoff_switch = $wpq->getpage("User:DYKadminBot/sudo"); if(!preg_match("/^true$/",$shutoff_switch)) die("Bot is stopped. Check shutoff switch: $shutoff_switch"); // work out which queue we're working with $queue_count = $wpq->getpage("User:DYKadminBot/count"); // can it be update tiem now plz include("time.php"); /* see source of time.php for details */ // checks if the hooks for that queue have been approved if(!preg_match("/\{\{DYKbotdo/i",$queue)) die("Hooks have not been approved by an administrator."); // Get the contents of the timer page $addontime = $wpq->getpage("User:DYKadminBot/time"); if($addontime == ""){ $addontime = "21600"; } // Get a unix timestamp with X hours added $unix_plus_six = mktime($hour, $minute, $second, $month, $day, $year)+$addontime; // check if it is time to update if($current_time > $unix_plus_six){ // do nothing and let the bot continue } else { die("Template is not due for update."); } // Get the current formatting of the template $tdyk_content = $wpq->getpage("Template:Did_you_know"); // strip away the formatting and only get the hooks preg_match("/(<!--Hooks-->.*<!--HooksEnd-->)/s", $queue, $content); // Generate what will be added to the template $create_content = preg_replace("/<!--Hooks-->.*<!--HooksEnd-->/s", "$content[1]", "$tdyk_content"); // convert the html entity back into plain text $create_content = preg_replace("/$/","$",$create_content); include("/var/www/DYKBot/archive.php"); $wpapi->login($username,$password); $es = "Adminbot automatically updating DYK template with hooks copied from [[Template:Did_you_know/Queue/$queue_count|queue $queue_count]]"; // do the update $wpi->forcepost("Template:Did you know",$create_content,$es); // Update is done, now for housekeeping // Reset the clock $resetclock = preg_replace("@\d{14}@", "{{subst:CURRENTTIMESTAMP}}", "$dykclocktime"); $wpi->forcepost("Template:Did you know/Next update/Time", $resetclock, "Resetting the clock"); // increment the queue count by one $new_count = $queue_count+1; // if the queue count would become 6 it has to reset to 1 if($new_count == "6"){ $update_count = "1"; } else { $update_count = $new_count;} // do the credits include("/var/www/DYKBot/credit.php"); $wpapi->login($username,$password); // Clear the queue $wpi->forcepost("Template:Did you know/Queue/$queue_count","{{User:DYKadminBot/REMOVE THIS LINE}}","Update is done, removing the hooks."); // update the queue count page $wpi->forcepost("User:DYKadminBot/count", "$update_count", "Updating - the next queue is number $update_count"); // purge $whttp->get("http://en.wikipedia.org/w/api.php?action=purge&titles=Main_Page|Template:Did_you_know/Queue|Template:Did_you_know/Next_update&format=php"); echo "Update completed."; ?>
time.php
[edit]<?php /***** This file is part of DYKadminBot. DYKadminBot is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. DYKadminBot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DYKadminBot. If not, see <http://www.gnu.org/licenses/>. ******/ date_default_timezone_set(UTC); /* Get the contents of the DYK clock */ $dykclocktime = $wpq->getpage("Template:Did you know/Next update/Time"); /* Trim it so we only have the timestamp */ $dykclocktime_trimmed = preg_replace("#\D#", "", $dykclocktime); /* if the timestamp isn't 14 characters long then something is wrong */ if (!preg_match("#^\d{14}$#", $dykclocktime_trimmed)) die("Error: Timestamp does not appear to be correct."); /* Extract the parts of the timestamp */ preg_match("#(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})#", $dykclocktime_trimmed, $ts_extract); $year = $ts_extract[1]; $month = $ts_extract[2]; $day = $ts_extract[3]; $hour = $ts_extract[4]; $minute = $ts_extract[5]; $second = $ts_extract[6]; /* get a unix timestamp with 5 and 3/4 hours added */ $timewarp = mktime($hour, $minute, $second, $month, $day, $year)+20700; /* get a current unix timestamp */ $current_time = time(); /* if the current unix timestamp is greater than the timestamp for when the template should be updated then do nothing and let the bot continue, otherwise die */ if($current_time > $timewarp){ // do nothing - there is at least one hour to go until the update } else { die("Template is not due for update."); } // We've got this far so there must be at least one hour to go until the update // a function for making a post to [[WP:AN]] function postAN(){ global $wpapi, $wpi, $wpq, $queue_count; // Queue is not approved - notify AN include("messages.php"); // Check if there is already a post on AN if there is don't repost $AN_content = $wpq->getpage("Wikipedia:Administrators' noticeboard"); if (preg_match("/<!--DYKbotmsg-->/", $AN_content)) { echo "Already posted to AN"; } else { // post to AN $wpi->forcepost("Wikipedia:Administrators' noticeboard", "$AN_content$nb_message", "{{Sign3647}} Attention admins: DYK is almost late"); } echo "Update is almost late. AN Notified."; } $queue = $wpq->getpage("Template:Did_you_know/Queue/$queue_count"); $queue = preg_replace("/\\\$/","$",$queue); // are the hooks approved? If not there is at least an hour to go until the update so post to AN if(!preg_match("@\{\{DYKbotdo@i",$queue)) postAN(); ?>
messages.php
[edit]<?php $nb_message = "\n\n== DYK is almost overdue (remove this message once resolved) ==\n\nIn less than one hour [[Template:Did you know|Did you know]] will need to be updated, however the [[Template:Did you know/Queue/$queue_count|'''next queue''']] either has no hooks or has not been approved by an administrator. It would be much appreciated if an administrator would take the time to ensure that DYK is updated on time by following these instructions: # Check the [[Template:Did you know/Next update|'''Next update''']] if there are between 6-10 hooks on the page then it is probably good to go. If not move approved hooks from the [[Template talk:Did you know|'''suggestions page''']] and add them and the credits as required. # Once completed edit [[Template:Did you know/Queue/$queue_count|'''queue #$queue_count''']] and replace the page with the entire content from the next update # Add {{tl|DYKbotdo}} to the top of the page and save the page # When the next queue is good to go '''remove this entire message from the board''' Then, when the time is right I will be able to update the template. Thanks and have a good day, ~~~ <small>DYKadminBot is operated by [[User:Ameliorate!|Ameliorate!]] <sup>([[User talk:Ameliorate!|talk]])</sup></small> <!--DYKbotmsg-->"; ?>
runbot.php
[edit]<?php include 'config.php'; include 'clueclass.php'; include 'bot.php'; ?>
clueclass.php
[edit]<?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 () { $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); $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); $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]['*']; } function getsection0 ($page) { $this->checkurl(); $ret = $this->api->revisions_section0($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; 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) { $data = $this->http->post($this->apiurl.'?action=login',array('lgname' => $user, 'lgpassword' => $pass)); } /** * 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) { 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']; } } /** My modified version of the revisions function **/ function revisions_section0 ($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&rvsection=0&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']; } } /** ENDS **/ /** * 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,$reqirestart = 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']; } 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']; } 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']; } 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']; } 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']; } function edit ($page,$data,$summary = '',$minor = false,$bot = true) { $params = Array( 'action' => 'edit', 'format' => 'php', 'title' => $page, 'text' => $data, 'token' => $this->getedittoken(), 'summary' => $summary, ($minor?'minor':'notminor') => '1', ($bot?'bot':'notbot') => '1' ); $x = $this->http->post($this->apiurl,$params); $x = unserialize($x); var_export($x); } 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); } function rollback ($title,$user,$reason,$token = null) { if (($token == null) or ($token == '')) { $token = $wpapi->revisions($title,1,'older',false,null,true,true); 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 ); $x = $this->http->post($this->apiurl,$params); $x = unserialize($x); var_export($x); } } class wikipediaindex { private $http; public $indexurl = 'http://en.wikipedia.org/w/index.php'; private $postinterval = 0; private $lastpost; private $edittoken; function __construct () { global $__wp__http; if (!isset($__wp__http)) { $__wp__http = new http; } $this->http = &$__wp__http; } 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"; // $html.= "<input type='hidden' value=\"0\" name=\"wpSection\" />"; // 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; } 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); } } function forcepost_section0 ($page,$data,$summery = '',$minor = false,$edithtml = null,$maxlag = null,$mlkg = null,$bot = true) { $post['wpSection'] = '0'; $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§ion=0'); } 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§ion=0&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§ion=0&bot='.(($bot == true)?'1':'0'),$post); } } return $x; } else { return $this->http->post($this->indexurl.'?title='.urlencode($page).'&action=submit§ion=0&bot='.(($bot == true)?'1':'0'),$post); } } 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('/\&\;(oldid\=|undo=)(\d*)\\\'\>(Revision as of|undo)/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 { 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\&\;from\=.*\&\;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); } 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; } 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); } 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); } 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; } 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); } } ?>
config.php
[edit]<?PHP $username = "DYKadminBot"; // username of the bot account $password = ""; // the password for the account, removed here obviously ?>
/var/www/DYKBot/
[edit]archive.php
[edit]<?php // include("clueclass.php"); $wpapi = new wikipediaapi; $wpq = new wikipediaquery; $wpi = new wikipediaindex; $whttp = new http; /* DYKBot's archival process */ $wpapi->login("DYKBot","<PASSWORD>"); // password removed obviously $tdyk = $wpq->getpage("Template:Did you know"); $tdyk = preg_replace("/\\\$/","$",$tdyk); preg_match("/(<!--Hooks-->.*)<!--HooksEnd-->/s",$tdyk,$tdyk); preg_match("/\[\[(Image:.*?)\|/",$tdyk[1],$image); $tdyk = preg_replace("/<!--Hooks-->.*?<\/div>\n/s","",$tdyk[1]); $tdyk = preg_replace("/''\((.*?)\)''/","''([[:$image[1]|$1]])''",$tdyk); $wpra = $wpq->getpage("Wikipedia:Recent additions"); $replace = "----\n*'''''~~~~~'''''\n$tdyk"; $set = preg_replace("/----/",$replace,$wpra); $wpi->forcepost("Wikipedia:Recent additions",$set,"Automatically updating archive"); echo "Archived hooks."; ?>
credit.php
[edit]<?php /* include("clueclass.php"); $wpapi = new wikipediaapi; $wpq = new wikipediaquery; $wpi = new wikipediaindex; $whttp = new http; */ $wpapi->login("DYKBot","<PASSWORD>"); // password removed fill in with your own $queue_count = $wpq->getpage("User:DYKadminBot/count"); $q_content_notaint = $wpq->getpage("Template:Did you know/Queue/$queue_count"); $q_content_notaint = preg_replace("/\\\$/","$",$q_content_notaint); $q_content_notaint = preg_replace("/\(/","%28",$q_content_notaint); $q_content_notaint = preg_replace("/\)/","%29",$q_content_notaint); $q_content = preg_replace("/<!--.*-->/s","",$q_content_notaint); $q_content = preg_replace("/\{\{DYKmake\|Example\|Editor.*?\}\}/","",$q_content); preg_match("/\{\{DYKbotdo\|(.*?)\}\}/",$q_content_notaint,$signed); preg_match("/(User|User.?talk):(.*?)(\||\])/",$signed[1],$userextract); preg_match_all("/\{\{DYKmake\|.*?\|(.*?)(\|.*?)?\}\}/",$q_content,$authors); foreach($authors[1] as $key_a){ preg_match("/\{\{DYKmake\|(.*?)\|$key_a(\|.*?)?\}\}/",$q_content,$auth_article); // preg_match("/(\{\{\*mp\}\}.*?$auth_article[1].*?\?)/",$q_content_notaint,$hook); preg_match("/\{\{DYKmake\|$auth_article[1]\|$key_a\|(.*?)\}\}/",$q_content,$comment); $auth_title_fix = preg_replace("/%28/","(",$auth_article[1]); $auth_title_fix = preg_replace("/%29/",")",$auth_title_fix); $talk = $wpq->getpage("User talk:$key_a"); $header = "== DYK for $auth_title_fix =="; $make_template = "{{subst:User:Ameliorate!/DYKmake |article=$auth_title_fix |optional=$comment[1] }}"; if($talk == ""){ $talk = $wpq->getpage("User talk:$key_a"); } $signature = preg_replace("/%28/","(",$signed[1]); $signature = preg_replace("/%29/",")",$signature); $author_message = "$talk\n\n$header\n\n$make_template$signature ~~~~~"; echo $signed[1]; $wpi->forcepost("User talk:$key_a",$author_message,"Giving DYK credit for $auth_title_fix on behalf of [[User:$userextract[2]|$userextract[2]]]"); } preg_match_all("/\{\{DYKnom\|.*?\|(.*?)(\|.*?)?\}\}/",$q_content,$noms); // // split // foreach($noms[1] as $key_n){ preg_match("/\{\{DYKnom\|(.*?)\|$key_n(\|.*?)?\}\}/",$q_content,$nom_article); preg_match("/\{\{DYKnom\|$nom_article[1]\|$key_a\|(.*?)\}\}/",$q_content,$comment); // preg_match("/(\{\{\*mp\}\}.*?$nom_article[1].*?\?)/",$q_content_notaint,$hook); $nom_title_fix = preg_replace("/%28/","(",$nom_article[1]); $nom_title_fix = preg_replace("/%29/",")",$nom_title_fix); $talk = $wpq->getpage("User talk:$key_n"); $header = "== DYK for $nom_title_fix =="; $nom_template = "{{subst:User:Ameliorate!/DYKnom |article=$nom_title_fix |optional=$comment[1] }}"; $signature = preg_replace("/%28/","(",$signed[1]); $signature = preg_replace("/%29/",")",$signature); if($talk == ""){ $talk = $wpq->getpage("User talk:$key_n"); } $nom_message = "$talk\n\n$header\n\n$nom_template$signature ~~~~~"; $wpi->forcepost("User talk:$key_n",$nom_message,"Giving DYK nomination credit for $nom_title_fix on behalf of [[User:$userextract[2]|$userextract[2]]]"); unset($talk); } // // split // preg_match_all("/\{\{DYKmake\|(.*?)\|.*?(\|.*?)?\}\}/",$q_content_notaint,$art_talks); foreach($art_talks[1] as $key_t){ $talk = $wpq->getsection0("Talk:$key_t"); preg_match("/(\{\{\*mp\}\}.*?$key_t.*?\?)/",$q_content_notaint,$hook); $template = "{{dyktalk|{{subst:CURRENTDAY}} {{subst:CURRENTMONTHNAME}}|{{subst:CURRENTYEAR}}|$hook[1]}}"; // preg_match("/(\{\{\*mp\}\}.*?$key_t.*?\?)/",$q_content_notaint,$hook); if(!preg_match("/dyktalk/",$talk)){ $wpi->forcepost_section0("Talk:$key_t","$talk\n$template","Article has appeared in [[WP:DYK]] - Adding {{[[Template:dyktalk|dyktalk]]}} template"); } else { // nothing } } ?>
crontab
[edit]*/5 * * * * lynx -dump http://localhost/DYKadminBot/runbot.php