#!/usr/bin/perl -w use strict; use LWP::UserAgent; use XML::Simple; use Term::ReadKey; use Getopt::Long; use Data::Dumper 'Dumper'; $Data::Dumper::Terse = 1; use Digest::MD5 qw(md5 md5_hex md5_base64); $| = 1; # Ugly regexps for the un-substitution feature: my $category = qr/[ _]*(?i:Category[ _]*:[ _]*T)alk[ _]pages[ _]of[ _](the[ _])?deleted[ _]replaceable[ _]fair[ _]use[ _]images[ _]*/; my $unsubst = qr(
*\n+: *''The following discussion is an archived debate of the proposed deletion of a fair use image as a replaceable image\. +(|)'''Please do not modify it\.''' *''(\s*\[\[$category(\|\{\{PAGENAME\}\})?\]\])? *\n+The result of the debate was (to '''Delete''' the image|'''delete''')\.(\s*\{\{#ifeq:\{\{NAMESPACE\}\}\|Image talk\|\[\[$category\|\{\{PAGENAME\}\}\]\](\s*\{\{G8-exempt\}\})?\}\})?)m; my $username = "Ilmari Karonen's adminbot"; my $server = 'en.wikipedia.org'; my $reason = "image talk pages marked with [[Template:Rtd]] are exempt from [[WP:CSD#G8|CSD G8]] ([[User:Ilmari Karonen's adminbot/Task 1|bot undeletion]])"; GetOptions( 'username|user|u=s' => \$username, 'server|s=s' => \$server, 'summary|m=s' => \$reason, ) and @ARGV or die "Usage: $0 [-u ] [-s ] [-m ] \n"; my $apiURI = "http://$server/w/api.php"; ReadMode 'noecho'; print STDERR "Password for $username \@ $server: "; my $pass = ; chomp $pass; print STDERR "\n"; ReadMode 'restore'; my $ua = LWP::UserAgent->new( agent => "Mozilla/4.0 (compatible; $0)", from => 'vyznev@toolserver.org', cookie_jar => {}, parse_head => 0, ); sub apireq { my $query = [format => 'xml', maxlag => 5, @_]; my $sleep = 5; while (1) { my $res = $ua->post($apiURI, $query); my $err = $res->header('MediaWiki-API-Error') || ""; return XMLin( $res->content ) if $res->is_success and $err ne 'maxlag'; warn "API request failed, ", ($err || $res->status_line), "; sleeping $sleep seconds...\n"; sleep $sleep; $sleep *= 2; } } my $login = apireq( action => 'login', lgname => $username, lgpassword => $pass ); $login->{login}{result} eq 'Success' or die "Login failed: $login->{login}{result}\n"; my $batch = 0; while (my $title = ) { chomp $title; # $title =~ m%(?i:User([ _]talk)?:I)lmari[ _]Karonen\/sandbox% or die "bad title $title"; # for testing unless ($batch) { print STDERR "Enter next batch size: "; $batch = int || 0; exit unless $batch > 0; } $batch--; print STDERR "Getting last revision and undelete token for $title... "; my $data = apireq( action => 'query', list => 'deletedrevs', titles => $title, drprop => 'timestamp|content|token', drlimit => 1, ); my $token = $data->{query}{deletedrevs}{page}{token}; my $timestamp = $data->{query}{deletedrevs}{page}{revisions}{rev}{timestamp}; my $content = $data->{query}{deletedrevs}{page}{revisions}{rev}{content}; $token and $timestamp and $content or die "failed, returned:\n", Dumper($data), "\n"; warn "ok.\n"; my $unsubsted = ($content =~ s/$unsubst[^\n\S]*\n?/{{rtd}}\n/g); die "Error unsubsting $title!\n" if $content =~ /\[\[$category(\]\]|\|)/; warn "-" x 72, "\n", $content, "\n", "-" x 72, "\n"; # sleep 5; next; # for testing, don't actually undelete anything print STDERR "Undeleting $title in 5 seconds... "; sleep 5; my $undelete = apireq( action => 'undelete', title => $title, reason => $reason, token => $token, ); if ($undelete->{error}) { die "error ($undelete->{error}{code}): $undelete->{error}{info}\n"; } else { warn "done!\n"; } next unless $unsubsted; print STDERR "Unsubsting $title in 5 seconds... "; sleep 5; utf8::encode($content); my $edit = apireq( action => 'edit', title => $title, basetimestamp => $timestamp, md5 => md5_hex($content), text => $content, summary => "unsubsting [[Template:Rtd]] after undeletion", minor => 1, bot => 1, token => $token, ); if ($edit->{error}) { die "error ($edit->{error}{code}): $edit->{error}{info}\n"; } elsif ($edit->{edit}{result} ne 'Success') { die "failed ($edit->{edit}{result}):\n", Dumper($edit), "\n"; } else { warn "done!\n"; } }