User:AnomieBOT/source/tasks/PUICloser.pm

\" with the actual name of the file. You'll also want to put your reason for deletion just after \" \". Feel free to just replace this entire section with the corrected template. If you are still having trouble, ask for help at WT:PUF or at my talk page. ~}}\n".$s->{'body'}."\n\n"; $ct--; push @closed, "File:$img"; } elsif($img eq ''){ $s->{'body'}=~s/^\s*|\s*$//g; $s->{'body'}="Erroneous Nomination. No image name is specified. Feel free to just replace this entire section with the corrected template. If you are still having trouble, ask for help at WT:FFD or at my talk page. ~\n".$s->{'body'}."\n\n"; $ct--; push @closed, "File:$img"; } else { $img{"File:$img"}=$s; }       }        if($ct==0 && @closed==0 && !$fixedhead){ push @closedpages, $tt; next; }

# Check if the unclosed files still exist. If not, close the # discussion. my @commentednonfree=; my @titles=keys %img; while(@titles){ my @img=splice(@titles, 0, 500); $res=$api->query(               titles       => join('|', @img),                prop         => 'info|imageinfo|categories|templates',                iiprop       => 'canonicaltitle',                clcategories => 'Category:All non-free media|Category:All free media',                onlylist('tltemplates',500,@ok_nonfree_templates),                tllimit      => 'max',            ); if($res->{'code'} ne 'success'){ $api->warn("Failed to retrieve file info for $title: ".$res->{'error'}."\n"); return 60; }           my %map=; %map=map { $_->{'to'}, $_->{'from'} } @{$res->{'query'}{'normalized'}} if exists($res->{'query'}{'normalized'}); PAGE: foreach (values %{$res->{'query'}{'pages'}}){ my $img=$api->apply_redirect_map( $_->{'title'}, \%map ); if(!exists($img{$img})){ my $d=Dumper($_); $d=~s/\s+/ /g; $api->warn("How odd, this was apparently returned even though it wasn't requested: $d\n"); next PAGE; }

# File exists here? if($_->{'imagerepository'} eq 'local' && $_->{'imageinfo'}[0]{'canonicaltitle'} eq $_->{'title'}){ # Comment if it's non-free next PAGE unless exists($_->{'categories'}); next PAGE unless grep($_->{'title'} eq 'Category:All non-free media', @{$_->{'categories'}}); next PAGE if grep($_->{'title'} eq 'Category:All free media', @{$_->{'categories'}}); next PAGE if $img{$img}{'body'}=~//; # Certain templates make the non-freeness OK                   foreach my $t (@{$self->{'ok-nonfree'}}) { next PAGE if(exists($_->{'templates'}) && grep($_->{'title'} eq $t, @{$_->{'templates'}})); }                   $img{$img}{'body'}=~s/\s*$//; $img{$img}{'body'}.="\n* Note: This image is currently tagged as non-free. If there is a dispute with the rationale, please tag the image with dfu or list it at WP:FFD. ~\n"; push @commentednonfree, "".$_->{'title'}.""; next PAGE; }

my $msg=undef; # First, check for a deletion. my $r=$api->query(                   letitle => $_->{'title'},                    list    => 'logevents',                    letype  => 'delete',                    lelimit => 1,                    leprop  => 'user|timestamp|comment',                ); if($r->{'code'} ne 'success'){ $api->warn("Failed to retrieve logs for ".$_->{'title'}.": ".$r->{'error'}."\n"); return 60; }               if(exists($r->{'query'}{'logevents'}[0])){ my $log=$r->{'query'}{'logevents'}[0];

# Skip for now if it was deleted within the past hour, to                   # give the closing admin a chance to close it themself. my $t=$api->ISO2timestamp($log->{'timestamp'}); next PAGE if(time-$t<3600);

# Check whether the deletion log entry could reasonably # belong to this PUF: the deletion log entry should be                   # dated on or after the PUF page date. Give them a day of                   # leeway just in case. $t=_date_add(_make_date($t),1,0,0); if(_cmp_date($t,$date)>=0){ # Close it! $msg="Delete; deleted"; if($log->{'comment'}=~/CSD(?:#|\]\] | )([GIF]\d+)/i ||                          $log->{'comment'}=~/db-([gif]\d+)/){ my $c=uc($1); $msg.=" as $c"; } elsif($log->{'comment'}=~/db-([a-z])/ && exists($db{$1})){ my $c=$db{$1}; $msg.=" as $c"; } elsif($log->{'comment'}=~/\x7b\x7b\s*isd\s*[|\x7d]/){ $msg.=" as F1"; }                       $msg.=" by "; $msg.=' A file with this name on Commons is now visible.' if $_->{'imagerepository'} eq 'shared'; }               }                if(!defined($msg) && $_->{'imageinfo'}[0]{'canonicaltitle'} ne $_->{'title'} && exists($_->{'redirect'})){ # It's a redirect. Is it because it was moved, or because # it was created that way?

# Get target my %targets=$api->resolve_redirects($_->{'title'}); if(exists($targets{''})){ $api->log("Could not resolve redirect ".$_->{'title'}.": ".$targets{''}{'error'}); next PAGE; }                   my $target=$targets{$_->{'title'}};

my $r=$api->query(                       letitle => $_->{'title'},                        list    => 'logevents',                        letype  => 'move',                        lelimit => 1,                    ); if($r->{'code'} ne 'success'){ $api->warn("Failed to retrieve logs for ".$_->{'title'}.": ".$r->{'error'}."\n"); return 60; }                   if(exists($r->{'query'}{'logevents'}[0])){ my $log=$r->{'query'}{'logevents'}[0];

# Skip for now if it was moved within the past hour, # to see if the mover closes it themself. my $ts=$api->ISO2timestamp($log->{'timestamp'}); next PAGE if(time-$ts<3600);

# Check whether the move log entry could reasonably # belong to this PUF: the move log entry should be                       # dated on or after the PUF page date. Give them a day # of leeway just in case. my $t=_date_add(_make_date($ts),1,0,0); if(_cmp_date($t,$date)>=0){ # Yes: comment and change the title, but don't                           # actually close. my $from=$log->{'title'}; my $to=$log->{'params'}{'target_title'}; my $user=$log->{'user'}; my $ts=strftime("%H:%M, %d %B %Y (UTC)", gmtime $ts); $ts=~s/, 0/, /; $img{$img}{'title'}="$target"; $img{$img}{'body'}=~s/\s*$//; $img{$img}{'body'}.="\n* Note: The file was moved from $from to $to by at $ts"; $img{$img}{'body'}.="; it now redirects to $target" if $target ne $to; $img{$img}{'body'}.=". ~\n"; push @moved, "$from→$target"; next PAGE; }                   }

# Redirect, but not because of a move. Close it. my $iter=$api->iterator(                       titles    => $_->{'title'},                        prop      => 'revisions',                        rvprop    => 'timestamp|content',                        rvslots   => 'main',                        rvsection => 0,                        rvlimit   => 5,                    ); my $ts=0; my $redir_re=$api->redirect_regex; while(my $res=$iter->next){ if(!$res->{'_ok_'}){ $api->warn("Could not retrieve revisions from iterator: ".$res->{'error'}."\n"); return 60; }                       $res=$res->{'revisions'}[0]; last unless $res->{'slots'}{'main'}{'*'}=~$redir_re; $ts=$api->ISO2timestamp($res->{'timestamp'}); }

# WTF? if($ts==0){ my $t=$_->{'title'}; $api->warn("$t claims it's a redirect, but no #REDIRECT found?\n"); $api->whine("$t confuses me", "When checking $t, the API prop=info reported it's a redirect, but the top revision does not match $redir_re. Which probably means my code needs fixing."); next PAGE; }

# Skip if it was redirectified too recently next PAGE if(time-$ts<3600);

if($target=~/^(?:File|Image):/i){ $msg="Redirect. The file nominated is a redirect. If you are trying to nominate the redirect for deletion, list it at WP:RFD. If you are trying to nominate $target for deletion, please nominate it by that name."; } else { $msg="Bad Redirect. The file nominated is a redirect to $target, which is outside the File namespace. Please fix it, or tag it with db-imagepage if it cannot be fixed."; }               }                if(!defined($msg)){ # If we get here, there was no (valid) deletion log for # this file, but it still doesn't exist locally. if($_->{'imagerepository'} eq 'shared'){ $msg="Wrong forum. The file is on Commons. Please nominate it for deletion there if you feel it is non-free."; } else { $msg="File does not exist. If the file name in the header contains a typo, feel free to correct the typo and un-close this discussion."; }               }                next if !defined($msg); $img{$img}{'body'}=~s/^\s+|\s+$//g; $img{$img}{'body'}="$msg ~\n".$img{$img}{'body'}."\n\n"; $ct--; push @closed, "".$_->{'title'}.""; }       }

# Mark for closing if applicable push @closedpages, $tt if $ct==0;

# Need to edit? next unless(@closed || @moved || $fixed || $fixedhead || @commentednonfree);

# Processed, now reconstruct the page $outtxt=$api->join_sections(@sections);

# Subst templates, if necessary my $subst=0; $outtxt=$api->process_templates($outtxt, sub {           my $name=shift;            shift; #$params            my $wikitext=shift;

return undef unless exists($tosubst{"Template:$name"}); $subst++; $wikitext=~s/^\{\{\s*/\{\{subst:/; return $wikitext; });

# Create summary my @summary=; if($fixedhead){ if(exists($tok->{'missing'})){ push @summary, "new discussion page: ".$date->[2].' '.$months[$date->[1]].' '.$date->[0]; } else { push @summary, "fix page header"; }       }        push @summary, "subst  and/or " if $subst>0; push @summary, 'move closing box'.(($fixed>1)?'es':'').' per WP:DPR' if $fixed; push @summary, 'close discussions for deleted/nonexistent files: '.join(', ', @closed) if @closed; push @summary, 'rename discussions for moved files: '.join(', ', @moved) if @moved; push @summary, 'commented on non-free files: '.join(', ', @commentednonfree) if @commentednonfree; my $summary='(BOT) '.ucfirst(join('; ', @summary)).$screwup; $api->log("$summary in $title"); if(length($summary)>500){ @summary=; if($fixedhead){ if(exists($tok->{'missing'})){ push @summary, "new discussion page: ".$date->[2].' '.$months[$date->[1]].' '.$date->[0]; } else { push @summary, "fix page header"; }           }            push @summary, "subst  and/or " if $subst>0; push @summary, 'move closing box'.(($fixed>1)?'es':'').' per WP:DPR' if $fixed; push @summary, 'close discussions for deleted/nonexistent files: [too many to list]' if @closed; push @summary, 'rename discussions for moved files: [too many to list]' if @moved; push @summary, 'commented on non-free files: [too many to list]' if @commentednonfree; $summary='(BOT) '.ucfirst(join('; ', @summary)).$screwup; }

my $r=$api->edit($tok, $outtxt, $summary, 0, 1); if($r->{'code'} ne 'success'){ $api->warn("Write failed on $title: ".$r->{'error'}."\n"); return 60; }   }

my $tok=$api->edittoken('Wikipedia:Possibly unfree files'); 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 Wikipedia:Possibly unfree files: ".$tok->{'error'}."\n"); return 60; }   my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'}; my $outtxt=$intxt; my @s=; if($intxt=~/(\n==\s*Holding cell\s*==\s*\n.*?\n)==/s){ my ($s1,$s2)=($1,$1); if(@closedpages){ my $re=join('|',map quotemeta($_), @closedpages); $s2=~s/\s*\n\*\s*\[\[(?:(?i:Wikipedia|WP)\s*:\s*Possibly unfree files)?(?:$re)\s*\]\]\s*(?=\n)//g; $s2=~s/\s*$//; push @s, "Removing completed dates from holding cell"; }       if($addNewDay){ my @t=gmtime(time-8*86400); my $t=strftime("/%Y %B %e", @t); unless($s2 =~ /(^|\n)\*\s*\[\[\Q$t\E\]\]/){ $s2 =~ s/\s*$/\n*$t\n/; push @s, "Adding new date to holding cell"; }       }        $outtxt=~s/\Q$s1\E/$s2\n\n/; }   my $ot=$outtxt; $ot=~s/\s//g; my $it=$intxt; $it=~s/\s//g; if($ot ne $it){ my $s=join('; ', @s); $api->log($s); my $r=$api->edit($tok, $outtxt, "(BOT) $s.$screwup", 0, 1); if($r->{'code'} ne 'success'){ $api->warn("Write failed on Wikipedia:Possibly unfree files: ".$r->{'error'}."\n"); return 60; }   }

# Save checked revision $self->{'lasttime'}=$starttime; $api->store->{'lasttime'}=$starttime;

$t=82800-($starttime%86400); return $starttime+$t-time if($t>0 && $t<3600); $t=86400-($starttime%86400); return $starttime+$t-time if($t>0 && $t<3600); return $starttime+3600-time; }

sub _make_date { my $t=shift || time; if(ref($t) eq 'ARRAY'){ return _fix_date([@$t]); } else { my @t=gmtime($t); @t=@t[3..5]; $t[1]+=1; $t[2]+=1900; return [@t]; } }

sub _date_add { my @t=@{$_[0]}; $t[0]+=$_[1]; $t[1]+=$_[2]; $t[2]+=$_[3]; return _fix_date([@t]); }

sub _fix_date { my $t=shift; my @t=gmtime(timegm(0,0,0,$t->[0],$t->[1]-1,$t->[2]-1900)); @t=@t[3..5]; $t[1]+=1; $t[2]+=1900; return [@t]; }

sub _cmp_date { my $a=shift; my $b=shift; my $x;

$x=$a->[2]-$b->[2]; $x=$a->[1]-$b->[1] if $x==0; $x=$a->[0]-$b->[0] if $x==0; return $x; }

sub _makepagehead { my $date=shift; my $prevdt=_date_add($date,-1,0,0); my $nextdt=_date_add($date,1,0,0); return '  '.'==='.$months[$date->[1]].' '.$date->[0].'==='; }

1;