![]() | 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. |
package tasks::PERTableUpdater;
=pod
=begin metadata
Bot: PERTableUpdater
Task: PERTableUpdater
BRFA: N/A
Status: Begun 2011-12-04
Created: 2011-12-01
Update [[User:AnomieBOT/PERTable]] and [[User:AnomieBOT/SPERTable]]
=end metadata
=cut
use utf8;
use strict;
use AnomieBOT::Task qw/:time bunchlist/;
use URI::Escape;
use Data::Dumper;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;
my %colors=(
'red' => ' style="background-color:#ffbfdc"',
'yellow' => ' style="background-color:#fff9bf"',
'green' => ' style="background-color:#e4ffcc"',
'clear' => '',
);
my %protact=(
'modify' => 'Modified',
'protect' => 'Protected',
'unprotect' => 'Unprotected',
);
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 $res;
$api->task('PERTableUpdater', 0, 10, qw/d::Sections d::Timestamp d::Talk/);
my $screwup=' Errors? [[User:'.$api->user.'/shutoff/PERTableUpdater]]';
# First, load MediaWiki:Titleblacklist to catch pages protected by
# that mechanism
my %tb=();
my $tb=$api->rawpage('MediaWiki:Titleblacklist');
if($tb->{'code'} ne 'success'){
$api->warn("Failed to load MediaWiki:Titleblacklist: ".$tb->{'error'}."\n");
return 60;
}
for my $line (split /\r?\n/, $tb->{'content'}){
my $re=$line;
my %opts=();
$re=~s/^\s*([^#]*?)\s*(?:#.*)?$/$1/;
if($re=~s/\s*<([^<>]*)>$//){
my $opts=$1;
$opts=~s/^\s+|\s+$//g;
for my $opt (split /\s*\|\s*/, $opts){
if($opt=~/^([^=]*?)\s*=\s*(.+)$/){
$opts{lc($1)}=$2;
} else {
$opts{lc($opt)}=1;
}
}
}
$re=~s/_/ /g;
$re="(?-i:$re)" if($opts{'casesensitive'}//0);
# Let's just hope no one ever uses {{ns:}} or {{int:}} here...
$tb{$re}={ line=>$line, opts=>\%opts } if($opts{'noedit'}//0);
}
my $starttime=time;
for my $sper (0,1){
my $iter=$api->iterator(
generator => 'categorymembers',
gcmtitle => $sper?'Category:Wikipedia semi-protected edit requests':'Category:Wikipedia protected edit requests',
gcmlimit => 'max',
prop => 'info',
);
my %oldpages=%{$api->store->{'pages'}//{}};
my %pages=();
while(my $p=$iter->next){
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve members for ".($sper?'CAT:SEP':'CAT:EP').": ".$p->{'error'}."\n");
return 60;
}
next unless $p->{'ns'}&1;
my $t=$p->{'title'};
if($p->{'ns'}==1){
$t=~s/^Talk://;
} else {
$t=~s/^([^:]+) talk:/$1:/;
}
$pages{$t}=($oldpages{$t} // {
ns => $p->{'ns'}&~1,
title => $t,
talk => $p->{'title'},
touched => ISO2timestamp($p->{'touched'}),
});
delete $pages{$t}{'color'};
delete $pages{$t}{'prottype'};
delete $pages{$t}{'reason'};
delete $pages{$t}{'logtitle'};
}
$api->store->{'pages'}=\%pages;
if(%pages){
$iter=$api->iterator(
titles => bunchlist(500, keys %pages),
prop => 'info',
inprop => 'protection',
);
while(my $p=$iter->next){
if(!$p->{'_ok_'}){
$api->warn("Failed to retrieve members for ".($sper?'CAT:SEP':'CAT:EP').": ".$p->{'error'}."\n");
return 60;
}
my $t=$p->{'title'};
my $pd=$pages{$t};
$pd->{'color'}='red';
$pd->{'prottype'}='Not protected';
$pd->{'reason'}='';
if($p->{'ns'}==8){
$pd->{'color'}='yellow';
$pd->{'prottype'}='MediaWiki page';
} elsif($p->{'ns'}==2 && $t=~m!/.*\.js$!){
$pd->{'color'}='yellow';
$pd->{'prottype'}='User JS page';
} elsif($p->{'ns'}==2 && $t=~m!/.*\.css$!){
$pd->{'color'}='yellow';
$pd->{'prottype'}='User CSS page';
}
while(my ($re,$data)=each %tb){
next unless $t=~/^(?:$re)$/si;
$pd->{'color'}='yellow' unless(exists($data->{'opts'}{'autoconfirmed'}) && !$sper);
$pd->{'prottype'}='[[MediaWiki:Titleblacklist|Title blacklist]]';
my $line=$data->{'line'};
$pd->{'reason'}=qq(Matching line: <syntaxhighlight lang="text" enclose="none">$line
);
}
my $protscore=-1; my $expiry=undef; my $pg=$t; for my $pp (@{$p->{'protection'}//[]}){ next unless $pp->{'type'} eq 'edit'; my $sc=0; $sc|=2 if $pp->{'level'} eq 'sysop'; $sc|=1 if exists($pp->{'source'}); next if $sc<$protscore; $pd->{'color'}=($p->{'ns'}==10?'green':'clear') if($pp->{'level'} eq ($sper?'autoconfirmed':'sysop')); if(exists($pp->{'source'})){ $pg=$pp->{'source'}; $pd->{'prottype'}="Cascade-protected from $pg"; } else { $pg=$t; $pd->{'prottype'}='Fully protected' if $pp->{'level'} eq 'sysop'; $pd->{'prottype'}='Semiprotected' if $pp->{'level'} eq 'autoconfirmed'; } $pd->{'prottype'}.=strftime(', expires %F at %T UTC', gmtime ISO2timestamp($pp->{'expiry'})) if $pp->{'expiry'} ne 'infinity'; $protscore=$sc; $pd->{'reason'}=; }
if($pd->{'reason'} eq ){ my $iter=$api->iterator( list => 'logevents', letype => 'protect', letitle => $pg, ); my $from=; while(my $le=$iter->next){ if(!$le->{'_ok_'}){ $api->warn("Failed to retrieve protection log for $pg: ".$le->{'error'}."\n"); return 60; } if($le->{'action'} eq 'move_prot'){ $from="From [[:".$le->{'0'}."]]: "; next; } next unless exists($protact{$le->{'action'}}); $le->{'timestamp'}=~s/T.*//; my $comment=$le->{'comment'}; $comment=~s/\s*\^*\](?: \(expires [^)]*\))?$//; $pd->{'reason'}=$from.$protact{$le->{'action'}}.' by [[User:'.$le->{'user'}.'|'.$le->{'user'}.']] on '.$le->{'timestamp'}.': "'.esccomment($comment).'"'; last; } } $pd->{'logtitle'}=$pg; } }
# The formatting here is a little strange, for backwards compat
my $txt=qq(
\n);
$txt.=qq({| class="wikitable" style="padding:0em"\n); $txt.=qq(|-\n); my $ct=scalar keys %pages; my $s=($ct==1?:'s'); my $type=($sper?'semi-protected':'protected'); my $pg='User:AnomieBOT/'.($sper?'SPERTable':'PERTable');$txt.=qq(! $ct $type edit request$s \n);
$txt.=qq(|-\n); $txt.=qq(|\n); $txt.=qq({| class="wikitable sortable" width=100% style="margin:0em"\n); $txt.=qq(! Page\n); $txt.=qq(! Tagged since\n); $txt.=qq(! Protection level\n); $txt.=qq(! class = "unsortable" | Last protection log entry\n); my $tgt=$sper?'editsemiprotected':'editprotected'; for my $p (sort { $a->{'touched'} <=> $b->{'touched'} } values %pages){ my $c=$colors{$p->{'color'}}; my $t=$p->{'title'}; my $et=encodetitle($p->{'logtitle'}); $t=":$t" if($p->{'ns'}==6 || $p->{'ns'}==14); my $tt=$p->{'talk'}; my $pt=$p->{'prottype'}; my $r=$p->{'reason'}; $txt.=qq(|-$c\n); $txt.=qq(| $t (request)\n); $txt.=strftime("| %F %H:%M\n", gmtime $p->{'touched'}); $txt.=qq(| $pt (log)\n); $txt.=qq(| $r\n); } $txt.=qq(|}\n); $txt.=qq(|-\n); $txt.=qq(|style="text-align:right;font-size:smaller"| Updated as needed. Last updated: 14:58, 4 December 2011 (UTC)\n);$txt.=qq(|}
);
my $tok=$api->edittoken($pg, 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 $pg: ".$tok->{'error'}."\n"); next; } my $intxt=$tok->{'revisions'}[0]{'*'}//; $intxt=~s/^\s+|\s+$//; $intxt=~s#.*#14:58, 4 December 2011 (UTC)#g; if($intxt ne $txt){ my $r=$api->edit($tok, $txt, "Update table ($ct request$s)", 0, 0); if($r->{'code'} ne 'success'){ $api->warn("Write failed on $pg: ".$r->{'error'}."\n"); } } }
my $t=$starttime-time+300; $t=0 if $t<0; return $t;
}
sub esccomment {
my $c=shift; $c=~s/{/{/g; return $c;
}
sub encodetitle {
my $t=shift; $t=~s/ /_/g; $t=uri_escape($t, '^A-Za-z0-9_\-.:/~'); return $t;
}
1;
</syntaxhighlight>