User:AnomieBOT/source/tasks/WikiProjectWorker.pm

This is an old revision of this page, as edited by AnomieBOT (talk | contribs) at 11:39, 9 April 2009 (Updating published sources: WikiProjectWorker: * Copy-paste fail. * Mark non-talk as processed.). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
package tasks::WikiProjectWorker;

=pod

=begin metadata

Task:     WikiProjectWorker
BRFA:     Wikipedia:Bots/Requests for approval/AnomieBOT 28
Status:   Approved 2009-04-08
Rate:     Max 6 edits/minute
Created:  2009-03-27
OnDemand: true

Perform various tasks at the request of the affected WikiProjects:
* Add or remove banners on a specific set of pages (e.g. pages in a category, pages transcluding a template).
* Adjust banner parameters, particularly assessments and task forces.
* Fix banner shells on pages edited for the above reasons.

=end metadata

=cut

use utf8;
use strict;

use Data::Dumper;
use Digest::SHA qw/sha256_base64/;
use AnomieBOT::Task;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;

# Request link, for edit summary.
my $req="[[User:AnomieBOT/req/WikiProjectJapan 1|request]]";

# Increment this number every time a new run is started, so we don't have to
# mess around with deleting previous runs' database entries.
my $seq=1;

# How to find the pages?
my @templates=('WP Shinto', 'Jmyth', 'Gaijin tarento', 'Project Owarai', 'WikiProject Japan');
my @iterators=(
    {
        list        => 'embeddedin',
        eititle     => [ map "Template:$_", @templates ],
        eilimit     => 'max',
    },
);

# Filter function: manipulate the found data as necessary, returning the talk
# page to tag (or undef to skip).
sub filter {
    return $_[0]->{'title'};
}

# Banner configurations.
my %banner_cfgs=(
    'WikiProject Japan' => {
        meta => 0,
        stubauto => undef,
        canonicalize => 'WikiProject Japan',
    },
);

sub new {
    my $class=shift;
    my $self=$class->SUPER::new();
    $self->{'config loaded'}=0;
    bless $self, $class;
    return $self;
}

=pod

=for info
Approved 2009-04-08<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT 28]]

=cut

sub approved {
    return 1;
}

