User:AnomieBOT/source/tasks/SandboxCleaner.pm

From Wikipedia, the free encyclopedia
package tasks::SandboxCleaner;

=pod

=begin metadata

Bot:     AnomieBOT
Task:    SandboxCleaner
BRFA:    None
Status:  On hold
Created: 2008-08-30

Clears a sandbox page to predefined content once every 12 hours (at or shortly
after 00:00 and 12:00 UTC), and checks once per hour to ensure the header is
present on the page. As with [[User:SoxBot IV]], this task takes instruction
from [[User:X!/Sandbots.css]].

=end metadata

=for notice
Credit to [[User:SoxBot IV/Source|SoxBot IV]] for a bit of inspiration on the code.

=cut

use utf8;
use strict;

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

use Data::Dumper;

my %texts=(
    'sandbox' => "{{Please leave this line alone (sandbox heading)}}\n<!-- Hello! Feel free to try your formatting and editing skills below this line. As this page is for editing experiments, this page will automatically be cleaned every 12 hours. -->",
    'sandboxtalk' => "{{Please leave this line alone (sandbox talk heading)}}\n<!-- Hello!  Feel free to try your formatting and editing skills below this line.  As this page is for editing experiments, this page will automatically be cleaned every 12 hours. -->",
    'intro' => "{{Please leave this line alone}}\n<!-- Feel free to change the text below this line. No profanity, please. -->",
    'xX' => "<noinclude>\nThis sandbox is itself a template.  This sandbox is for experimenting with templates.\n{{Please leave this line alone (template sandbox heading)}}\n</noinclude>\n\nIf you defined parameters such as <tt><nowiki>{{Template sandbox|First|Second|name=\"Named\"}}</nowiki></tt>:\n;First:{{{1}}}\n;Second:{{{2}}}\n;Name:{{{name}}}\n\n----\n<!-- Hello!  Feel free to try your formatting and editing skills below this line.  As this page is for editing experiments, this page will automatically be cleaned every 12 hours. -->",
    'ts' => "<noinclude>\nThis sandbox is itself a template.  This sandbox is for experimenting with templates.\n{{Please leave this line alone (template sandbox heading)}}\n</noinclude>\n\nIf you defined parameters such as <tt><nowiki>{{Template sandbox|First|Second|name=\"Named\"}}</nowiki></tt>:\n;First:{{{1}}}\n;Second:{{{2}}}\n;Name:{{{name}}}\n\n----",
    'tutorial' => "{{Please leave this line alone (tutorial sandbox heading)}}\n<!-- Hello!  Feel free to try your formatting and editing skills below this line.  As this page is for editing experiments, this page will automatically be cleaned every 12 hours. -->",
    'tutorialtalk' => "{{Please leave this line alone (tutorial sandbox talk heading)}}\n<!-- Hello!  Feel free to try your formatting and editing skills below this line.  As this page is for editing experiments, this page will automatically be cleaned every 12 hours. -->",
);

my %pages=(
    "Wikipedia:Sandbox" => $texts{'sandbox'},
    "Wikipedia talk:Sandbox" => $texts{'sandboxtalk'},
    "Wikipedia:Introduction" => $texts{'intro'},
    "Template:X1" => $texts{'xX'},
    "Template talk:X1" => $texts{'tutorialtalk'},
    "Template:X2" => $texts{'xX'},
    "Template talk:X2" => $texts{'tutorialtalk'},
    "Template:X3" => $texts{'xX'},
    "Template talk:X3" => $texts{'tutorialtalk'},
    "Template:X4" => $texts{'xX'},
    "Template talk:X4" => $texts{'tutorialtalk'},
    "Template:X5" => $texts{'xX'},
    "Template talk:X5" => $texts{'tutorialtalk'},
    "Template:X6" => $texts{'xX'},
    "Template talk:X6" => $texts{'tutorialtalk'},
    "Template:X7" => $texts{'xX'},
    "Template talk:X7" => $texts{'tutorialtalk'},
    "Template:X8" => $texts{'xX'},
    "Template talk:X8" => $texts{'tutorialtalk'},
    "Template:X9" => $texts{'xX'},
    "Template talk:X9" => $texts{'tutorialtalk'},
    "Template:Template sandbox" => $texts{'ts'},
    "Wikipedia:Tutorial (Editing)/sandbox" => $texts{'tutorial'},
    "Wikipedia talk:Tutorial (Editing)/sandbox" => $texts{'tutorialtalk'},
    "Wikipedia:Tutorial (Formatting)/sandbox" => $texts{'tutorial'},
    "Wikipedia talk:Tutorial (Formatting)/sandbox" => $texts{'tutorialtalk'},
    "Wikipedia:Tutorial (Wikipedia links)/sandbox" => $texts{'tutorial'},
    "Wikipedia talk:Tutorial (Wikipedia links)/sandbox" => $texts{'tutorialtalk'},
    "Wikipedia:Tutorial (External links)/sandbox" => $texts{'tutorial'},
    "Wikipedia talk:Tutorial (External links)/sandbox" => $texts{'tutorialtalk'},
    "Wikipedia:Tutorial (Keep in mind)/sandbox" => $texts{'tutorial'},
    "Wikipedia talk:Tutorial (Keep in mind)/sandbox" => $texts{'tutorialtalk'},
);

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

