Open main menu

User:AnomieBOT/source/tasks/CategoryLister.pm

package tasks::CategoryLister;

=pod

=begin metadata

Bot:     AnomieBOT
Task:    CategoryLister
BRFA:    N/A
Status:  Begun 2017-01-22
Created: 2017-01-22

Copy category membership to pages under [[User:AnomieBOT/C]], based on
configuration in [[User:AnomieBOT/CategoryLister/Categories]].

=end metadata

=cut

use utf8;
use strict;

use AnomieBOT::Task;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;

use Data::Dumper;

sub new {
    my $class=shift;
    my $self=$class->SUPER::new;
    bless $self, $class;
    return $self;
}

=pod

=for info
Per [[WP:BOT#Approval]], any bot or automated editing process that only
affects only the operators' user and talk pages (or subpages thereof),
and which are not otherwise disruptive, may be run without prior
approval.

=cut

sub approved {
    return 999;
}

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

    my $prefix = "User:AnomieBOT/C/";

    $api->task('CategoryLister', 0, 10, qw(d::Templates d::Timestamp));

    my $endtime=time()+300;

    my $res = $api->query(
        titles => 'User:AnomieBOT/CategoryLister/Categories',
        prop => 'revisions',
        rvprop => 'content',
        rvslots => 'main',
        rvlimit => 1,
        formatversion => 2,
    );
    if($res->{'code'} ne 'success'){
        $api->warn("Failed to retrieve [[User:AnomieBOT/CategoryLister/Categories]]: ".$res->{'error'}."\n");
        return 60;
    }

    my @conf = ();
    $api->process_templates($res->{'query'}{'pages'}[0]{'revisions'}[0]{'slots'}{'main'}{'content'}, sub {
        my $name=shift;
        my $params=shift;
        return undef unless $name eq 'User:AnomieBOT/CategoryLister/Cat';

        my ($title, $ns, $reverse) = ('', '*', 0);
        foreach my $p ($api->process_paramlist(@$params)){
            $title = $p->{'value'} if $p->{'name'} eq '1';
            $ns = $p->{'value'} if $p->{'name'} eq 'ns';
            if ( $p->{'name'} eq 'reverse' ) {
                $p->{'value'} =~ s/^\s+|\s+$//g;
                $reverse = !$p->{'value'}=~/^(¬|no|n|false|0|)/i;
            }
        }
        push @conf, [ $title, $ns, $reverse ] if $title ne '' && $ns=~/^(\*|\d+)$/;
        return undef;
    } );

    my $err = 0;
    LOOP: foreach my $conf (@conf) {
        my ($title, $ns, $reverse) = @$conf;
        $api->log("Checking $title (ns=$ns, reverse=$reverse)");

        my $iter = $api->iterator(
            list => 'categorymembers',
            cmtitle => "Category:$title",
            cmprop => 'title|timestamp',
            cmnamespace => $ns,
            cmlimit => 'max',
            cmsort => 'timestamp',
            cmdir => $reverse ? 'desc' : 'asc',
            formatversion => 2,
        );
        my $txt = '';
        while ( my $p = $iter->next ) {
            return 0 if $api->halting;

            if(!$p->{'_ok_'}){
                $api->warn("Failed to retrieve pages in [[Category:$title]]: ".$p->{'error'}."\n");
                $err = 1;
                next LOOP;
            }

            $p->{'title'}=~s/^[^:]*:// if $p->{'ns'} ne 0;
            $txt .= "{{CF/$title|$p->{title}|$p->{ns}|$p->{timestamp}|extra={{{extra|}}}}}\n";
        }

        my $tok=$api->edittoken($prefix . $title);
        if($tok->{'code'} eq 'shutoff'){
            $api->warn("Task disabled: ".$tok->{'content'}."\n");
            return 300;
        }
        if($tok->{'code'} ne 'success'){
            $api->warn("Failed to retrieve edit token for $prefix$title: ".$tok->{'error'});
            return 60;
        }
        my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'} // '';

        $txt=~s/\s*$/\n/;
        $intxt=~s/\s*$/\n/;

        if ( $intxt ne $txt ) {
            my $res=$api->edit($tok, $txt, "Update listing from [[:Category:$title]]", 0, 1);
            if($res->{'code'} ne 'success'){
                $api->warn("Write for $prefix$title failed: ".$res->{'error'});
                next;
            } else {
                $api->log("Updated $prefix$title");
            }
        }
    }

    return $err ? 300 : (3600 - (time() % 3600));
}

1;