Approved 2020-02-02 Wikipedia:Bots/Requests for approval/AnomieBOT 78 |
package tasks::TFDTemplateSubster;
=pod
=begin metadata
Bot: AnomieBOT
Task: TFDTemplateSubster
BRFA: Wikipedia:Bots/Requests for approval/AnomieBOT 78
Status: Approved 2020-02-02
Created: 2020-01-31
Subst templates to implement the results of a deletion discussion, based on listing at
[[User:AnomieBOT/TFDTemplateSubster]].
=end metadata
=cut
use utf8;
use strict;
use Data::Dumper;
use JSON;
use AnomieBOT::Task qw/:time/;
use tasks::TemplateSubster::Base;
use vars qw/@ISA/;
@ISA=qw/tasks::TemplateSubster::Base/;
my $workPage = 'User:AnomieBOT/TFDTemplateSubster';
my $workPageTalk = 'User talk:AnomieBOT/TFDTemplateSubster';
sub new {
my $class=shift;
my $self=$class->SUPER::new();
$self->{'tfdMap'} = undef;
bless $self, $class;
return $self;
}
=pod
=for info
Approved 2020-02-02<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT 78]]
=cut
sub approved {
return 3;
}
sub run {
my ($self, $api)=@_;
my $res;
$api->task('TFDTemplateSubster',0,10,qw/d::Redirects d::Templates d::Talk d::IWNS/);
$res = $self->fetchSig( $api );
return $res if defined( $res );
# Spend a max of 5 minutes on this task before restarting
my $endtime = time() + 300;
# Load and check work page
$res = $api->query(
titles => $workPage,
prop => 'revisions|info',
inprop => 'protection',
rvprop => 'content',
rvslots => 'main',
rvlimit => 1,
formatversion => 2,
);
if ( $res->{'code'} ne 'success' ) {
$api->warn( "Failed to get work list: " . $res->{'error'} . "\n" );
return 60;
}
$res = $res->{'query'}{'pages'}[0];
if ( exists( $res->{'missing'} ) ) {
$api->warn( "Work list does not exist, WTF?\n" );
return 3600;
}
my $ok=0;
foreach ( @{$res->{'protection'}} ) {
$ok=1 if($_->{'type'} eq 'edit' && ($_->{'level'} eq 'sysop' || $_->{'level'} eq 'templateeditor'));
}
if ( !$ok ) {
$api->whine( "Work page is unprotected", "The work page must be fully protected or template-protected. Please have the page protected..", Pagename => $workPageTalk );
return 300;
}
# Extract work list
my %workList = ();
my $fail = 0;
$api->process_templates( $res->{'revisions'}[0]{'slots'}{'main'}{'content'}, sub {
return undef if $fail;
my $name=shift;
my $params=shift;
return undef unless $name eq '/row';
my %data = ();
foreach ( $api->process_paramlist( @$params ) ) {
$data{$_->{'name'}} = $_->{'value'};
}
return undef unless exists( $data{1} );
my ( $link, $logpage );
if ( exists( $data{'link'} ) ) {
$link = $data{'link'};
( $logpage = $link ) =~ s/#.*//;
} else {
return undef unless exists( $data{2} );
$logpage = 'WP:Templates for discussion/Log/' . $data{2};
$link = $logpage . '#' . ( $data{'section'} // $data{1} );
}
my $res = $api->query(
'titles' => join( '|', $data{1}, $logpage ),
'formatversion' => 2,
);
if ( $res->{'code'} ne 'success' ) {
$fail = 1;
return undef;
}
$workList{$data{1}} = {
'link' => $link,
'reason' => $data{'reason'} // '',
};
return undef;
} );
return 3600 unless %workList; # Nothing to process
# Don't resolve in case only a redirect should be substed for some strange reason.
my %r = $api->redirects_to( keys %workList );
if ( exists( $r{''} ) ) {
$api->warn( "Failed to get redirects: " . $r{''}{'error'} . "\n" );
return 60;
}
# Handle normalizations, e.g. underscore to space.
while ( my ( $r, $t ) = each( %r ) ) {
if ( defined( $workList{$r} ) && !defined( $workList{$t} ) ) {
$workList{$t} = $workList{$r};
delete $workList{$r};
}
}
$self->{'tfdMap'} = {};
while ( my ( $r, $t ) = each( %r ) ) {
$self->{'tfdMap'}{$r} = $workList{$t};
}
# Load and (if necessary) parse processing status page
my $tok = $api->edittoken( "$workPage/status", EditRedir => 1, NoExclusion => 1 );
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 $workPage/status: " . $tok->{'error'} . "\n" );
return 300;
}
my $revid = $tok->{'lastrevid'} // 0;
my $status = $api->store->{'status'} // [ 0, {} ];
if ( $status->[0] < $revid ) {
eval {
$status->[1] = JSON->new->decode( $tok->{'revisions'}[0]{'slots'}{'main'}{'*'} // '' );
};
$status->[1] = {} unless ref( $status->[1] ) eq 'HASH';
}
# Prepare processing list
my %process = ();
foreach my $title ( keys %workList ) {
$process{$title} = 0;
}
foreach my $title ( keys %{$status->[1]} ) {
$process{$title} = $status->[1]{$title} if exists( $process{$title} );
}
# Process pages!
$res = $self->process( $api, \%process, \%r, $endtime );
# Write processing status, if necessary
my $writeStatus = $self->{'writeStatus'} // 0;
foreach my $title ( keys %process ) {
$writeStatus = 1 if $process{$title} != ( $status->[1]{$title} // 0 );
}
if ( $writeStatus ) {
$api->log( "Updating processing status page" );
my $outtxt = JSON->new->pretty->canonical->encode( \%process );
my $res2 = $api->edit( $tok, $outtxt, 'Updating status page', 1, 1, contentmodel => 'json' );
if ( $res2->{'code'} eq 'success' ) {
$revid = $res2->{'edit'}{'newrevid'};
$writeStatus = 0;
} else {
$api->warn( "Write failed on $workPage/status: " . $res2->{'error'} . "\n" );
}
$api->store->{'status'} = [ $revid, \%process ];
$api->store->{'writeStatus'} = $writeStatus;
}
# Done for now.
return $res;
}
sub summary {
my ($self, $api, @remv) = @_;
my %per = ();
foreach my $t ( @remv ) {
my $tfd = $self->{'tfdMap'}{"Template:$t"} // $self->{'tfdMap'}{$t} // undef;
if ( !defined( $tfd ) ) {
die "TFD entry for {{$t}} is missing from tfdMap! " . Dumper( $self->{'tfdMap'} );
}
my $per = '[[' . $tfd->{'link'} . ( $tfd->{'reason'} ne '' ? '|' . $tfd->{'reason'} : '' ) . ']]';
push @{$per{$per}}, "{{$t}}";
}
my @s = ();
foreach my $per ( keys %per ) {
my @t = @{$per{$per}};
$t[-1] = 'and ' . $t[-1] if @t > 1;
push @s, join( ( @t > 2 ) ? ', ' : ' ', @t ) . ' per ' . $per;
}
return "Substing templates: " . join( '; ', @s ) . ". Report errors at [[$workPageTalk]].";
}
1;