Utilisateur:Hexabot/botclasses.php
(Redirigé depuis Utilisateur:ZiziBot/botclasses.php)
<?php
/**
* botclasses.php - Bot classes for interacting with mediawiki.
* Last update : 2022-12-23 by Hexasoft
*
* (c) 2008-2012 Chris G - http://en.wiki.x.io/wiki/User:Chris_G
* (c) 2009-2010 Fale - http://en.wiki.x.io/wiki/User:Fale
* (c) 2010 Kaldari - http://en.wiki.x.io/wiki/User:Kaldari
* (c) 2011 Gutza - http://en.wiki.x.io/wiki/User:Gutza
* (c) 2011-2012 Irønie - http://en.wiki.x.io/wiki/User:Irønie
* (c) 2013-2022 Hexasoft - http://fr.wiki.x.io/wiki/User:Hexasoft
*
* lyricwiki class not included
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Developers (add yourself here if you worked on the code):
* Cobi - [[User:Cobi]] - Wrote the http class and some of the wikipedia class
* Chris - [[User:Chris_G]] - Wrote the most of the wikipedia class
* Fale - [[User:Fale]] - Polish, wrote the extended and some of the wikipedia class
* Kaldari - [[User:Kaldari]] - Submitted a patch for the imagematches function
* Gutza - [[User:Gutza]] - Submitted a patch for http->setHTTPcreds(), and http->quiet
* Irønie - [[User:Irønie]] - Fixed bugs, tiny stuffs, French categories, $extra parameter, categorymembers, extends func, query-continue
* Hexasoft - [[User:Hexasoft]] - Fixed CURL, login and cookie bugs
**/
/**
*** RECENT CHANGES ***
- update API "query-continue" see http://lists.wikimedia.org/pipermail/mediawiki-api-announce/2012-July/000030.html
- assert=user to prevent anonymous edits.
- 2022 : update edit token extraction
*** TODO ***
CURL cleaning
nobots and edit()
content caching on getpage() query
**/
/**
// HOW TO USE ?
See examples: https://fr.wiki.x.io/wiki/User:Hexasoft/CodeExamples
**/
/**
* This class is designed to provide a simplified interface to cURL which maintains cookies.
* @author Cobi
**/
class http {
private $ch; // CURL object
private $uid;
public $cookie_jar;
public $postfollowredirs;
public $getfollowredirs;
public $quiet = false;
public $user_agent = 'Default bot UA';
public $cookie_prefix = "filled-with-login-name";
// update user agent string
function set_user_agent($str) {
$this->user_agent = $str;
}
// set quiet state
function set_quiet($val) {
$this->quiet = $val;
}
// data encoding
function data_encode($data, $keyprefix = "", $keypostfix = "") {
assert(is_array($data));
$vars = NULL;
foreach($data as $key => $value) {
if(is_array($value)) {
$vars .= $this->data_encode($value, $keyprefix.$key.$keypostfix.urlencode("["), urlencode("]"));
} else {
$vars .= $keyprefix . $key . $keypostfix . "=" . urlencode($value) . "&";
}
}
return $vars;
}
// build object
function __construct($login) {
$this->ch = curl_init();
$this->uid = dechex(rand(0,99999999)); // not used anymore
$this->quiet = true;
$this->cookie_prefix = $login;
curl_setopt($this->ch,CURLOPT_COOKIEJAR, $this->cookie_prefix . '.dat');
curl_setopt($this->ch,CURLOPT_COOKIEFILE, $this->cookie_prefix . '.dat');
curl_setopt($this->ch,CURLOPT_MAXCONNECTS,100);
// curl_setopt($this->ch,CURLOPT_CLOSEPOLICY,CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
$this->postfollowredirs = 0;
$this->getfollowredirs = 1;
$this->cookie_jar = array();
}
// POST data
function post($url, $data) {
$time = microtime(1);
curl_setopt($this->ch, CURLOPT_URL, $url);
curl_setopt($this->ch, CURLOPT_USERAGENT, $this->user_agent);
/* Crappy hack to add extra cookies, should be cleaned up */
$cookies = NULL;
foreach ($this->cookie_jar as $name => $value) {
if (empty($cookies)) {
$cookies = "$name=$value";
} else {
$cookies .= "; $name=$value";
}
}
if ($cookies != NULL) {
curl_setopt($this->ch, CURLOPT_COOKIE, $cookies);
}
curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, $this->postfollowredirs);
curl_setopt($this->ch, CURLOPT_MAXREDIRS, 10);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('Expect:'));
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_FAILONERROR, 1);
// curl_setopt($this->ch, CURLOPT_POSTFIELDS, substr($this->data_encode($data), 0, -1) );
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
$data = curl_exec($this->ch);
if (!$this->quiet) {
echo "\033[1;30m POST: ".$url.' ('.round((microtime(1) - $time),3).' s) ('.strlen($data)." b)\033[0m\n";
}
return $data;
}
// perform GET
function get($url) {
$time = microtime(1);
curl_setopt($this->ch, CURLOPT_URL, $url);
curl_setopt($this->ch, CURLOPT_USERAGENT, $this->user_agent);
/* Crappy hack to add extra cookies, should be cleaned up */
$cookies = NULL;
foreach ($this->cookie_jar as $name => $value) {
if (empty($cookies)) {
$cookies = "$name=$value";
} else {
$cookies .= "; $name=$value";
}
}
if ($cookies != NULL) {
curl_setopt($this->ch, CURLOPT_COOKIE, $cookies);
}
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);
if( preg_match('`^https://`i', $url) ) {
//curl_setopt($this->ch, CURLOPT_VERBOSE, TRUE); // debug
curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, TRUE); // + CURLOPT_CAINFO
curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 2); // default 2
}
// curl_setopt($this->ch, CURLOPT_FAILONERROR, 1);
$data = curl_exec($this->ch);
return $data;
}
// obvious
function setHTTPcreds($uname,$pwd) {
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($this->ch, CURLOPT_USERPWD, $uname.":".$pwd);
}
// cleanup everything
function __destruct () {
curl_close($this->ch);
}
}
/**
* This class is interacts with wikipedia using api.php
* @author Chris G and Cobi
**/
class wikipedia {
private $http;
private $token;
private $ecTimestamp;
private $editTimestamp;
public $url;
public $editlaps; // delay in seconds between 2 edits
/**
* This is our constructor.
* @return void
**/
function __construct($login, $url='http://en.wiki.x.io/w/api.php', $hu=null, $hp=null) {
$this->http = new http($login);
$this->token = NULL;
$this->url = $url;
$this->ecTimestamp = NULL;
$this->editTimestamp = NULL;
$this->editlaps = 20;
if ($hu !== NULL) {
$this->http->setHTTPcreds($hu, $hp);
}
}
// properties setter (not very useful)
function __set($var, $val) {
switch($var) {
case 'quiet':
$this->http->quiet = $val;
break;
default:
echo "WARNING: Unknown variable ($var)!\n";
}
}
/**
* Sends a query to the api.
* @param $query The query string.
* @param $post POST data if its a post request (optional).
* @return The api result.
**/
function query($query, $post=NULL) {
if ($post == NULL) {
$ret = $this->http->get($this->url.$query);
} else {
$ret = $this->http->post($this->url.$query, $post);
}
return unserialize($ret);
}
/** Irønie
* Gets the recent changes (RC)
* http://fr.wiki.x.io/w/api.php?action=query&list=recentchanges&rcexcludeuser=ZiziBot&rcnamespace=0&rcprop=title|comment|user&rcshow=!bot|!redirect&rctype=edit|new&rclimit=100
* rctoponly - Only list changes which are the latest revision
rcstart - The timestamp to start enumerating from
rcend - The timestamp to end enumerating
rcdir - In which direction to enumerate
newer - List oldest first. Note: rcstart has to be before rcend.
older - List newest first (default). Note: rcstart has to be later than rcend.
One value: newer, older. Default: older
*/
function getrecentchanges($extra='') {
if($extra == TRUE) {
$extra = str_replace('&&','&', '&' . $extra);
}
$continue = '';
$RCedits = array();
while(TRUE) {
$res = $this->query('?action=query&format=php&list=recentchanges&rcexcludeuser=ZiziBot&rcnamespace=0&rcprop=title|comment|user|timestamp|sizes&rcshow=!bot|!redirect&rctype=edit|new&rclimit=6'); //.$continue
if (isset($res['error'])) {
return FALSE;
}
foreach ($res['query']['recentchanges'] as $x) {
$RCedits[] = $x; //$x['title'];
}
return $RCedits;
/* TODO Gérer option rcstart
// [query-continue][recentchanges][rcstart] => '2012-05-26T10:36:16Z'
if (empty($res['query-continue']['recentchanges'])) {
if ($subcat>0) {
foreach ($pages as $p) {
// FIXED : French cat with utf8 strlen trick
if (substr($p,0,9)=='Category:' OR substr($p,0,11)=='Catégorie:' AND $bouclecat[$p] == FALSE ) {
$bouclecat[$p] = 1;
$pages2 = $this->categorymembers($p, ($subcat-1), $extra );
$pages = array_merge($pages,$pages2);
}
}
}
return $pages;
} else {
unset($continue);
foreach( $res['query-continue']['categorymembers'] AS $key => $val ) { // FIXED : API changes 07-2012
$continue .= '&'.urlencode($key).'='.urlencode($val);
}
}
*/
}
}
/**
* Gets the content of a page. Returns false on error.
* @param $page The wikipedia page to fetch.
* @param $revid The revision id to fetch (optional)
* @return The wikitext for the page.
**/
function getpage($page, $revid=NULL, $detectEditConflict=FALSE) {
$append = '';
if ($revid != NULL) {
$append = '&rvstartid=' . $revid;
}
$x = $this->query('?action=query&format=php&prop=revisions&titles=' . urlencode($page) . '&rvlimit=1&rvprop=content|timestamp' . $append);
if($x['query']['pages'] == FALSE) {
return FALSE;
}
foreach($x['query']['pages'] as $ret) {
if (isset($ret['revisions'][0]['*'])) {
if ($detectEditConflict) {
$this->ecTimestamp = $ret['revisions'][0]['timestamp'];
}
return $ret['revisions'][0]['*'];
} else {
return FALSE;
}
}
}
/**
* 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) {
$x = $this->query('?action=query&format=php&prop=revisions&titles=' . urlencode($page) . '&rvlimit=1&rvprop=content');
foreach ($x['query']['pages'] as $ret) {
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) {
$x = $this->query('?action=query&list=allusers&format=php&auprop=editcount&aulimit=1&aufrom=' . urlencode($user));
return $x['query']['allusers'][0]['editcount'];
}
/**
* Returns an array with all the members of $category
* @param $category The category to use.
* @param $subcat (bool) Go into sub categories?
* @return array
**/
// Note cmlimit (mailing) : "Also note the documentation already say the default value is null, that is currently wrong."
// FIXED : Replaced $subcat (bool) by $subcat (numeric) for depth choice
// FIXED : Added $extra for extra parameters Example : $extra = 'cmnamespace=0|1|2&cmtype=page|subcat|file'
// FIXED : + 'Catégorie:' (french cat names)
// TODO : $categoryname for other languages -> http://fr.wiki.x.io/w/api.php?action=query&meta=siteinfo&siprop=namespacealiases voir subpages()
/*
TODO : verif update API : pas d'historique ou documentation complète => voir https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=history;f=includes/api
**** API update nov 2011 ***** (Roan @ mediawiki-api@lists.wikimedia.org)
To only list files, use &cmtype=file (previously &cmnamespace=6)
To only list subcategories, use &cmtype=subcat (previously &cmnamespace=14)
To only list 'normal' pages that aren't files or subcategories, use
&cmtype=page (previously &cmnamespace=0|1|etc|everything|except|six|and|fourteen)
You can combine values for cmtype, e.g. &cmtype=file|subcat to list
both files and subcategories. The default value for cmtype is file|subcat|page (i.e. list everything).
*/
function categorymembers($category, $subcat=0, $extra=NULL) {
global $bouclecat;
if ($extra == TRUE) {
$extra = str_replace('&&','&', '&' . $extra);
}
$continue = '';
$pages = array();
while(TRUE) {
$res = $this->query('?action=query&list=categorymembers&cmtitle=' . urlencode($category) . $extra . '&format=php&cmlimit=1000' . $continue);
if (isset($res['error'])) {
echo "\nERROR query categorymembers\n";
print_r($res['error']);
return FALSE;
}
foreach ($res['query']['categorymembers'] as $x) {
$pages[] = $x['title'];
}
if (empty($res['continue']['cmcontinue'])) {
if ($subcat > 0) {
foreach($pages as $p) {
// FIXED : French cat with utf8 strlen trick
if ((substr($p,0,9) == 'Category:') or (substr($p,0,11) == 'Catégorie:')
and ($bouclecat[$p] == FALSE)) {
$bouclecat[$p] = 1;
$pages2 = $this->categorymembers($p, ($subcat-1), $extra );
$pages = array_merge($pages,$pages2);
}
}
}
return $pages;
} else {
unset($continue);
$val = $res['continue']['cmcontinue'];
$continue = '&cmcontinue=' . urlencode($val);
}
}
}
/**
* Returns a list of pages that link to $page.
* @param $page
* @param $extra (defaults to null)
* @return array
**/
function whatlinkshere($page, $extra=NULL) {
$continue = '';
$pages = array();
while(TRUE) {
$res = $this->query('?action=query&list=backlinks&bltitle=' . urlencode($page) . '&bllimit=500&format=php' . $continue . $extra);
if (isset($res['error'])) {
return FALSE;
}
foreach ($res['query']['backlinks'] as $x) {
$pages[] = $x['title'];
}
if (empty($res['query-continue']['backlinks'])) {
return $pages;
} else {
unset($continue);
foreach( $res['query-continue']['backlinks'] as $key => $val) { // FIXED : API changes 07-2012
$continue .= '&' . urlencode($key) . '=' . urlencode($val);
}
}
}
}
/**
* Returns a list of pages that include the image.
* @param $image
* @param $extra (defaults to null)
* @return array
**/
function whereisincluded($image, $extra=NULL) {
$continue = '';
$pages = array();
while (TRUE) {
$res = $this->query('?action=query&list=imageusage&iutitle=' . urlencode($image) . '&iulimit=500&format=php' . $continue . $extra);
if (isset($res['error'])) {
return FALSE;
}
foreach ($res['query']['imageusage'] as $x) {
$pages[] = $x['title'];
}
if (empty($res['query-continue']['imageusage'])) {
return $pages;
} else {
unset($continue);
foreach($res['query-continue']['imageusage'] as $key => $val) { // FIXED : API changes 07-2012
$continue .= '&' . urlencode($key) . '=' . urlencode($val);
}
}
}
}
/**
* Returns a list of pages that use the $template.
* @param $template the template we are intereste into
* @param $extra (defaults to null)
* @return array
**/
function whatusethetemplate($template, $extra=NULL) {
$continue = '';
$pages = array();
while (TRUE) {
$res = $this->query('?action=query&list=embeddedin&eititle=Template:' . urlencode($template) . '&eilimit=1000&format=php' . $continue . $extra);
if (isset($res['error'])) {
return FALSE;
}
foreach($res['query']['embeddedin'] as $x) {
$pages[] = $x['title'];
}
if (empty($res['continue']['eicontinue'])) {
return $pages;
} else {
unset($continue);
$val = $res['continue']['eicontinue'];
$continue = '&' . 'eicontinue=' . urlencode($val);
}
}
}
/**
* Returns an array with all the subpages of $page
* @param $page
* @param $extra extra parameters
* @return array
* ADDED : + $extra
* &apfilterredir= - Which pages to list. One value: all, redirects, nonredirects
* aplimit = 500 / 5000 for bots / default 10
**/
function subpages($page, $extra='') {
/* Calculate all the namespace codes */
$ret = $this->query('?action=query&meta=siteinfo&siprop=namespaces&format=php');
foreach ($ret['query']['namespaces'] as $x) {
$namespaces[$x['*']] = $x['id'];
}
$temp = explode(':', $page, 2);
$namespace = $namespaces[$temp[0]];
$title = $temp[1];
$continue = '';
$subpages = array();
while (TRUE) {
$res = $this->query('?action=query&format=php&list=allpages&apprefix=' . urlencode($title) . '&aplimit=500&apnamespace=' . $namespace . $continue . $extra);
if (isset($x[error])) {
return FALSE;
}
foreach ($res['query']['allpages'] as $p) {
$subpages[] = $p['title'];
}
if (empty($res['query-continue']['allpages'])) {
return $subpages;
} else {
unset($continue);
foreach($res['query-continue']['allpages'] as $key => $val) { // FIXED : API changes 07-2012
$continue .= '&' . urlencode($key) . '=' . urlencode($val);
}
}
}
}
/**
* This function takes a username and password and logs you into wikipedia.
* @param $user Username to login as.
* @param $pass Password that corresponds to the username.
* @return array
**/
function login($user, $pass) {
$post = array('lgname' => $user, 'lgpassword' => $pass);
// first, check if we're still logged
$ret = $this->query('?action=query&prop=info&titles=&assert=user&format=php');
if (isset($ret['error'])) {
$ret = $this->query('?action=login&format=php',$post);
/* This is now required - see https://bugzilla.wikimedia.org/show_bug.cgi?id=23076 */
if ($ret['login']['result'] == 'NeedToken') {
$post['lgtoken'] = $ret['login']['token'];
$ret = $this->query( '?action=login&format=php', $post );
}
if ($ret['login']['result'] != 'Success') {
echo "LOGIN ERROR on ". $this->url. "\n";
print_r($ret);
die();
} else {
return $ret;
}
} else {
// still logged
$tmp['login'] = [];
$tmp['login']['result'] = 'Success';
return $tmp;
}
}
/* crappy hack to allow users to use cookies from old sessions */
// note: no more needed (and no more used: don't use)
function setLogin($data) {
$this->http->cookie_jar = array(
$data['cookieprefix'].'UserName' => $data['lgusername'],
$data['cookieprefix'].'UserID' => $data['lguserid'],
$data['cookieprefix'].'Token' => $data['lgtoken'],
$data['cookieprefix'].'_session' => $data['sessionid'],
);
}
/**
* Check if we're allowed to edit $page.
* See http://en.wiki.x.io/wiki/Template:Bots
* for more info.
* @param $page The page we want to edit.
* @param $user The bot's username.
* @return bool
**/
function nobots($page, $user=NULL, $text=NULL) {
if ($text == NULL) {
$text = $this->getpage($page);
}
if ($user != NULL) {
if (preg_match('/\{\{(nobots|bots\|allow=none|bots\|deny=all|bots\|optout=all|bots\|deny=.*?' . preg_quote($user,'/') . '.*?)\}\}/iS', $text)) {
return FALSE;
}
} else {
if (preg_match('/\{\{(nobots|bots\|allow=none|bots\|deny=all|bots\|optout=all)\}\}/iS', $text)) {
return FALSE;
}
}
return TRUE;
}
/**
* This function returns the edit token for the current user.
* @return edit token.
**/
function getedittoken() {
/*
$x = $this->query('?action=query&prop=info&intoken=edit&titles=Main%20Page&format=php');
foreach ($x['query']['pages'] as $ret) {
return $ret['edittoken'];
}
*/
$x = $this->query('?action=query&meta=tokens&format=php');
return $x['query']['tokens']['csrftoken'];
}
/**
* Purges the cache of $page.
* @param $page The page to purge.
* @return Api result.
**/
function purgeCache($page) {
return $this->query('?action=purge&titles=' . urlencode($page) . '&format=php');
}
/**
* Checks if $user has email enabled.
* Uses index.php.
* @param $user The user to check.
* @return bool.
**/
function checkEmail($user) {
$x = $this->query('?action=query&meta=allmessages&ammessages=noemailtext|notargettext&amlang=en&format=php');
$messages[0] = $x['query']['allmessages'][0]['*'];
$messages[1] = $x['query']['allmessages'][1]['*'];
$page = $this->http->get(str_replace('api.php', 'index.php', $this->url) . '?title=Special:EmailUser&target=' . urlencode($user));
if (preg_match('/(' . preg_quote($messages[0],'/') . '|' . preg_quote($messages[1],'/') . ')/i', $page)) {
return FALSE;
} else {
return TRUE;
}
}
/**
* Returns all the pages $page is transcluded on.
* @param $page The page to get the transclusions from.
* @param $sleep The time to sleep between requets (set to null to disable).
* @return array.
**/
// eilimit : No more than 500 (5000 for bots) allowed, default=10
// $extra : eifilterredir=all, redirects, nonredirects
// einamespace - The namespace to enumerate
function getTransclusions($page, $sleep=NULL, $extra=NULL) {
$continue = '';
$pages = array();
while (TRUE) {
$ret = $this->query('?action=query&list=embeddedin&eititle=' . urlencode($page) . $continue . $extra . '&eilimit=5000&format=php');
if ($sleep != NULL) {
echo "∆";
sleep($sleep);
}
foreach ($ret['query']['embeddedin'] as $x) {
$pages[] = $x['title'];
}
if (isset($ret['query-continue']['embeddedin'])) {
unset($continue);
foreach($ret['query-continue']['embeddedin'] as $key => $val) { // FIXED : API changes 07-2012
$continue .= '&' . urlencode($key) . '=' . urlencode($val);
}
} else {
return $pages;
}
}
}
/**
* 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)
* @return api result
**/
// FIXED : $section=false/is_numeric to manage §ion=0 (heading)
function edit($page, $data, $summary='', $minor=FALSE, $bot=TRUE, $section=FALSE, $detectEC=FALSE, $maxlag='') {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'title' => $page,
'text' => $data,
'token' => $this->token,
'summary' => $summary,
($minor?'minor':'notminor') => '1',
($bot?'bot':'notbot') => '1'
);
if (is_numeric($section)) {
$params['section'] = $section;
}
if (($this->ecTimestamp != NULL) and ($detectEC == TRUE)) {
$params['basetimestamp'] = $this->ecTimestamp;
$this->ecTimestamp = NULL;
}
if ($maxlag != '') {
$maxlag = '&maxlag=' . $maxlag;
}
// sécurité vitesse édition
if (($this->editTimestamp > 0) and ((time() - $this->editTimestamp) < $this->editlaps)) {
$waitsec = ($this->editlaps - (time()-($this->editTimestamp)));
echo "\nPause ".$waitsec." secondes...\n";
sleep($waitsec);
}
$this->editTimestamp = time();
return $this->query('?action=edit&assert=user&format=php' . $maxlag, $params);
}
/**
* Add a text at the bottom of a page
* @param $page The page we're working with.
* @param $text The text that you want to add.
* @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)
* @return api result
**/
function addtext( $page, $text, $summary='', $minor=FALSE, $bot=TRUE) {
$data = $this->getpage($page);
$data.= "\n" . $text;
return $this->edit($page, $data, $summary, $minor, $bot);
}
/**
* Moves a page.
* @param $old Name of page to move.
* @param $new New page title.
* @param $reason Move summary to use.
* @param $movetalk Move the page's talkpage as well.
* @return api result
**/
function move($old, $new, $reason, $options=NULL) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'from' => $old,
'to' => $new,
'token' => $this->token,
'reason' => $reason
);
if ($options != NULL) {
$option = explode('|', $options);
foreach ($option as $o) {
$params[$o] = TRUE;
}
}
return $this->query('?action=move&format=php', $params);
}
/**
* 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 $bot mark the rollback as bot.
* @return api result
**/
function rollback ($title, $user, $reason=NULL, $bot=FALSE) {
$ret = $this->query('?action=query&prop=revisions&rvtoken=rollback&titles=' . urlencode($title) . '&format=php');
// print_r($ret);
foreach ($ret['query']['pages'] as $x) {
$token = $x['revisions'][0]['rollbacktoken'];
break;
}
$params = array(
'title' => $title,
'user' => $user,
'token' => $token
);
if ($bot) {
$params['markbot'] = true;
}
if ($reason != NULL) {
$params['summary'] = $reason;
}
return $this->query('?action=rollback&format=php', $params);
}
/**
* Blocks a user.
* @param $user The user to block.
* @param $reason The block reason.
* @param $expiry The block expiry.
* @param $options a piped string containing the block options.
* @return api result
**/
function block($user, $reason='vand', $expiry='infinite', $options=NULL, $retry=TRUE) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'expiry' => $expiry,
'user' => $user,
'reason' => $reason,
'token' => $this->token
);
if ($options != NULL) {
$option = explode('|', $options);
foreach ($option as $o) {
$params[$o] = true;
}
}
$ret = $this->query('?action=block&format=php', $params);
/* Retry on a failed token. */
if ($retry and ($ret['error']['code'] == 'badtoken')) {
$this->token = $this->getedittoken();
return $this->block($user, $reason, $expiry, $options, FALSE);
}
return $ret;
}
/**
* Unblocks a user.
* @param $user The user to unblock.
* @param $reason The unblock reason.
* @return api result
**/
function unblock($user, $reason) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'user' => $user,
'reason' => $reason,
'token' => $this->token
);
return $this->query('?action=unblock&format=php', $params);
}
/**
* Emails a user.
* @param $target The user to email.
* @param $subject The email subject.
* @param $text The body of the email.
* @param $ccme Send a copy of the email to the user logged in.
* @return api result
**/
function email($target, $subject, $text, $ccme=FALSE) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'target' => $target,
'subject' => $subject,
'text' => $text,
'token' => $this->token
);
if ($ccme) {
$params['ccme'] = true;
}
return $this->query('?action=emailuser&format=php', $params);
}
/**
* Deletes a page.
* @param $title The page to delete.
* @param $reason The delete reason.
* @return api result
**/
function delete($title, $reason) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'title' => $title,
'reason' => $reason,
'token' => $this->token
);
return $this->query('?action=delete&format=php', $params);
}
/**
* Undeletes a page.
* @param $title The page to undelete.
* @param $reason The undelete reason.
* @return api result
**/
function undelete($title, $reason) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'title' => $title,
'reason' => $reason,
'token' => $this->token
);
return $this->query('?action=undelete&format=php', $params);
}
/**
* (Un)Protects a page.
* @param $title The page to (un)protect.
* @param $protections The protection levels (e.g. 'edit=autoconfirmed|move=sysop')
* @param $expiry When the protection should expire (e.g. '1 day|infinite')
* @param $reason The (un)protect reason.
* @param $cascade Enable cascading protection? (defaults to false)
* @return api result
**/
function protect($title, $protections, $expiry, $reason, $cascade=FALSE) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'title' => $title,
'protections' => $protections,
'expiry' => $expiry,
'reason' => $reason,
'token' => $this->token
);
if ($cascade) {
$params['cascade'] = true;
}
return $this->query('?action=protect&format=php', $params);
}
/**
* Uploads an image.
* @param $page The destination file name.
* @param $file The local file path.
* @param $desc The upload discrption (defaults to '').
**/
function upload($page, $file, $desc='') {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$params = array(
'filename' => $page,
'comment' => $desc,
'text' => $desc,
'token' => $this->token,
'ignorewarnings' => '1',
'file' => '@'.$file
);
return $this->query('?action=upload&format=php', $params);
}
/*
$page - page
$revs - rev ids to delete (seperated with ,)
$comment - delete comment
*/
function revdel($page, $revs, $comment) {
if ($this->token == NULL) {
$this->token = $this->getedittoken();
}
$post = array(
'wpEditToken' => $this->token,
'ids' => $revs,
'target' => $page,
'type' => 'revision',
'wpHidePrimary' => 1,
'wpHideComment' => 1,
'wpHideUser' => 0,
'wpRevDeleteReasonList' => 'other',
'wpReason' => $comment,
'wpSubmit' => 'Apply to selected revision(s)'
);
return $this->http->post(str_replace('api.php', 'index.php', $this->url) . '?title=Special:RevisionDelete&action=submit', $post);
}
/**
* Creates a new account.
* Uses index.php as there is no api to create accounts yet :(
* @param $username The username the new account will have.
* @param $password The password the new account will have.
* @param $email The email the new account will have.
**/
function createaccount ($username, $password, $email=NULL) {
$post = array(
'wpName' => $username,
'wpPassword' => $password,
'wpRetype' => $password,
'wpEmail' => $email,
'wpRemember' => 0,
'wpIgnoreAntiSpoof' => 0,
'wpCreateaccount' => 'Create account',
);
return $this->http->post(str_replace('api.php', 'index.php', $this->url) . '?title=Special:UserLogin&action=submitlogin&type=signup', $post);
}
/**
* Changes a users rights.
* @param $user The user we're working with.
* @param $add A pipe-separated list of groups you want to add.
* @param $remove A pipe-separated list of groups you want to remove.
* @param $reason The reason for the change (defaults to '').
**/
function userrights($user, $add, $remove, $reason='') {
// get the userrights token
$token = $this->query('?action=query&list=users&ususers=' . urlencode($user) . '&ustoken=userrights&format=php');
$token = $token['query']['users'][0]['userrightstoken'];
$params = array(
'user' => $user,
'token' => $token,
'add' => $add,
'remove' => $remove,
'reason' => $reason
);
return $this->query('?action=userrights&format=php', $params);
}
/**
* Gets the number of images matching a particular sha1 hash.
* @param $hash The sha1 hash for an image.
* @return The number of images with the same sha1 hash.
**/
function imagematches($hash) {
$x = $this->query('?action=query&list=allimages&format=php&aisha1=' . $hash);
return count($x['query']['allimages']);
}
}
/**
* This class extends the wiki class to provide an high level API for the most commons actions.
* @author Fale
**/
class extended extends wikipedia {
/**
* Add a category to a page
* @param $page The page we're working with.
* @param $category The category that you want to add.
* @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)
* @return api result
UGLY - DO NOT USE !
**/
//function addcategory( $page, $category, $summary = '', $minor = false, $bot = true )
//{
// $data = $this->getpage( $page );
// $data.= "\n[["."Catégorie:" . $category . "]]";
// return $this->edit( $page, $data, $summary, $minor, $bot );
//}
/**
* Find a string
* @param $page The page we're working with.
* @param $string The string that you want to find.
* @return bool value
**/
function findstring($page, $string) {
$data = $this->getpage($page);
if( strstr($data, $string))
return TRUE;
else
return FALSE;
}
/**
* Replace a string
* @param $page The page we're working with.
* @param $string The string that you want to replace.
* @param $newstring The string that will replace the present string.
* @return the new text of page
**/
function replacestring($page, $string, $newstring) {
$data = $this->getpage($page);
return str_replace($string, $newstring, $data);
}
/**
* Get a template from a page
* @param $page The page we're working with
* @param $template The name of the template we are looking for
* @return the searched (NULL if the template has not been found)
**/
function gettemplate($page, $template) {
$data = $this->getpage($page);
$template = preg_quote($template, " ");
$r = "/{{" . $template . "(?:[^{}]*(?:{{[^}]*}})?)+(?:[^}]*}})?/i";
preg_match_all($r, $data, $matches);
if(isset($matches[0][0])) {
return $matches[0][0];
} else {
return NULL;
}
}
/*
* Return redirect page title or false
* TODO : boucle pour arriver sur bonne page. Attention boucle sans fin
*/
function page_redirect($page) {
$data = $this->getpage($page);
if(preg_match('/^#REDIRECT(?:ION)? ?\[\[([^\]]+)\]\]/i', $data, $matches)) {
return (string) trim($matches[1]);
} else {
return FALSE;
}
}
}