sub run {
    my ($self, $api)=@_;
    my $res;

    $api->task('WikiProjectWorker', 0, 10, qw/d::Util d::WikiProjectTagging/);
    my $errto = 'Errors? [[User:'.$api->user.'/shutoff/WikiProjectWorker]]';

    # Load configs, if necessary
    if(!$self->{'config loaded'}){
        my %cfg=();
        while(my ($banner,$cfg)=each %banner_cfgs){
            $cfg=$api->WPBMetaConfig($cfg->{'meta'}, %$cfg) if exists($cfg->{'meta'});
            $cfg{$banner}=$cfg;
        }
        $api->WPBconfig(%cfg);
        $self->{'config loaded'}=1;
    }
    if(($api->store->{'configured'} // 0) < $seq){
        $api->store->{'conflicting assessments'}='';
        $api->store->{'NA assessments'}='';
    }

    # Update log page
    if($api->store->{'conflicting assessments'} ne '' || $api->store->{'NA assessments'} ne ''){
        my $tok=$api->edittoken('Wikipedia:WikiProject Japan/Assessment/Tag cleanup');
        if($tok->{'code'} ne 'success'){
            $api->warn("Cannot update log: ".$tok->{'error'}."\n");
            return 60;
        }
        my $txt=$tok->{'revisions'}[0]{'*'};
        my $a=$api->store->{'conflicting assessments'};
        my $b=$api->store->{'NA assessments'};
        unless($txt=~s/(<!-- Insert conflicting assessments above here -->)/$a$1/ &&
               $txt=~s/(<!-- Insert NA assessments above here -->)/$b$1/){
            $api->warn("Log is missing conflicting assessments marker");
            $api->whine("[[Wikipedia:WikiProject Japan/Assessment/Tag cleanup]] is broken", "Help! [[Wikipedia:WikiProject Japan/Assessment/Tag cleanup]] is missing the comments that tell me where to insert new log entries. I can't do anything to that page until someone fixes it.");
            return 60;
        }
        $api->warn("Updating log\n");
        my $r=$api->edit($tok, $txt, "Bot updating log", 0, 0);
        if($r->{'code'} ne 'success'){
            $api->warn("Write failed on log: ".$r->{'error'}."\n");
            return 60;
        }
        $api->store->{'conflicting assessments'}='';
        $api->store->{'NA assessments'}='';
    }

    # Preload cache
    $api->redirects_to(map "Template:$_", @templates);

    # Spend a max of 5 minutes on this task before restarting
    my $endtime=time()+300;

    foreach my $itercfg (@iterators) {
        my $iter=$api->iterator(%$itercfg);
        while(my $page=$iter->next()){
            if(!$page->{'_ok_'}){
                $api->warn("Could not retrieve page from iterator: ".$page->{'error'}."\n");
                return 60;
            }

            my $pageid=$page->{'pageid'};
            next if ($api->store->{$pageid} // 0) >= $seq;

            my $title=filter($page);
            if(!defined($title)){
                $api->warn("Skipping ".$page->{'title'}.", filter returned undef\n");
                $api->store->{$pageid}=$seq;
                next;
            }

            my $tok=$api->edittoken($title, EditRedir => 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 $title: ".$tok->{'error'}."\n");
                next;
            }
            if(($tok->{'ns'}&1)==0){
                $api->warn("Cannot edit $title: namespace ".$tok->{'ns'}." is non-talk\n");
                $api->store->{$pageid}=$seq;
                next;
            }
            if(exists($tok->{'redirect'})){
                $api->warn("$title is a redirect, skipping.\n");
                $api->store->{$pageid}=$seq;
                next;
            }

            $api->warn("Checking $title...\n");

            my $intxt=exists($tok->{'revisions'}[0]{'*'})?$tok->{'revisions'}[0]{'*'}:'';
            my ($outtxt,$nowiki)=$api->strip_nowiki($intxt);

            ### PROCESSING ###

            # Is there a WikiProject Japan template already on the page?
            my $need_WPJ=($api->WPBcheck($outtxt, "WikiProject Japan") == 0);

            # First, remove the to-be-removed templates and calculate task
            # force parameters.
            my $fail=0;
            my $class=undef;
            my $imp=undef;
            my %tf=();
            (undef,$outtxt)=$api->WPBcheck($outtxt, sub {
                my $banner=shift;
                shift; # $name
                my $oname=shift;
                my @params=@{shift()};

                my $wpj=($banner eq 'WikiProject Japan');

                $tf{'shinto'}=1 if $banner eq 'WP Shinto';
                $tf{'myth'}=1   if $banner eq 'Jmyth';
                $tf{'gaijin'}=1 if $banner eq 'Gaijin tarento';
                $tf{'owarai'}=1 if $banner eq 'Project Owarai';
                foreach (@params) {
                    if(/^\s*class\s*=\s*(\S.*?)\s*$/s){
                        $fail=1 if(defined($class) && lc($class) ne lc($1));
                        $class=$1;
                    }
                    if(/^\s*importance\s*=\s*(\S.*?)\s*$/s){
                        $fail=1 if(defined($imp) && lc($imp) ne lc($1));
                        $imp=$1;
                    }
                    if($wpj && /^\s*tf[23]?\s*=\s*(\S.*?)\s*$/s){
                        my $x=lc($1);
                        $tf{'baseball'}=1    if $x eq 'baseball';
                        $tf{'car'}=1         if $x eq 'car';
                        $tf{'CJKV'}=1        if $x eq 'cjkv';
                        $tf{'dist-muni'}=1   if $x eq 'districts and municipalities';
                        $tf{'gaijin'}=1      if $x eq 'gaijin tarento';
                        $tf{'milhist'}=1     if $x eq 'military history';
                        $tf{'music'}=1       if $x eq 'music';
                        $tf{'myth'}=1        if $x eq 'mythology';
                        $tf{'owarai'}=1      if $x eq 'owarai';
                        $tf{'phototf'}=1     if $x eq 'photo';
                        $tf{'prefectures'}=1 if $x eq 'prefectures';
                        $tf{'royalty'}=1     if $x eq 'royalty and nobility';
                        $tf{'shinto'}=1      if $x eq 'shinto';
                        $tf{'tokyo'}=1       if $x eq 'tokyo';
                        $tf{'update'}=1      if $x eq 'update';
                    }
                }
                return undef if $banner eq 'WikiProject Japan';
                return '' unless $need_WPJ;
                $need_WPJ=0;
                return '{{WikiProject Japan}}';
            }, @templates);
            if(ref($outtxt) eq 'HASH'){
                $api->warn("Processing $title failed: ".$outtxt->{'error'}."\n");
                next;
            }

            # Second, add (or replace) {{WikiProject Japan}}
            my @tf=map "$_=yes", keys %tf;
            my @assess=();
            if($fail){
                $api->store->{'conflicting assessments'}.="* [[:$title]]\n";
            } else {
                push @assess, "class=$class" if defined($class);
                push @assess, "importance=$imp" if defined($imp);
                $api->store->{'NA assessments'}.="* [[:$title]]\n" if ($class // '') eq 'na';
            }
            my $assess=undef;
            unless(defined($class)){
                $assess=$api->WPBassess($title);
                if(ref($assess) eq 'HASH'){
                    $api->warn("Processing $title failed: ".$assess->{'error'}."\n");
                    next;
                }
            }
            $outtxt=$api->WPBadd($outtxt, $assess, sub {
                shift; # $banner
                shift; # $name
                my $oname=shift;
                my $params=shift;
                shift; # $wikitext
                my $new=shift;
                return undef if $new;

                $params = [ grep $_!~/^\s*tf[23]?\s*=/, @$params ];

                foreach my $tf (keys %tf) {
                    push @$params, "$tf=yes" unless(grep(s/^(\s*\Q$tf\E\s*=(?:\s*(?=\S))?).*?(\s*)$/${1}yes$2/s, @$params));
                }

                if(@assess){
                    if(defined($class)){
                        push @$params, "class=$class" unless(grep(s/^(\s*class\s*=(?:\s*(?=\S))?).*?(\s*)$/$1$class$2/s, @$params));
                    }
                    if(defined($imp)){
                        push @$params, "importance=$imp" unless(grep(s/^(\s*importance\s*=(?:\s*(?=\S))?).*?(\s*)$/$1$imp$2/s, @$params));
                    }
                }

                return "{{$oname}}" unless @$params;
                return "{{$oname|".join("|", @$params)."}}";
            }, 'WikiProject Japan', @tf, @assess);
            if(ref($outtxt) eq 'HASH'){
                $api->warn("Processing $title failed: ".$outtxt->{'error'}."\n");
                next;
            }

            $outtxt=$api->replace_nowiki($outtxt, $nowiki);

            # Need to edit?
            if($outtxt ne $intxt){
                $outtxt=$api->WPBfixshell($outtxt);
                if(ref($outtxt) eq 'HASH'){
                    $api->warn("Processing $title failed: ".$outtxt->{'error'}."\n");
                    next;
                }

                my $summary="Adjusting WikiProject tagging for WikiProject Japan per $req $errto";
                $api->warn("$summary in $title\n");
                my $r=$api->edit($tok, $outtxt, $summary, 1, 1);
                if($r->{'code'} ne 'success'){
                    $api->warn("Write failed on $title: ".$r->{'error'}."\n");
                    next;
                }
            } else {
                $api->warn("Nothing to do in $title\n");
            }

            # Remember that we processed this page already
            $api->store->{$pageid}=$seq;

            # If we've been at it long enough, let another task have a go.
            return 0 if time()>=$endtime;
        }
    }

    # No more pages to check, try again in 10 minutes or so in case of errors.
    return 600;
}