PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/botclasses.php

https://github.com/Dispositif/addbot
PHP | 1147 lines | 675 code | 67 blank | 405 comment | 97 complexity | c4a5b854d3769442cce203e1bed0fbb1 MD5 | raw file
  1. <?php
  2. /**
  3. * botclasses.php - Bot classes for interacting with mediawiki.
  4. *
  5. * (c) 2008-2012 Chris G - http://en.wikipedia.org/wiki/User:Chris_G
  6. * (c) 2009-2010 Fale - http://en.wikipedia.org/wiki/User:Fale
  7. * (c) 2010 Kaldari - http://en.wikipedia.org/wiki/User:Kaldari
  8. * (c) 2011 Gutza - http://en.wikipedia.org/wiki/User:Gutza
  9. * (c) 2012 Sean - http://en.wikipedia.org/wiki/User:SColombo
  10. * (c) 2012 Brain - http://en.wikipedia.org/wiki/User:Brian_McNeil
  11. * (c) 2012-2013 Addshore - http://en.wikipedia.org/wiki/User:Addshore
  12. *
  13. * This program is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation; either version 2 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program; if not, write to the Free Software
  25. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26. *
  27. * Developers (add yourself here if you worked on the code):
  28. * Cobi - [[User:Cobi]] - Wrote the http class and some of the wikipedia class
  29. * Chris - [[User:Chris_G]] - Wrote the most of the wikipedia class
  30. * Fale - [[User:Fale]] - Polish, wrote the extended and some of the wikipedia class
  31. * Kaldari - [[User:Kaldari]] - Submitted a patch for the imagematches function
  32. * Gutza - [[User:Gutza]] - Submitted a patch for http->setHTTPcreds(), and http->quiet
  33. * Sean - [[User:SColombo]] - Wrote the lyricwiki class (now moved to lyricswiki.php)
  34. * Brain - [[User:Brian_McNeil]] - Wrote wikipedia->getfileuploader() and wikipedia->getfilelocation
  35. * Addshore - [[User:Addshore]] - Functions for Addbot (lastedit,lasteditonpage,firstedit,update nobots)
  36. **/
  37. /**
  38. * This class is designed to provide a simplified interface to cURL which maintains cookies.
  39. * @author Cobi
  40. **/
  41. class http {
  42. private $ch;
  43. private $uid;
  44. public $cookie_jar;
  45. public $postfollowredirs;
  46. public $getfollowredirs;
  47. public $quiet=true;
  48. function data_encode ($data, $keyprefix = "", $keypostfix = "") {
  49. assert( is_array($data) );
  50. $vars=null;
  51. foreach($data as $key=>$value) {
  52. if(is_array($value))
  53. $vars .= $this->data_encode($value, $keyprefix.$key.$keypostfix.urlencode("["), urlencode("]"));
  54. else
  55. $vars .= $keyprefix.$key.$keypostfix."=".urlencode($value)."&";
  56. }
  57. return $vars;
  58. }
  59. function __construct () {
  60. $this->ch = curl_init();
  61. $this->uid = dechex(rand(0,99999999));
  62. curl_setopt($this->ch,CURLOPT_COOKIEJAR,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
  63. curl_setopt($this->ch,CURLOPT_COOKIEFILE,'/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
  64. curl_setopt($this->ch,CURLOPT_MAXCONNECTS,100);
  65. curl_setopt($this->ch,CURLOPT_CLOSEPOLICY,CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
  66. $this->postfollowredirs = 0;
  67. $this->getfollowredirs = 1;
  68. $this->cookie_jar = array();
  69. }
  70. function post ($url,$data) {
  71. //echo 'POST: '.$url."\n";
  72. $time = microtime(1);
  73. curl_setopt($this->ch,CURLOPT_URL,$url);
  74. curl_setopt($this->ch,CURLOPT_USERAGENT,'php wikibot classes');
  75. /* Crappy hack to add extra cookies, should be cleaned up */
  76. $cookies = null;
  77. foreach ($this->cookie_jar as $name => $value) {
  78. if (empty($cookies))
  79. $cookies = "$name=$value";
  80. else
  81. $cookies .= "; $name=$value";
  82. }
  83. if ($cookies != null)
  84. curl_setopt($this->ch,CURLOPT_COOKIE,$cookies);
  85. curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,$this->postfollowredirs);
  86. curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
  87. curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('Expect:'));
  88. curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
  89. curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
  90. curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
  91. curl_setopt($this->ch,CURLOPT_POST,1);
  92. // curl_setopt($this->ch,CURLOPT_FAILONERROR,1);
  93. // curl_setopt($this->ch,CURLOPT_POSTFIELDS, substr($this->data_encode($data), 0, -1) );
  94. curl_setopt($this->ch,CURLOPT_POSTFIELDS, $data);
  95. $data = curl_exec($this->ch);
  96. // echo "Error: ".curl_error($this->ch);
  97. // var_dump($data);
  98. // global $logfd;
  99. // if (!is_resource($logfd)) {
  100. // $logfd = fopen('php://stderr','w');
  101. if (!$this->quiet)
  102. echo 'POST: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n";
  103. // }
  104. return $data;
  105. }
  106. function get ($url) {
  107. //echo 'GET: '.$url."\n";
  108. $time = microtime(1);
  109. curl_setopt($this->ch,CURLOPT_URL,$url);
  110. curl_setopt($this->ch,CURLOPT_USERAGENT,'php wikibot classes');
  111. /* Crappy hack to add extra cookies, should be cleaned up */
  112. $cookies = null;
  113. foreach ($this->cookie_jar as $name => $value) {
  114. if (empty($cookies))
  115. $cookies = "$name=$value";
  116. else
  117. $cookies .= "; $name=$value";
  118. }
  119. if ($cookies != null)
  120. curl_setopt($this->ch,CURLOPT_COOKIE,$cookies);
  121. curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION,$this->getfollowredirs);
  122. curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
  123. curl_setopt($this->ch,CURLOPT_HEADER,0);
  124. curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
  125. curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
  126. curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
  127. curl_setopt($this->ch,CURLOPT_HTTPGET,1);
  128. //curl_setopt($this->ch,CURLOPT_FAILONERROR,1);
  129. $data = curl_exec($this->ch);
  130. //echo "Error: ".curl_error($this->ch);
  131. //var_dump($data);
  132. //global $logfd;
  133. //if (!is_resource($logfd)) {
  134. // $logfd = fopen('php://stderr','w');
  135. if (!$this->quiet)
  136. echo 'GET: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n";
  137. //}
  138. return $data;
  139. }
  140. function setHTTPcreds($uname,$pwd) {
  141. curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  142. curl_setopt($this->ch, CURLOPT_USERPWD, $uname.":".$pwd);
  143. }
  144. function __destruct () {
  145. curl_close($this->ch);
  146. @unlink('/tmp/cluewikibot.cookies.'.$this->uid.'.dat');
  147. }
  148. }
  149. /**
  150. * This class is interacts with wikipedia using api.php
  151. * @author Chris G and Cobi
  152. **/
  153. class wikipedia {
  154. private $http;
  155. private $token;
  156. private $ecTimestamp;
  157. public $url;
  158. /**
  159. * This is our constructor.
  160. * @return void
  161. **/
  162. function __construct ($url='http://en.wikipedia.org/w/api.php',$hu=null,$hp=null) {
  163. $this->http = new http;
  164. $this->token = null;
  165. $this->url = $url;
  166. $this->ecTimestamp = null;
  167. if ($hu!==null)
  168. $this->http->setHTTPcreds($hu,$hp);
  169. }
  170. function __set($var,$val) {
  171. switch($var) {
  172. case 'quiet':
  173. $this->http->quiet=$val;
  174. break;
  175. default:
  176. echo "WARNING: Unknown variable ($var)!\n";
  177. }
  178. }
  179. /**
  180. * Sends a query to the api.
  181. * @param $query The query string.
  182. * @param $post POST data if its a post request (optional).
  183. * @return The api result.
  184. **/
  185. function query ($query,$post=null) {
  186. if ($post==null)
  187. $ret = $this->http->get($this->url.$query);
  188. else
  189. $ret = $this->http->post($this->url.$query,$post);
  190. return unserialize($ret);
  191. }
  192. /**
  193. * Gets the content of a page. Returns false on error.
  194. * @param $page The wikipedia page to fetch.
  195. * @param $revid The revision id to fetch (optional)
  196. * @return The wikitext for the page.
  197. **/
  198. function getpage ($page,$revid=null,$detectEditConflict=false) {
  199. $append = '';
  200. if ($revid!=null)
  201. $append = '&rvstartid='.$revid;
  202. $x = $this->query('?action=query&format=php&prop=revisions&titles='.urlencode($page).'&rvlimit=1&rvprop=content|timestamp'.$append);
  203. foreach ($x['query']['pages'] as $ret) {
  204. if (isset($ret['revisions'][0]['*'])) {
  205. if ($detectEditConflict)
  206. $this->ecTimestamp = $ret['revisions'][0]['timestamp'];
  207. return $ret['revisions'][0]['*'];
  208. } else
  209. return false;
  210. }
  211. }
  212. /**
  213. * Gets the page id for a page.
  214. * @param $page The wikipedia page to get the id for.
  215. * @return The page id of the page.
  216. **/
  217. function getpageid ($page) {
  218. $x = $this->query('?action=query&format=php&prop=revisions&titles='.urlencode($page).'&rvlimit=1&rvprop=content');
  219. foreach ($x['query']['pages'] as $ret) {
  220. return $ret['pageid'];
  221. }
  222. }
  223. /**
  224. * Gets the number of contributions a user has.
  225. * @param $user The username for which to get the edit count.
  226. * @return The number of contributions the user has.
  227. **/
  228. function contribcount ($user) {
  229. $x = $this->query('?action=query&list=allusers&format=php&auprop=editcount&aulimit=1&aufrom='.urlencode($user));
  230. return $x['query']['allusers'][0]['editcount'];
  231. }
  232. /**
  233. * Returns an array with all the members of $category
  234. * @param $category The category to use.
  235. * @param $subcat (bool) Go into sub categories?
  236. * @return array
  237. **/
  238. function categorymembers ($category,$subcat=false) {
  239. $continue = '';
  240. $pages = array();
  241. while (true) {
  242. $res = $this->query('?action=query&list=categorymembers&cmtitle='.urlencode($category).'&format=php&cmlimit=500'.$continue);
  243. if (isset($x['error'])) {
  244. return false;
  245. }
  246. foreach ($res['query']['categorymembers'] as $x) {
  247. $pages[] = $x['title'];
  248. }
  249. if (empty($res['query-continue']['categorymembers']['cmcontinue'])) {
  250. if ($subcat) {
  251. foreach ($pages as $p) {
  252. if (substr($p,0,9)=='Category:') {
  253. $pages2 = $this->categorymembers($p,true);
  254. $pages = array_merge($pages,$pages2);
  255. }
  256. }
  257. }
  258. return $pages;
  259. } else {
  260. $continue = '&cmcontinue='.urlencode($res['query-continue']['categorymembers']['cmcontinue']);
  261. }
  262. }
  263. }
  264. /**
  265. * Returns an array with all the ctegories of a page
  266. * @param $page The page to check
  267. * @param $hidden (bool) Do you want to only show hiddencats or only show normalcats? (can be null)
  268. * @return array
  269. **/
  270. function categories($page,$hidden=null) {
  271. if($hidden === true){ $h = "&clshow=hidden";}
  272. elseif($hidden === false){$h = "&clshow=!hidden";}
  273. else{$h = "";}
  274. $continue = '';
  275. $pages = array();
  276. while (true) {
  277. $res = $this->query('?action=query&prop=categories&clprop=hidden&titles='.urlencode($page).$h.'&format=php&cllimit=1000'.$continue);
  278. if (isset($x['error'])) {
  279. return false;
  280. }
  281. foreach ($res['query']['pages'] as $p) {
  282. foreach ($p['categories'] as $x) {
  283. $pages[] = $x['title'];
  284. }
  285. }
  286. if (empty($res['query-continue']['categories']['clcontinue'])) {
  287. return $pages;
  288. } else {
  289. $continue = '&clcontinue='.urlencode($res['query-continue']['categories']['clcontinue']);
  290. }
  291. }
  292. }
  293. /**
  294. * Returns a list of pages that link to $page.
  295. * @param $page
  296. * @param $extra (defaults to null)
  297. * @return array
  298. **/
  299. function whatlinkshere ($page,$extra=null) {
  300. $continue = '';
  301. $pages = array();
  302. while (true) {
  303. $res = $this->query('?action=query&list=backlinks&bltitle='.urlencode($page).'&bllimit=1000&format=php'.$continue.$extra);
  304. if (isset($res['error'])) {
  305. return false;
  306. }
  307. foreach ($res['query']['backlinks'] as $x) {
  308. $pages[] = $x['title'];
  309. }
  310. if (empty($res['query-continue']['backlinks']['blcontinue'])) {
  311. return $pages;
  312. } else {
  313. $continue = '&blcontinue='.urlencode($res['query-continue']['backlinks']['blcontinue']);
  314. }
  315. }
  316. }
  317. /**
  318. * Returns a list of pages that include the image.
  319. * @param $image
  320. * @param $extra (defaults to null)
  321. * @return array
  322. **/
  323. function whereisincluded ($image,$extre=null) {
  324. $continue = '';
  325. $pages = array();
  326. while (true) {
  327. $res = $this->query('?action=query&list=imageusage&iutitle='.urlencode($image).'&iulimit=500&format=php'.$continue.$extra);
  328. if (isset($res['error']))
  329. return false;
  330. foreach ($res['query']['imageusage'] as $x) {
  331. $pages[] = $x['title'];
  332. }
  333. if (empty($res['query-continue']['imageusage']['iucontinue']))
  334. return $pages;
  335. else
  336. $continue = '&iucontinue='.urlencode($res['query-continue']['imageusage']['iucontinue']);
  337. }
  338. }
  339. /**
  340. * Returns a list of pages that use the $template.
  341. * @param $template the template we are intereste into
  342. * @param $extra (defaults to null)
  343. * @return array
  344. **/
  345. function whatusethetemplate ($template,$extra=null) {
  346. $continue = '';
  347. $pages = array();
  348. while (true) {
  349. $res = $this->query('?action=query&list=embeddedin&eititle=Template:'.urlencode($template).'&eilimit=500&format=php'.$continue.$extra);
  350. if (isset($res['error'])) {
  351. return false;
  352. }
  353. foreach ($res['query']['embeddedin'] as $x) {
  354. $pages[] = $x['title'];
  355. }
  356. if (empty($res['query-continue']['embeddedin']['eicontinue'])) {
  357. return $pages;
  358. } else {
  359. $continue = '&eicontinue='.urlencode($res['query-continue']['embeddedin']['eicontinue']);
  360. }
  361. }
  362. }
  363. /**
  364. * Returns an array with all the subpages of $page
  365. * @param $page
  366. * @return array
  367. **/
  368. function subpages ($page) {
  369. /* Calculate all the namespace codes */
  370. $ret = $this->query('?action=query&meta=siteinfo&siprop=namespaces&format=php');
  371. foreach ($ret['query']['namespaces'] as $x) {
  372. $namespaces[$x['*']] = $x['id'];
  373. }
  374. $temp = explode(':',$page,2);
  375. $namespace = $namespaces[$temp[0]];
  376. $title = $temp[1];
  377. $continue = '';
  378. $subpages = array();
  379. while (true) {
  380. $res = $this->query('?action=query&format=php&list=allpages&apprefix='.urlencode($title).'&aplimit=500&apnamespace='.$namespace.$continue);
  381. if (isset($x[error])) {
  382. return false;
  383. }
  384. foreach ($res['query']['allpages'] as $p) {
  385. $subpages[] = $p['title'];
  386. }
  387. if (empty($res['query-continue']['allpages']['apfrom'])) {
  388. return $subpages;
  389. } else {
  390. $continue = '&apfrom='.urlencode($res['query-continue']['allpages']['apfrom']);
  391. }
  392. }
  393. }
  394. /**
  395. * This function takes a username and password and logs you into wikipedia.
  396. * @param $user Username to login as.
  397. * @param $pass Password that corrisponds to the username.
  398. * @return array
  399. **/
  400. function login ($user,$pass) {
  401. $post = array('lgname' => $user, 'lgpassword' => $pass);
  402. $ret = $this->query('?action=login&format=php',$post);
  403. /* This is now required - see https://bugzilla.wikimedia.org/show_bug.cgi?id=23076 */
  404. if ($ret['login']['result'] == 'NeedToken') {
  405. $post['lgtoken'] = $ret['login']['token'];
  406. $ret = $this->query( '?action=login&format=php', $post );
  407. }
  408. if ($ret['login']['result'] != 'Success') {
  409. echo "Login error: \n";
  410. print_r($ret);
  411. die();
  412. } else {
  413. return $ret;
  414. }
  415. }
  416. /* crappy hack to allow users to use cookies from old sessions */
  417. function setLogin($data) {
  418. $this->http->cookie_jar = array(
  419. $data['cookieprefix'].'UserName' => $data['lgusername'],
  420. $data['cookieprefix'].'UserID' => $data['lguserid'],
  421. $data['cookieprefix'].'Token' => $data['lgtoken'],
  422. $data['cookieprefix'].'_session' => $data['sessionid'],
  423. );
  424. }
  425. /**
  426. * Check if we're allowed to edit $page.
  427. * See http://en.wikipedia.org/wiki/Template:Bots
  428. * for more info.
  429. * @param $page The page we want to edit.
  430. * @param $user The bot's username.
  431. * @return bool
  432. **/
  433. function nobots ($page,$user=null,$text=null) {
  434. if ($text == null) {
  435. $text = $this->getpage($page);
  436. }
  437. if (preg_match('/\{\{(nobots|bots\|allow=none|bots\|deny=all|bots\|optout=all|bots\|deny=.*?'.preg_quote($user,'/').'.*?)\}\}/iS',$text))
  438. return false;
  439. if (preg_match('/\{\{(bots\|allow=all|bots\|allow=.*?'.preg_quote($user,'/').'.*?)\}\}/iS', $text))
  440. return true;
  441. if (preg_match('/\{\{(bots\|allow=.*?)\}\}/iS', $text))
  442. return false;
  443. return true;
  444. }
  445. /**
  446. * This function returns the edit token for the current user.
  447. * @return edit token.
  448. **/
  449. function getedittoken () {
  450. $x = $this->query('?action=query&prop=info&intoken=edit&titles=Main%20Page&format=php');
  451. foreach ($x['query']['pages'] as $ret) {
  452. return $ret['edittoken'];
  453. }
  454. }
  455. /**
  456. * Purges the cache of $page.
  457. * @param $page The page to purge.
  458. * @return Api result.
  459. **/
  460. function purgeCache($page) {
  461. return $this->query('?action=purge&titles='.urlencode($page).'&format=php');
  462. }
  463. /**
  464. * Checks if $user has email enabled.
  465. * Uses index.php.
  466. * @param $user The user to check.
  467. * @return bool.
  468. **/
  469. function checkEmail($user) {
  470. $x = $this->query('?action=query&meta=allmessages&ammessages=noemailtext|notargettext&amlang=en&format=php');
  471. $messages[0] = $x['query']['allmessages'][0]['*'];
  472. $messages[1] = $x['query']['allmessages'][1]['*'];
  473. $page = $this->http->get(str_replace('api.php','index.php',$this->url).'?title=Special:EmailUser&target='.urlencode($user));
  474. if (preg_match('/('.preg_quote($messages[0],'/').'|'.preg_quote($messages[1],'/').')/i',$page)) {
  475. return false;
  476. } else {
  477. return true;
  478. }
  479. }
  480. /**
  481. * Returns all the pages $page is transcluded on.
  482. * @param $page The page to get the transclusions from.
  483. * @param $sleep The time to sleep between requets (set to null to disable).
  484. * @return array.
  485. **/
  486. function getTransclusions($page,$sleep=null,$extra=null) {
  487. $continue = '';
  488. $pages = array();
  489. while (true) {
  490. $ret = $this->query('?action=query&list=embeddedin&eititle='.urlencode($page).$continue.$extra.'&eilimit=500&format=php');
  491. if ($sleep != null) {
  492. sleep($sleep);
  493. }
  494. foreach ($ret['query']['embeddedin'] as $x) {
  495. $pages[] = $x['title'];
  496. }
  497. if (isset($ret['query-continue']['embeddedin']['eicontinue'])) {
  498. $continue = '&eicontinue='.$ret['query-continue']['embeddedin']['eicontinue'];
  499. } else {
  500. return $pages;
  501. }
  502. }
  503. }
  504. /**
  505. * Edits a page.
  506. * @param $page Page name to edit.
  507. * @param $data Data to post to page.
  508. * @param $summary Edit summary to use.
  509. * @param $minor Whether or not to mark edit as minor. (Default false)
  510. * @param $bot Whether or not to mark edit as a bot edit. (Default true)
  511. * @return api result
  512. **/
  513. function edit ($page,$data,$summary = '',$minor = false,$bot = true,$section = null,$detectEC=false,$maxlag='') {
  514. if ($this->token==null) {
  515. $this->token = $this->getedittoken();
  516. }
  517. $params = array(
  518. 'title' => $page,
  519. 'text' => $data,
  520. 'token' => $this->token,
  521. 'summary' => $summary,
  522. ($minor?'minor':'notminor') => '1',
  523. ($bot?'bot':'notbot') => '1'
  524. );
  525. if ($section != null) {
  526. $params['section'] = $section;
  527. }
  528. if ($this->ecTimestamp != null && $detectEC == true) {
  529. $params['basetimestamp'] = $this->ecTimestamp;
  530. $this->ecTimestamp = null;
  531. }
  532. if ($maxlag!='') {
  533. $maxlag='&maxlag='.$maxlag;
  534. }
  535. $sleep=10;
  536. do{
  537. $return = $this->query('?action=edit&format=php'.$maxlag,$params);
  538. //if we didnt get a maxlag error we are done
  539. if($return['error']['code'] != "maxlag"){return $return;}
  540. if($sleep < 60) {$sleep = $sleep + 1;}
  541. echo "\nSleeping for $sleep due to Maxlag! ".$return['error']['info']." seconds\n";
  542. sleep($sleep);
  543. } while(true);
  544. }
  545. /**
  546. * Add a text at the bottom of a page
  547. * @param $page The page we're working with.
  548. * @param $text The text that you want to add.
  549. * @param $summary Edit summary to use.
  550. * @param $minor Whether or not to mark edit as minor. (Default false)
  551. * @param $bot Whether or not to mark edit as a bot edit. (Default true)
  552. * @return api result
  553. **/
  554. function addtext( $page, $text, $summary = '', $minor = false, $bot = true )
  555. {
  556. $data = $this->getpage( $page );
  557. $data.= "\n" . $text;
  558. return $this->edit( $page, $data, $summary, $minor, $bot );
  559. }
  560. /**
  561. * Moves a page.
  562. * @param $old Name of page to move.
  563. * @param $new New page title.
  564. * @param $reason Move summary to use.
  565. * @param $movetalk Move the page's talkpage as well.
  566. * @return api result
  567. **/
  568. function move ($old,$new,$reason,$options=null) {
  569. if ($this->token==null) {
  570. $this->token = $this->getedittoken();
  571. }
  572. $params = array(
  573. 'from' => $old,
  574. 'to' => $new,
  575. 'token' => $this->token,
  576. 'reason' => $reason
  577. );
  578. if ($options != null) {
  579. $option = explode('|',$options);
  580. foreach ($option as $o) {
  581. $params[$o] = true;
  582. }
  583. }
  584. return $this->query('?action=move&format=php',$params);
  585. }
  586. /**
  587. * Rollback an edit.
  588. * @param $title Title of page to rollback.
  589. * @param $user Username of last edit to the page to rollback.
  590. * @param $reason Edit summary to use for rollback.
  591. * @param $bot mark the rollback as bot.
  592. * @return api result
  593. **/
  594. function rollback ($title,$user,$reason=null,$bot=false) {
  595. $ret = $this->query('?action=query&prop=revisions&rvtoken=rollback&titles='.urlencode($title).'&format=php');
  596. foreach ($ret['query']['pages'] as $x) {
  597. $token = $x['revisions'][0]['rollbacktoken'];
  598. break;
  599. }
  600. $params = array(
  601. 'title' => $title,
  602. 'user' => $user,
  603. 'token' => $token
  604. );
  605. if ($bot) {
  606. $params['markbot'] = true;
  607. }
  608. if ($reason != null) { $params['summary'] = $reason; }
  609. return $this->query('?action=rollback&format=php',$params);
  610. }
  611. /**
  612. * Blocks a user.
  613. * @param $user The user to block.
  614. * @param $reason The block reason.
  615. * @param $expiry The block expiry.
  616. * @param $options a piped string containing the block options.
  617. * @return api result
  618. **/
  619. function block ($user,$reason='vand',$expiry='infinite',$options=null,$retry=true) {
  620. if ($this->token==null) {
  621. $this->token = $this->getedittoken();
  622. }
  623. $params = array(
  624. 'expiry' => $expiry,
  625. 'user' => $user,
  626. 'reason' => $reason,
  627. 'token' => $this->token
  628. );
  629. if ($options != null) {
  630. $option = explode('|',$options);
  631. foreach ($option as $o) {
  632. $params[$o] = true;
  633. }
  634. }
  635. $ret = $this->query('?action=block&format=php',$params);
  636. /* Retry on a failed token. */
  637. if ($retry and $ret['error']['code']=='badtoken') {
  638. $this->token = $this->getedittoken();
  639. return $this->block($user,$reason,$expiry,$options,false);
  640. }
  641. return $ret;
  642. }
  643. /**
  644. * Unblocks a user.
  645. * @param $user The user to unblock.
  646. * @param $reason The unblock reason.
  647. * @return api result
  648. **/
  649. function unblock ($user,$reason) {
  650. if ($this->token==null) {
  651. $this->token = $this->getedittoken();
  652. }
  653. $params = array(
  654. 'user' => $user,
  655. 'reason' => $reason,
  656. 'token' => $this->token
  657. );
  658. return $this->query('?action=unblock&format=php',$params);
  659. }
  660. /**
  661. * Emails a user.
  662. * @param $target The user to email.
  663. * @param $subject The email subject.
  664. * @param $text The body of the email.
  665. * @param $ccme Send a copy of the email to the user logged in.
  666. * @return api result
  667. **/
  668. function email ($target,$subject,$text,$ccme=false) {
  669. if ($this->token==null) {
  670. $this->token = $this->getedittoken();
  671. }
  672. $params = array(
  673. 'target' => $target,
  674. 'subject' => $subject,
  675. 'text' => $text,
  676. 'token' => $this->token
  677. );
  678. if ($ccme) {
  679. $params['ccme'] = true;
  680. }
  681. return $this->query('?action=emailuser&format=php',$params);
  682. }
  683. /**
  684. * Deletes a page.
  685. * @param $title The page to delete.
  686. * @param $reason The delete reason.
  687. * @return api result
  688. **/
  689. function delete ($title,$reason) {
  690. if ($this->token==null) {
  691. $this->token = $this->getedittoken();
  692. }
  693. $params = array(
  694. 'title' => $title,
  695. 'reason' => $reason,
  696. 'token' => $this->token
  697. );
  698. return $this->query('?action=delete&format=php',$params);
  699. }
  700. /**
  701. * Undeletes a page.
  702. * @param $title The page to undelete.
  703. * @param $reason The undelete reason.
  704. * @return api result
  705. **/
  706. function undelete ($title,$reason) {
  707. if ($this->token==null) {
  708. $this->token = $this->getedittoken();
  709. }
  710. $params = array(
  711. 'title' => $title,
  712. 'reason' => $reason,
  713. 'token' => $this->token
  714. );
  715. return $this->query('?action=undelete&format=php',$params);
  716. }
  717. /**
  718. * (Un)Protects a page.
  719. * @param $title The page to (un)protect.
  720. * @param $protections The protection levels (e.g. 'edit=autoconfirmed|move=sysop')
  721. * @param $expiry When the protection should expire (e.g. '1 day|infinite')
  722. * @param $reason The (un)protect reason.
  723. * @param $cascade Enable cascading protection? (defaults to false)
  724. * @return api result
  725. **/
  726. function protect ($title,$protections,$expiry,$reason,$cascade=false) {
  727. if ($this->token==null) {
  728. $this->token = $this->getedittoken();
  729. }
  730. $params = array(
  731. 'title' => $title,
  732. 'protections' => $protections,
  733. 'expiry' => $expiry,
  734. 'reason' => $reason,
  735. 'token' => $this->token
  736. );
  737. if ($cascade) {
  738. $params['cascade'] = true;
  739. }
  740. return $this->query('?action=protect&format=php',$params);
  741. }
  742. /**
  743. * Uploads an image.
  744. * @param $page The destination file name.
  745. * @param $file The local file path.
  746. * @param $desc The upload discrption (defaults to '').
  747. **/
  748. function upload ($page,$file,$desc='') {
  749. if ($this->token == null) {
  750. $this->token = $this->getedittoken();
  751. }
  752. $params = array(
  753. 'filename' => $page,
  754. 'comment' => $desc,
  755. 'text' => $desc,
  756. 'token' => $this->token,
  757. 'ignorewarnings' => '1',
  758. 'file' => '@'.$file
  759. );
  760. return $this->query('?action=upload&format=php',$params);
  761. }
  762. /*
  763. $page - page
  764. $revs - rev ids to delete (seperated with ,)
  765. $comment - delete comment
  766. */
  767. function revdel ($page,$revs,$comment) {
  768. if ($this->token==null) {
  769. $this->token = $this->getedittoken();
  770. }
  771. $post = array(
  772. 'wpEditToken' => $this->token,
  773. 'ids' => $revs,
  774. 'target' => $page,
  775. 'type' => 'revision',
  776. 'wpHidePrimary' => 1,
  777. 'wpHideComment' => 1,
  778. 'wpHideUser' => 0,
  779. 'wpRevDeleteReasonList' => 'other',
  780. 'wpReason' => $comment,
  781. 'wpSubmit' => 'Apply to selected revision(s)'
  782. );
  783. return $this->http->post(str_replace('api.php','index.php',$this->url).'?title=Special:RevisionDelete&action=submit',$post);
  784. }
  785. /**
  786. * Creates a new account.
  787. * Uses index.php as there is no api to create accounts yet :(
  788. * @param $username The username the new account will have.
  789. * @param $password The password the new account will have.
  790. * @param $email The email the new account will have.
  791. **/
  792. function createaccount ($username,$password,$email=null) {
  793. $post = array(
  794. 'wpName' => $username,
  795. 'wpPassword' => $password,
  796. 'wpRetype' => $password,
  797. 'wpEmail' => $email,
  798. 'wpRemember' => 0,
  799. 'wpIgnoreAntiSpoof' => 0,
  800. 'wpCreateaccount' => 'Create account',
  801. );
  802. return $this->http->post(str_replace('api.php','index.php',$this->url).'?title=Special:UserLogin&action=submitlogin&type=signup',$post);
  803. }
  804. /**
  805. * Changes a users rights.
  806. * @param $user The user we're working with.
  807. * @param $add A pipe-separated list of groups you want to add.
  808. * @param $remove A pipe-separated list of groups you want to remove.
  809. * @param $reason The reason for the change (defaults to '').
  810. **/
  811. function userrights ($user,$add,$remove,$reason='') {
  812. // get the userrights token
  813. $token = $this->query('?action=query&list=users&ususers='.urlencode($user).'&ustoken=userrights&format=php');
  814. $token = $token['query']['users'][0]['userrightstoken'];
  815. $params = array(
  816. 'user' => $user,
  817. 'token' => $token,
  818. 'add' => $add,
  819. 'remove' => $remove,
  820. 'reason' => $reason
  821. );
  822. return $this->query('?action=userrights&format=php',$params);
  823. }
  824. /**
  825. * Gets the number of images matching a particular sha1 hash.
  826. * @param $hash The sha1 hash for an image.
  827. * @return The number of images with the same sha1 hash.
  828. **/
  829. function imagematches ($hash) {
  830. $x = $this->query('?action=query&list=allimages&format=php&aisha1='.$hash);
  831. return count($x['query']['allimages']);
  832. }
  833. /** BMcN 2012-09-16
  834. * Retrieve a media file's actual location.
  835. * @param $page The "File:" page on the wiki which the URL of is desired.
  836. * @return The URL pointing directly to the media file (Eg http://upload.mediawiki.org/wikipedia/en/1/1/Example.jpg)
  837. **/
  838. function getfilelocation ($page) {
  839. $x = $this->query('?action=query&format=php&prop=imageinfo&titles='.urlencode($page).'&iilimit=1&iiprop=url');
  840. foreach ($x['query']['pages'] as $ret ) {
  841. if (isset($ret['imageinfo'][0]['url'])) {
  842. return $ret['imageinfo'][0]['url'];
  843. } else
  844. return false;
  845. }
  846. }
  847. /** BMcN 2012-09-16
  848. * Retrieve a media file's uploader.
  849. * @param $page The "File:" page
  850. * @return The user who uploaded the topmost version of the file.
  851. **/
  852. function getfileuploader ($page) {
  853. $x = $this->query('?action=query&format=php&prop=imageinfo&titles='.urlencode($page).'&iilimit=1&iiprop=user');
  854. foreach ($x['query']['pages'] as $ret ) {
  855. if (isset($ret['imageinfo'][0]['user'])) {
  856. return $ret['imageinfo'][0]['user'];
  857. } else
  858. return false;
  859. }
  860. }
  861. /**
  862. * Gets the date of the last edit the user has made
  863. * @param $user The username for which to get the last edit
  864. * @return The timestamp of the last edit e.g. 2012-02-28T13:10:12Z
  865. * @author Addshore
  866. **/
  867. function lastedit ($user) {
  868. $x = $this->query('?action=query&list=usercontribs&uclimit=1&format=php&acuser='.urlencode($user));
  869. return $x['query']['usercontribs'][0]['timestamp'];
  870. }
  871. /**
  872. * Gets the time stamp of the last edit a user made on an article
  873. * @param $user The username for which to get the last edit
  874. * @param $page The page we want to check
  875. * @return The timestamp of the last edit e.g. 2012-02-28T13:10:12Z
  876. * @author Addshore
  877. **/
  878. function lasteditonpage ($user,$page) {
  879. $x = $this->query('?action=query&prop=revisions&format=php&titles='.urlencode($page).'&rvlimit=1&rvuser='.urlencode($user));
  880. return $x['query']['pages'][0]['revisions'][0]['timestamp'];
  881. }
  882. /**
  883. * Gets the date of the last edit the user has made
  884. * @param $user The username for which to get the first edit
  885. * @return The timestamp of the last edit e.g. 2012-02-28T13:10:12Z
  886. * @author Addshore
  887. **/
  888. function firstedit ($user) {
  889. $x = $this->query('?action=query&list=usercontribs&uclimit=1&format=php&acuser='.urlencode($user));
  890. return $x['query-continue'][0]['ucstart'];
  891. }
  892. /**
  893. * Gets the date of the last edit the user has made
  894. * @param $page The name of the page to be checks
  895. * @return Array of each protection status ([0]/[1]/[2])([type],[level],[expiry])
  896. * @author Addshore
  897. **/
  898. function protectionstatus ($page){
  899. //http://en.wikipedia.org/w/api.php?action=query&titles=Cyprus&prop=info&inprop=protection
  900. $x = $this->query('?action=query&format=php&prop=info&inprop=protection&titles='.urlencode($page));
  901. foreach ($x['query']['pages'] as $page)
  902. {
  903. return $page['protection'];
  904. }
  905. }
  906. /**
  907. * Gets the date of the last edit the user has made
  908. * @param $page The name of the page to be checked
  909. * @return Array of entities the page can be found in on wikidata
  910. * @author Addshore
  911. **/
  912. function wikidatasitelinks ($page,$lang = "en"){
  913. $x = $this->http->get("http://wikidata.org/w/api.php?action=wbgetentities&sites=".urlencode($lang)."wiki&props=sitelinks&format=php&titles=".urlencode($page));
  914. $x = unserialize($x);
  915. if($x['success'] == 1)
  916. {
  917. return $x['entities'];
  918. }
  919. }
  920. }
  921. /**
  922. * This class extends the wiki class to provide an high level API for the most commons actions.
  923. * @author Fale
  924. **/
  925. class extended extends wikipedia
  926. {
  927. /**
  928. * Add a category to a page
  929. * @param $page The page we're working with.
  930. * @param $category The category that you want to add.
  931. * @param $summary Edit summary to use.
  932. * @param $minor Whether or not to mark edit as minor. (Default false)
  933. * @param $bot Whether or not to mark edit as a bot edit. (Default true)
  934. * @return api result
  935. **/
  936. function addcategory( $page, $category, $summary = '', $minor = false, $bot = true )
  937. {
  938. $data = $this->getpage( $page );
  939. $data.= "\n[[Category:" . $category . "]]";
  940. return $this->edit( $page, $data, $summary, $minor, $bot );
  941. }
  942. /**
  943. * Find a string
  944. * @param $page The page we're working with.
  945. * @param $string The string that you want to find.
  946. * @return bool value (1 found and 0 not-found)
  947. **/
  948. function findstring( $page, $string )
  949. {
  950. $data = $this->getpage( $page );
  951. if( strstr( $data, $string ) )
  952. return 1;
  953. else
  954. return 0;
  955. }
  956. /**
  957. * Replace a string
  958. * @param $page The page we're working with.
  959. * @param $string The string that you want to replace.
  960. * @param $newstring The string that will replace the present string.
  961. * @return the new text of page
  962. **/
  963. function replacestring( $page, $string, $newstring )
  964. {
  965. $data = $this->getpage( $page );
  966. return str_replace( $string, $newstring, $data );
  967. }
  968. /**
  969. * Get a template from a page
  970. * @param $page The page we're working with
  971. * @param $template The name of the template we are looking for
  972. * @return the searched (NULL if the template has not been found)
  973. **/
  974. function gettemplate( $page, $template ) {
  975. $data = $this->getpage( $page );
  976. $template = preg_quote( $template, " " );
  977. $r = "/{{" . $template . "(?:[^{}]*(?:{{[^}]*}})?)+(?:[^}]*}})?/i";
  978. preg_match_all( $r, $data, $matches );
  979. if( isset( $matches[0][0] ) )
  980. return $matches[0][0];
  981. else
  982. return NULL;
  983. }
  984. }
  985. /**
  986. * This class extends the wiki class to provide an high level API for the most commons actions.
  987. * @author Sean
  988. */
  989. class lyricwiki extends extended
  990. {
  991. /**
  992. * Constructor. Note that the parameters are in different order (primarily because it
  993. * is very unlikely the user will want to change the URL of LyricWiki, and fairly likely
  994. * that they may want to set a username/password).
  995. *
  996. * If not logging in, the caller can safely call this with no parameters.
  997. */
  998. function __construct ($hu=null, $hp=null, $url='http://lyrics.wikia.com/api.php') {
  999. parent::__construct($url,$hu,$hp);
  1000. }
  1001. /**
  1002. * Returns basic info about a song as well as a fair-use snippet of
  1003. * lyrics and a link to the LyricWiki page where the full lyrics can
  1004. * be viewed (full lyrics are no longer available via the API because
  1005. * of licensing issues - publishers won't allow that for free).
  1006. *
  1007. * @return associative array containing the song data. (TODO: Document what happens on No Match)
  1008. */
  1009. function getSong($artist, $song){
  1010. return $this->query('?action=lyrics&func=getSong&fmt=php&artist='.urlencode($artist).'&song='.urlencode($song));
  1011. } // end getSong()
  1012. /**
  1013. * Returns basic info about an artist as well as their
  1014. * entire discography.
  1015. *
  1016. * @param - artist - a string containing the name of the artist (spaces or underscores are fine)
  1017. * @return an associative array containing the artist data (TODO: Document what happens on No Match)
  1018. */
  1019. function getArtist($artist){
  1020. return $this->query('?action=lyrics&func=getArtist&fmt=php&artist='.urlencode($artist));
  1021. } // end getArtist()
  1022. /**
  1023. * Returns hometown information (country, state/province, hometown) for
  1024. * the given artist if it can be found.
  1025. *
  1026. *
  1027. * @param - artist - a string containing the name of the artist (spaces or underscores are fine)
  1028. * @return an associative array whose keys are 'country', 'state', and 'hometown'. If the
  1029. * artist's hometown could not be found, then the values corresponding to those keys will
  1030. * be empty strings.
  1031. */
  1032. function getHometown($artist){
  1033. return $this->query('?action=lyrics&func=getHometown&fmt=php&artist='.urlencode($artist));
  1034. } // end getHometown()
  1035. /**
  1036. * Gets information about the Song Of The Day (SOTD).
  1037. *
  1038. * @return an associative array representing the current Song of the Day. The
  1039. * fields returned include {artist, song, lyrics (just a snippet), url of song,
  1040. * page_namespace, page_id, isOnTakedownList, nominatedBy, reason}
  1041. */
  1042. function getSotd(){
  1043. return $this->query('?action=lyrics&func=getSotd&fmt=php');
  1044. } // end getSotd()
  1045. /**
  1046. * Returns information about the currently most-popular songs.
  1047. *
  1048. * @return an array containing associative arrays of the data for each top song.
  1049. */
  1050. function getTopSongs(){
  1051. return $this->query('?action=lyrics&func=getTopSongs&fmt=php');
  1052. } // end getTopSongs()
  1053. /**
  1054. * Allows easy submission of lyrics for a new song to LyricWiki.
  1055. *
  1056. * @param artist - the artist of the song
  1057. * @param song - the name of the song (following page naming conventions: http://lyrics.wikia.com/LW:PN ).
  1058. * @param lyrics - the full lyrics to submit
  1059. * @param language - optional string for the language (the language name should be in English - eg: "Swedish" not "Svenska").
  1060. * @param onAlbums - an array which contains strings which are album titles.
  1061. * @param overwriteIfExists - true means that this page data (the lyrics and onAlbums) will overwrite
  1062. * the page if it already exists. false means that nothing will be changed if the
  1063. * page already exists.
  1064. */
  1065. function postSong($artist, $song, $lyrics, $language="", $onAlbums=array(), $overwriteIfExists=false){
  1066. $params = array(
  1067. 'artist' => $artist,
  1068. 'song' => $song,
  1069. 'lyrics' => $lyrics,
  1070. 'overwriteIfExists' => ($overwriteIfExists ? "1" : "0"),
  1071. );
  1072. if(!empty($language)){
  1073. $params['language'] = $language;
  1074. }
  1075. if(count($onAlbums) > 0){
  1076. $params['onAlbums'] = implode("|", $onAlbums);
  1077. }
  1078. return $this->query('?action=lyrics&func=postSong&fmt=php',$params);
  1079. } // end postSong()
  1080. } // end class lyricwiki