User:AnomieBOT/source/tasks/PeerReviewArchiver.pm
BRFA approved 2017-02-18 Wikipedia:Bots/Requests for approval/AnomieBOT 76 |
package tasks::PeerReviewArchiver;
=pod
=begin metadata
Bot: AnomieBOT
Task: PeerReviewArchiver
BRFA: Wikipedia:Bots/Requests for approval/AnomieBOT 76
Status: Approved 2017-02-18
Created: 2017-01-22
Archive old requests for Peer Review:
* Collect titles and archive indexes from [[:Category:Current peer reviews]],
excluding those listed at [[Template:FAC peer review sidebar]] or
[[:Template:Peer review/Unanswered peer reviews sidebar]] or that have only one contributor.
* For each page where both the talk page and the PR archive page exist, and the
PR hasn't been edited for at least 1 month, replace
<code><nowiki>{{Peer review|archive=$index}}</nowiki></code> with
<code><nowiki>{{subst:Close peer review|archive=$index}}</nowiki></code> on the talk
page and <code><nowiki>{{Peer review page}}</nowiki></code> with
<code><nowiki>{{Closed peer review page}}</nowiki></code> on the PR.
=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;
sub new {
my $class=shift;
my $self=$class->SUPER::new;
bless $self, $class;
return $self;
}
=pod
=for info
BRFA approved 2017-02-18<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT 76]]
=cut
sub approved {
return 2;
}
sub run {
my ($self, $api)=@_;
my $screwup=' Errors? [[User:'.$api->user.'/shutoff/PeerReviewArchiver]]';
$api->task('PeerReviewArchiver', 0, 10, qw(d::Templates d::Timestamp d::Redirects));
my $endtime=time()+300;
my %r = $api->redirects_to_resolved( 'Template:Peer review', 'Template:Peer review page' );
if(exists($r{''})){
$api->warn("Failed to get redirects: ".$r{''}{'error'}."\n");
return 60;
}
my $templatePeerReview = $r{'Template:Peer review'};
my $templatePeerReviewPage = $r{'Template:Peer review page'};
my %pages = ();
my @now = gmtime;
$now[4]--;
my $expiry = strftime( '%FT%TZ', @now );
# This category holds peer review pages. Filter by expiry.
my $iter = $api->iterator(
generator => 'categorymembers',
gcmtitle => 'Category:Current peer reviews',
gcmnamespace => 4,
gcmtype => 'page',
gcmlimit => 'max',
prop => 'revisions',
rvprop => 'timestamp',
formatversion => 2,
);
while ( my $p = $iter->next ) {
return 0 if $api->halting;
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve pages in [[Category:Current peer reviews]]: ".$p->{'error'}."\n");
return 300;
}
if ( $p->{'title'} =~ /^Wikipedia:Peer review\/(.+)\/archive(\d+)$/ && $p->{'revisions'}[0]{'timestamp'} lt $expiry ) {
$pages{$1 . '#' . $2} = {
title => $1,
index => $2,
};
}
}
# Requested to not process PRs listed at [[Template:FAC peer review sidebar]]
# at [[User talk:Anomie#AnomieBot closing peer reviews - request for update]].
$iter = $api->iterator(
generator => 'links',
titles => 'Template:FAC peer review sidebar',
gplnamespace => 4,
gpllimit => 'max',
formatversion => 2,
);
while ( my $p = $iter->next ) {
return 0 if $api->halting;
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve pages linked from [[Template:FAC peer review sidebar]]: ".$p->{'error'}."\n");
return 300;
}
if ( $p->{'title'} =~ /^Wikipedia:Peer review\/(.+)\/archive(\d+)$/ ) {
if ( exists( $pages{$1 . '#' . $2} ) ) {
$p = $pages{$1 . '#' . $2};
$api->warn( "Skipping $p->{title} archive $p->{index}, listed at Template:FAC peer review sidebar" );
delete $pages{$1 . '#' . $2};
}
}
}
# Requested to not process PRs listed at [[Template:Peer review/Unanswered peer reviews sidebar]]
# at [[User talk:AnomieBOT#Further requests for change at Wikipedia peer review]].
$iter = $api->iterator(
generator => 'links',
titles => 'Template:Peer review/Unanswered peer reviews sidebar',
gplnamespace => 4,
gpllimit => 'max',
formatversion => 2,
);
while ( my $p = $iter->next ) {
return 0 if $api->halting;
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve pages linked from [[Template:Peer review/Unanswered peer reviews sidebar]]: ".$p->{'error'}."\n");
return 300;
}
if ( $p->{'title'} =~ /^Wikipedia:Peer review\/(.+)\/archive(\d+)$/ ) {
if ( exists( $pages{$1 . '#' . $2} ) ) {
$p = $pages{$1 . '#' . $2};
$api->warn( "Skipping $p->{title} archive $p->{index}, listed at Template:Peer review/Unanswered peer reviews sidebar" );
delete $pages{$1 . '#' . $2};
}
}
}
my $err = 0;
foreach my $p (values %pages) {
return 0 if $api->halting;
$api->log("Checking $p->{title} archive $p->{index}");
my $talk = 'Talk:' . $p->{'title'};
my $pr = 'Wikipedia:Peer review/' . $p->{'title'} . '/archive' . $p->{'index'};
my $tok1 = $api->edittoken( $talk, EditRedir => 1 );
if($tok1->{'code'} eq 'shutoff'){
$api->warn("Task disabled: " . $tok1->{'content'} . "\n");
return 300;
}
if($tok1->{'code'} ne 'success'){
$api->warn("Failed to retrieve edit token for $talk: " . $tok1->{'error'});
$err = 1;
next;
}
my $tok2 = $api->edittoken( $pr, EditRedir => 1 );
if($tok2->{'code'} eq 'shutoff'){
$api->warn("Task disabled: " . $tok2->{'content'} . "\n");
return 300;
}
if($tok2->{'code'} ne 'success'){
$api->warn("Failed to retrieve edit token for $pr: " . $tok2->{'error'});
$err = 1;
next;
}
if ( exists( $tok1->{'missing'} ) ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, $talk does not exist" );
next;
}
if ( exists( $tok2->{'missing'} ) ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, $pr does not exist" );
next;
}
if ( exists( $tok1->{'redirect'} ) ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, $talk is a redirect" );
next;
}
if ( exists( $tok2->{'redirect'} ) ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, $pr is a redirect" );
next;
}
if ( $tok2->{'revisions'}[0]{'timestamp'} ge $expiry ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, not expired yet (" . $tok2->{'revisions'}[0]{'timestamp'} . " >= $expiry)" );
next;
}
my $res = $api->query(
[ 'contributors' ],
titles => $pr,
prop => 'contributors',
pclimit => 2,
formatversion => 2,
);
if($res->{'code'} ne 'success'){
$api->warn("Failed to retrieve contributor count for $pr: " . $res->{'error'});
$err = 1;
next;
}
my $ct = ( $res->{'query'}{'pages'}[0]{'anoncontributors'} // 0 ) + @{$res->{'query'}{'pages'}[0]{'contributors'} // []};
if ( $ct <= 1 ) {
$api->warn( "Skipping $p->{title} archive $p->{index}, only one contributor to $pr" );
next;
}
# Save the talk page first, then the PR, because editing the PR resets
# the month timer so both pages would disappear from both cats.
my $intxt1 = $tok1->{'revisions'}[0]{'slots'}{'main'}{'*'};
my $outtxt1 = $api->process_templates( $intxt1, sub {
my $name=shift;
my $params=shift;
return undef unless ($r{"Template:$name"} // '') eq $templatePeerReview;
return '{{subst:Close peer review' . (@$params ? '|' . join( '|', @$params ) : '' ) . '}}';
} );
my $intxt2 = $tok2->{'revisions'}[0]{'slots'}{'main'}{'*'};
my $outtxt2 = $api->process_templates( $intxt2, sub {
my $name=shift;
return undef unless ($r{"Template:$name"} // '') eq $templatePeerReviewPage;
return '{{Closed peer review page}}';
} );
if ( $intxt1 ne $outtxt1 ) {
my $res = $api->edit($tok1, $outtxt1, "Archive expired peer review. $screwup", 0, 0);
if($res->{'code'} ne 'success'){
$api->warn("Write for $talk failed: ".$res->{'error'});
$err = 1;
next;
} else {
$api->log("Archived peer review on $talk");
}
}
if ( $intxt2 ne $outtxt2 ) {
my $res = $api->edit($tok2, $outtxt2, "Archive expired peer review. $screwup", 0, 0);
if($res->{'code'} ne 'success'){
$api->warn("Write for $pr failed: ".$res->{'error'});
$err = 1;
next;
} else {
$api->log("Archived peer review on $pr");
}
}
}
return $err ? 300 : 43200;
}
1;