=pod

=for warning
Not approved.

=cut

sub approved {
    return 0;
}

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

    $api->task('SandboxCleaner');
    $api->read_throttle(0);
    $api->edit_throttle(10);

    my @now=gmtime;

    if($self->{'needlast'}){
        # Load last edit for each page on startup
        my @p=keys %pages;
        foreach (@p){
            next if exists($self->{'last'}{$_});
            $res=$api->query(
                titles  => $_,
                prop    => 'revisions',
                rvuser  => $api->user,
                rvprop  => 'timestamp',
                rvlimit => 1  # Only need the last rev
            );
            if($res->{'code'} ne 'success'){
                $self->warn("Failed to retrieve last edit date for $_: ".$res->{'error'}."\n");
                return 60;
            }
            $res=[values(%{$res->{'query'}{'pages'}})];
            $self->{'last'}{$_}=-1;
            if(exists($res->[0]{'revisions'}[0]{'timestamp'})){
                my @t=gmtime($self->ISO2timestamp($res->[0]{'revisions'}[0]{'timestamp'}));
                $self->{'last'}{$_}=$t[2] if($t[3]==$now[3] && $t[4]==$now[4] && $t[5]==$now[5]);
            }
        }
        $self->{'needlast'}=0;
    }

    # If it's too late in the hour, just wait until next hour.
    my $t=3600-$now[1]*60-$now[0];
    return $t if $t<1800;

    # Get the list of enabled pages
    $res=$api->rawpage('User:X!/Sandbots.css');
    if($res->{'code'} ne 'success'){
        $self->warn("Failed to retrieve sandbots list: ".$res->{'error'}."\n");
        return 60;
    }

    my $fail=0;
    foreach (split(/\|/, $res->{'content'})){
        s/^\s+|\s+$//g;
        next unless /^(.+?)\s*=\s*(.+)$/;
        my ($page,$bot)=($1,$2);

        # On our list?
        if(!exists($pages{$page})){
            $self->warn("Unknown sandbox $page\n");
            $self->{'last'}{$page}=$now[2];
            next;
        }

        # Are we authorized?
        if($bot ne $api->user){
            #$self->warn("Not authorized for $page\n");
            $self->{'last'}{$page}=$now[2];
            next;
        }

        # Time to check again?
        next if $self->{'last'}{$page}==$now[2];

        # Get edit token
        my $tok=$api->edittoken($page);
        if($tok->{'code'} eq 'shutoff'){
            $self->warn("Task disabled: ".$tok->{'content'}."\n");
            return 300;
        }
        if($tok->{'code'} ne 'success'){
            $self->warn("Failed to retrieve edit token for $page: ".$tok->{'error'});
            return 60;
        }
        if(exists($tok->{'missing'})){
            $self->warn("Page $page does not exist");
            $self->{'last'}{$page}=$now[2];
            next;
        }
        my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'};

        # Generate new page content
        my $header=$pages{$page};
        my $outtxt;
        my $summary;
        if($now[2]==0 || $now[2]==12){
            $outtxt=$header;
            $summary='Clearing the sandbox ([[WP:BOT|BOT]] EDIT)';
        } elsif(index($intxt,$header)<0){
            $outtxt="$header\n$intxt";
            $summary='Restoring the sandbox header ([[WP:BOT|BOT]] EDIT)';
        } else {
            $outtxt=$intxt; # no edit
        }

        # Perform edit, if needed
        if($intxt eq $outtxt){
            $self->warn("No update needed for $page\n");
        } else {
            my $res=$api->edit($tok, $outtxt, $summary, 0, 1);
            if($res->{'code'} ne 'success'){
                $self->warn("Write for $page failed: ".$res->{'error'}."\n");
                $fail=1;
                next;
            }
            $self->warn("Updated $page\n");
        }

        # Record update time
        $self->{'last'}{'page'}=$now[2];
    }

    # If one of the edits failed (probably an edit conflict), try it again
    # ASAP.
    return 0 if $fail;

    # We processed all pages, calculate the number of seconds until the next
    # time we're needed.
    my @next=gmtime;
    return 0 if $next[2]!=$now[2];
    return 3600-$next[1]*60-$next[0];
}

1;