User:AnomieBOT/source/tasks/POTDPageDeleter.pm
Appearance
Approved 2019-03-17 Wikipedia:Bots/Requests for approval/AnomieBOT III 5 |
package tasks::POTDPageDeleter;
=pod
=begin metadata
Bot: AnomieBOT III
Task: POTDPageDeleter
BRFA: Wikipedia:Bots/Requests for approval/AnomieBOT III 5
Status: Approved 2019-03-17
Created: 2019-03-01
Delete Template:POTD protected/YYYY-MM-DD (per [[WP:CSD#G6]]) 30 days after the
listed date, and remove the "See also" link from the corresponding
Template:POTD/YYYY-MM-DD.
=end metadata
=cut
use utf8;
use strict;
use AnomieBOT::Task;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;
use POSIX qw/strftime/;
use Data::Dumper;
my $screwup;
my @reasons = (
'[[WP:CSD#G6|G6]]: Obsolete POTD protected subpage (older than 30 days).',
'[[WP:CSD#G8|G8]]: Subpage of a deleted page.',
'[[WP:CSD#G8|G8]]: Talk page of a deleted page.',
);
sub new {
my $class=shift;
my $self=$class->SUPER::new;
bless $self, $class;
return $self;
}
=pod
=for info
Approved 2019-03-17<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT III 5]]
=cut
sub approved {
return 500;
}
sub run {
my ($self, $api)=@_;
$api->task('POTDPageDeleter', 0, 10, qw(d::Talk d::Nowiki));
$screwup='If this bot is malfunctioning, please report it at [[User:'.$api->user.'/shutoff/POTDPageDeleter]]';
my $endtime = time() + 300;
my @when = gmtime;
$when[3] -= 30;
my $when = strftime( '%F', @when );
# Main templates first, and their sub and talk pages.
my $iter = $api->iterator(
list => 'allpages',
apnamespace => 10,
apprefix => 'POTD protected/',
aplimit => 'max',
);
while(my $p=$iter->next){
return 0 if $api->halting;
return 0 if time() >= $endtime; # Give other tasks a chance to run
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve POTD pages for deletion: ".$p->{'error'}."\n");
return 60;
}
my $title = $p->{'title'};
next unless $title =~ m{^Template:POTD protected/(\d{4}-\d{2}-\d{2})$};
my $dt = $1;
next unless $dt lt $when;
my ($res, $key, $reason) = $self->do_delete( $api, $title, 0 );
if ( $res ) {
return $res if $res > 0;
next;
}
my $title2 = "Template:POTD/$dt";
my $tok=$api->edittoken( $title2, EditRedir => 1, links => { titles => $title } );
if ( $tok->{'code'} eq 'shutoff' ) {
$api->warn( "Task disabled: " . $tok->{'content'} . "\n" );
return 300;
}
if ( $tok->{'code'} ne 'success' ) {
$api->warn( "Failed to get edit token for $title2: " . $tok->{'error'} . "\n" );
return 60;
}
if ( exists( $tok->{'missing'} ) ) {
$api->warn( "WTF? $title2 is missing!\n" );
return 60;
}
next unless @{$tok->{'links'} // []};
my $intxt = $tok->{'revisions'}[0]{'slots'}{'main'}{'*'};
my $outtxt = $intxt;
$outtxt =~ s!\n\*\s*\[\[Template:POTD(?:[ _]protected|\{\{#ifeq:\{\{BASEPAGENAME\}\}\|POTD protected\|\|(?:_| )protected\}\})/(?:\{\{SUBPAGENAME\}\}|$dt)(?:[/|][^]]*)?\]\][ \t]*!!g;
$outtxt =~ s!\n==\s*See also\s*==\s*(?=(?:\[\[Category:[^]]+\]\]\s*|<\!--(?>.*?-->)\s*)*(?:</noinclude>|$))!\n!;
$outtxt =~ s!<noinclude>\s*</noinclude>\s*$!!;
if ( $intxt ne $outtxt ) {
$api->log( "Removing reference to [[Template:POTD protected/$dt]] from $title2" );
$res = $api->edit( $tok, $outtxt, "Removing reference to deleted [[Template:POTD protected/$dt]]. $screwup", 0, 1 );
if($res->{'code'} ne 'success'){
$api->warn( "Write failed on $title2: " . $res->{'error'} . "\n" );
return 60;
}
} else {
$api->warn( "Failed to find link to remove in $title2\n" );
}
}
# Any leftover subpages.
$iter = $api->iterator(
list => 'allpages',
apnamespace => 10,
apprefix => 'POTD protected/',
aplimit => 'max',
);
while(my $p=$iter->next){
return 0 if $api->halting;
return 0 if time() >= $endtime; # Give other tasks a chance to run
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve POTD pages for deletion: ".$p->{'error'}."\n");
return 60;
}
my $title = $p->{'title'};
next unless $title =~ m{^Template:POTD protected/(\d{4}-\d{2}-\d{2})/.*$};
next unless $1 lt $when;
my $tt = $title;
my @tt = ();
while ( 1 ) {
$tt =~ s!/[^/]*$!!;
last if $tt =~ m{^Template:POTD protected/\d{4}-\d{2}-\d{2}$};
push @tt, $tt;
}
my $r = $api->query( titles => join( '|', @tt ), formatversion => 2 );
next if grep { !exists( $_->{'missing'} ) } @{$r->{'query'}{'pages'}};
my ($res, $key, $reason) = $self->do_delete( $api, $title, 1 );
if ( $res ) {
return $res if $res > 0;
next;
}
}
# Any leftover talk pages.
$iter = $api->iterator(
generator => 'allpages',
gapnamespace => 11,
gapprefix => 'POTD protected/',
gaplimit => 'max',
prop => 'info',
inprop => 'subjectid'
);
while(my $p=$iter->next){
return 0 if $api->halting;
return 0 if time() >= $endtime; # Give other tasks a chance to run
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve POTD talk pages for deletion: ".$p->{'error'}."\n");
return 60;
}
next if exists( $p->{'subjectid'} );
my $title = $p->{'title'};
next unless $title =~ m{^Template talk:POTD protected/(\d{4}-\d{2}-\d{2})(?:/.*)?$};
next unless $1 lt $when;
my ($res, $key, $reason) = $self->do_delete( $api, $title, 2 );
if ( $res ) {
return $res if $res > 0;
next;
}
}
return $self->nexttime();
}
sub do_delete {
my ($self, $api, $title, $what) = @_;
my $tok = $api->gettoken( 'csrf', Title => $title, EditRedir => 1, templates => { templates => 'Template:G8-exempt' } );
if ( $tok->{'code'} eq 'shutoff' ) {
$api->warn( "Task disabled: " . $tok->{'content'} . "\n" );
return (300, undef);
}
if ( $tok->{'code'} eq 'botexcluded' ) {
$api->warn( "Bot excluded from $title: " . $tok->{'error'} . "\n" );
return (-1, 'skip', 'bot excluded');
}
if ( $tok->{'code'} ne 'success' ) {
$api->warn( "Failed to get delete token for $title: " . $tok->{'error'} . "\n" );
return (-1, 'fail', "couldn't fetch delete token");
}
if ( exists( $tok->{'missing'} ) ) {
#$api->log("$title no longer exists, skipping");
return (0, 'ok', "no longer exists");
}
if ( $what > 0 && @{$tok->{'templates'} // []} ) {
#$api->log("$title is G8-exempt");
return (-1, 'skip', "marked G8-exempt");
}
my $reason = $reasons[$what];
$api->log( "Deleting $title: $reason" );
my $res = $api->action( $tok,
action => 'delete',
title => $title,
reason => "$reason $screwup",
);
if ( $res->{'code'} ne 'success' ) {
$api->warn( "Failed to delete $title: " . $res->{'error'} . "\n" );
return (-1, 'fail', 'delete failed');
}
if ( $what < 2 ) {
my $talk = $title;
$talk =~ s/^Template:/Template talk:/;
my ($res, $key, $reason) = $self->do_delete( $api, $talk, 2 );
return ($res, $key, $reason) if $res;
}
if ( $what == 0 ) {
# Delete subpages of deleted page
my $t = $title;
$t =~ s/^Template://;
my $iter = $api->iterator(
list => 'allpages',
apnamespace => 10,
apprefix => "$t/",
aplimit => 'max',
);
my %skip = ();
ITER: while( my $p = $iter->next ) {
if ( !$p->{'_ok_'} ) {
$api->warn( "Failed to retrieve subpages of $title for deletion: " . $p->{'error'} . "\n" );
last;
}
my @parts = split( m!/!, $p->{'title'} );
for ( my $i = 0; $i < @parts; $i++ ) {
my $t = join( '/', @parts[0..$i] );
next ITER if exists( $skip{$t} );
}
if ( exists( $p->{'subjectid'} ) || @{$p->{'templates'} // []} ) {
$skip{$p->{'title'}} = 1;
next ITER;
}
my ($res, $key, $reason) = $self->do_delete( $api, $p->{'title'}, 1 );
$skip{$p->{'title'}} = 1 if $res;
}
}
return ( 0, 'ok', 'ok' );
}
sub nexttime {
my $t = 86400 - ( time % 86400 );
return $t < 60 ? 60 : $t;
}
1;