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;