#!/usr/bin/perl -T #______________________________________________________________________________# #DeleteAsstBot.pl PRE-RELEASE VERSION # # Author Dan Adams , (User:PocklingtonDan) # #______________________________________________________________________________# #______________________________________________________________________________# # CHANGES STILL TO MAKE # # # # - Don't allow use by blocked IPs? # # http://en.wikipedia.org/w/index.php?title=Special%3AIpblocklist&action=search&ip=PLACE_IP_HERE # - Change "ARTICLENAME (nomination N)" to "Articlename (nth nomination)". # # - add question mark/help link next to each input field # # # # RECENT CHANGES # # # # 09.01.06 - Version 0.01 - Initial code written # # 10.01.06 - Version 0.02 - Fixed bug where it wrote bot sig twice. # # 11.01.06 - Version 0.03 - Added edit token system # # # #______________________________________________________________________________# #______________________________________________________________________________# # WHAT THE SCRIPT DOES # # # # This script is a wikipedia bot. It acts as a wizard interface for the process# # of nominating an article for deletion, a currently manual task. It takes # # user input in a wizard interface and makes the necessary wikipedia edits in # # the background. # # # # INTENDED USE # # # # It is intended that ultimately this script is placed on the toolserver and # # becomes the default method of nominating articles for deletion from wikipedia# # # #______________________________________________________________________________# #______________________________________________________________________________# # PACKAGES TO IMPORT (must be installed on server) # #______________________________________________________________________________# # "Use Always" Packages use strict; use warnings; use CGI::Carp "fatalsToBrowser"; # during testing and debugging only # Required Packages for features. use CGI qw(:standard Vars); use URI::Escape; use LWP::Simple; use LWP::UserAgent; use HTML::Template; #______________________________________________________________________________# # SETTINGS # #______________________________________________________________________________# #Variables my $editorName = "**********"; my $editorPassword = "**************"; my $language = "en"; # language code for wikipedia namespace my $enabled = "true"; # set to false to take offline/disable #Local paths my $editTokenFile = "/*********/editToken.txt"; my $inputFormFile = "/**********/inputForm.tmpl"; my $errorFile = "/*********/error.tmpl"; my $respondToUserFile = "/*************/respondToUser.tmpl"; # Web paths my $pathToScript = "http://*********.com/cgi-bin"; my $pathToImages = "http://www.**********.com"; #______________________________________________________________________________# # MAIN ROUTINE # #______________________________________________________________________________# my $action = param('action') or 'getInput'; if ($enabled eq "true") { if ($action eq 'getInput') {getInput;} elsif ($action eq 'processInput') {processInput;} else {error("Unrecognised action request");} } else {error("Bot is currently offline for maintenance.");} exit; #______________________________________________________________________________# # SUBROUTINES # #______________________________________________________________________________# sub getInput { my $remoteHost = $ENV{'REMOTE_ADDR'}; my $editToken = generateEditToken($remoteHost); printInputForm($editToken); return(); } #______________________________________________________________________________# sub processInput { my $editToken = param('editToken'); my $articleForDeletion = param('articleForDeletion'); my $categoryForDeletion = param('categoryForDeletion'); my $reasonForDeletion = param('reasonForDeletion'); validateFilePaths($editTokenFile); validateBasicFormats($editToken, $articleForDeletion, $categoryForDeletion, $reasonForDeletion); validatePageExists($articleForDeletion); validateCategoryCode($categoryForDeletion); my $remoteHost = validateEditToken($editToken); notifyArticleCreator($articleForDeletion, $remoteHost); my $afdUrl = setupDeletionDebatePage($articleForDeletion, $categoryForDeletion, $reasonForDeletion, $remoteHost); tagArticleWithDeletionNotice($articleForDeletion, $remoteHost, $afdUrl); logDeletionNomination($articleForDeletion, $remoteHost, $afdUrl); fileMaintenance($editToken); respondToUser($articleForDeletion, $afdUrl); return(); } #______________________________________________________________________________# sub checkForUnexpectedArgs { my @unexpectedArgs = @_; if (scalar(@unexpectedArgs) > 0) { error("Mismatch between expected and received number of arguments."); } return(); } #______________________________________________________________________________# sub checkFileCanBeAccessed { my($filePath,$requestType, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); if ($requestType eq "READ") { unless (-r $filePath) { error("The file $filePath cannot be read as requested."); } } elsif ($requestType eq "WRITE") { unless (-w $filePath) { error("The file $filePath cannot be written to as requested."); } } elsif ($requestType eq "EXISTS") { unless (-e $filePath) { error("The file $filePath does not exist."); } } else{ error("Invalid file access method was chosen."); } return(); } #______________________________________________________________________________# sub fileMaintenance { my($editToken, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); open(TOKENFILE, '+>', "$editTokenFile") or error("Cannot open edit token file."); # open for both read and write flock(TOKENFILE, 2) or error("Cannot lock edit token file."); my $fileContents = ; chomp($fileContents); my @allLines = split(/\n/, $fileContents); my $currentLineNumber = 0; my $matchLineNumber = 0; foreach my $line (@allLines) { my ($possibleMatchToken, $possibleMatchRemoteHost) = split(/\|/, $line); if ($possibleMatchToken eq $editToken) { $matchLineNumber = $currentLineNumber; } $currentLineNumber++; } splice(@allLines, $matchLineNumber, 1); foreach my $newLine (@allLines) { print TOKENFILE "$newLine\n"; } flock(TOKENFILE, 8); close (TOKENFILE) or error("Cannot close edit token file."); return (); } #______________________________________________________________________________# sub respondToUser { my($articleName, $afdUrl, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $articleURL = uri_escape($articleName, "\0-\377"); my $template = HTML::Template->new(filename => 'respondToUser.tmpl'); $template->param(LANGUAGE => $language); $template->param(ARTICLEURL => $articleURL); $template->param(PATHTOIMAGES => $pathToImages); $template->param(ARTICLENAME => $articleName); $template->param(AFDURL => $afdUrl); print "Content-Type: text/html\n\n", $template->output; return(); } #______________________________________________________________________________# sub writeResultsToPage { my($replacement_page, $replacement_text, $replacement_summary, $additionalTextStyle, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); use LWP::UserAgent; my $agent=LWP::UserAgent->new; $agent->agent('Perlwikipedia/0.90'); $agent->cookie_jar({file=> '.perlwikipedia-cookies'}); my $login = HTTP::Request->new(POST => "http://$language.wikipedia.org/w/index.php?title=Special:Userlogin&action=submitlogin&type=login"); $login->content_type('application/x-www-form-urlencoded'); $login->content("wpName=$editorName&wpPassword=$editorPassword&wpRemember=1&wpLoginattempt=Log+in"); my $logger_inner = $agent->request($login); my $do_redirect=HTTP::Request->new(GET =>'http://$language.wikipedia.org/w/index.php?title=Special:Userlogin&wpCookieCheck=login'); my $redirecter= $agent->request($do_redirect); my $is_success=$redirecter->content; use HTML::Form; my $ua = LWP::UserAgent->new; $ua->agent("Perlwikipedia/0.90"); $ua->cookie_jar($agent->cookie_jar()); my $response = $ua->get("$replacement_page"); my $form = HTML::Form->parse($response); my $existingText = $form->find_input('wpTextbox1')->value; my $summary = $form->find_input('wpSummary')->value; my $save = $form->find_input('wpSave')->value; my $edittoken = $form->find_input('wpEditToken')->value; my $starttime = $form->find_input('wpStarttime')->value; my $edittime = $form->find_input('wpEdittime')->value; my $textToWrite = ""; if ($additionalTextStyle eq "APPENDFIRST") {$textToWrite = $replacement_text . $existingText;} elsif($additionalTextStyle eq "APPENDLAST") {$textToWrite = $existingText . $replacement_text;} elsif ($additionalTextStyle eq "REPLACE") {$textToWrite = $replacement_text} else {error("Unrecognised wikipedia write request.");} $form->value('wpTextbox1', $textToWrite); $form->value('wpSummary', $replacement_summary ); $response = $ua->request($form->click); return(); } #______________________________________________________________________________# sub logDeletionNomination { my($articleForDeletion, $remoteHost, $afdUrl, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $articleURL = uri_escape($articleForDeletion, "\0-\377"); my ($day, $month, $year) = getCurrentDate(); my $pageURL = "http://$language.wikipedia.org/w/index.php?title=Wikipedia:Articles_for_deletion/Log/" . $year . "_" . $month . "_" . $day . "&action=edit"; my $contentToAdd = "{{subst:afd3 | pg=$articleForDeletion}}"; my $editSummary = "Adding [[Wikipedia:$afdUrl]]"; writeResultsToPage($pageURL, $contentToAdd, $editSummary, "APPENDLAST"); return(); } #______________________________________________________________________________# sub getCurrentDate { my @months = qw(January February March April May June July August September October November December); my @weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun); my ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime(); my $currentYear = 1900 + $yearOffset; my $currentMonth = $months[$month]; return ($dayOfMonth, $currentMonth, $currentYear); } #______________________________________________________________________________# sub setupDeletionDebatePage { my($articleForDeletion, $categoryForDeletion, $reasonForDeletion, $remoteHost, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $afdPage = "Articles_for_deletion/$articleForDeletion"; my $numberOfPreviousNoms = getNoOfPreviousNoms($afdPage); my $currentNominationNumber = $numberOfPreviousNoms +1; my $afdPageToCreate = $afdPage . " (nomination $currentNominationNumber)"; my $afdUrlToCreate = uri_escape($afdPageToCreate, "\0-\377"); my $pageURL = "http://$language.wikipedia.org/w/index.php?title=Wikipedia:$afdUrlToCreate&action=edit"; my $contentToAdd = "{{subst:afd2 | pg=$articleForDeletion | cat=$categoryForDeletion | text=$reasonForDeletion}} - [[User:$editorName|$editorName]] (on behalf of IP: $remoteHost) ~~~~~"; my $editSummary = "Creating deletion discussion page for [[$articleForDeletion]] because $reasonForDeletion"; writeResultsToPage($pageURL, $contentToAdd, $editSummary, "REPLACE"); return($afdUrlToCreate); } #______________________________________________________________________________# sub tagArticleWithDeletionNotice { my($articleForDeletion, $remoteHost, $afdUrl, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $articleURL = uri_escape($articleForDeletion, "\0-\377"); my $pageURL = "http://$language.wikipedia.org/w/index.php?title=$articleURL&action=edit"; my $contentToAdd = "{{subst:afd}}"; my $editSummary = "nominated for deletion: see [[Wikipedia:$afdUrl]]"; writeResultsToPage($pageURL, $contentToAdd, $editSummary, "APPENDFIRST"); return(); } #______________________________________________________________________________# sub notifyArticleCreator { my($articleForDeletion, $remoteHost, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $articleCreator = fetchArticleCreator($articleForDeletion); my $pageURL = "http://$language.wikipedia.org/w/index.php?title=User_talk:$articleCreator&action=edit§ion=new"; my $contentToAdd = "{{subst:AFDWarning|$articleForDeletion}} - [[User:$editorName|$editorName]] (on behalf of IP: $remoteHost) ~~~~~"; my $editSummary = "Warning notification - $articleForDeletion proposed for deletion"; writeResultsToPage($pageURL, $contentToAdd, $editSummary, "REPLACE"); return(); } #______________________________________________________________________________# sub fetchArticleCreator { my($articleForDeletion, @unexpectedArgs) = @_; checkForUnexpectedArgs(@unexpectedArgs); my $articleURL = uri_escape($articleForDeletion, "\0-\377"); my $articleHistoryUrl = "http://en.wikipedia.org/w/index.php?title=$articleURL&dir=prev&action=history"; my $browser = LWP::UserAgent->new(); $browser->timeout(60); my $request = HTTP::Request->new(GET => $articleHistoryUrl); my $response = $browser->request($request); my $articleHistoryContents = $response->content(); for ($articleHistoryContents) { s/[\s\S]*?