#!/usr/bin/perl -w
#
# flc.pl -- Pass or fail an Featured Article class review
# This Bot runs every day, looking for featured list articles that have been promoted by a delegate
# If it finds one, it follows the steps involved in promoting or failing it.
# Usage: flc.pl
# 29 Nov 15 Created
# 28 Dec 15 Create update_featured_list_log
# 11 Jan 16 Do not leave a blank line on the nomination page
# Put the FLC star before DEFAULTSORT
# 24 Jan 16 Featured list removal
# 17 Aug 16 Corrected typo causing duplicated Featured log entries
# 3 Sep 17 Correction for new announcements page format
# 16 Nov 17 Correct revid in article history
use English;
use strict;
use utf8;
use warnings;
use Carp;
use Data::Dumper;
use Date::Calc qw(Delta_Days);
use File::Basename qw(dirname);
use File::Spec;
use MediaWiki::Bot;
use POSIX qw(strftime);
use XML::Simple;
binmode (STDERR, ':utf8');
binmode (STDOUT, ':utf8');
# Pages used
my $candidates = 'Wikipedia:featured list candidates';
my $category = 'Wikipedia featured list candidates';
my $removal_candidates = 'Wikipedia:featured list removal candidates';
my $removal_category = 'Wikipedia featured list removal candidates';
my $removal_log = 'Wikipedia:Featured list removal candidates/log/%M %Y';
my $promoted_log = 'Wikipedia:Featured list candidates/Featured log/%M %Y';
my $failed_log = 'Wikipedia:Featured list candidates/Failed log/%M %Y';
my $announcements = 'Template:Announcements/New featured content';
my $goings_on = 'Wikipedia:Goings-on';
my $featured_list_log = 'Template:Featured list log';
my $nomination_page; # used only for testing
my @months = qw(January February March April May June July August September October November December);
################################################################################
my $sandbox_test = 0;
if ($sandbox_test) {
$candidates = 'User:Hawkeye7/sandbox/test1';
$nomination_page = 'User:Hawkeye7/sandbox/test2';
$category = 'Hawkeye7 test pages';
$promoted_log = 'User:Hawkeye7/Featured log/%M %Y';
$failed_log = 'User:Hawkeye7/Failed log/%M %Y';
$announcements = 'User:Hawkeye7/sandbox/test3';
$goings_on = 'User:Hawkeye7/sandbox/test4';
$featured_list_log = 'User:Hawkeye7/sandbox/test5';
}
################################################################################
my $editor = MediaWiki::Bot->new ({
assert => 'bot',
host => 'en.wikipedia.org',
protocol => 'https',
}) or die "new MediaWiki::Bot failed";
my $dirname = dirname (__FILE__, '.pl');
push @INC, $dirname;
require Cred;
my $cred = new Cred ();
my $log = $cred->log ();
sub showtime (@) {
print $log strftime ('%H:%M:%S %a %d %b %Y', localtime (time)), @ARG;
}
sub error_exit ($) {
my @message = @ARG;
if ($editor->{error}->{code}) {
push @message, ' (', $editor->{error}->{code} , ') : ' , $editor->{error}->{details};
}
showtime ': ', @message, "\n";
croak @message;
}
sub allow_bots ($$;$) {
my($text, $user, $opt) = @ARG;
return 0 if $text =~ /{{[nN]obots}}/;
return 1 if $text =~ /{{[bB]ots}}/;
if ($text =~ /{{[bB]ots\s*\|\s*allow\s*=\s*(.*?)\s*}}/s){
return 1 if $1 eq 'all';
return 0 if $1 eq 'none';
my @bots = split(/\s*,\s*/, $1);
return (grep $ARG eq $user, @bots)?1:0;
}
if ($text =~ /{{[bB]ots\s*\|\s*deny\s*=\s*(.*?)\s*}}/s){
return 0 if $1 eq 'all';
return 1 if $1 eq 'none';
my @bots = split(/\s*,\s*/, $1);
return (grep $ARG eq $user, @bots)?0:1;
}
if (defined($opt) && $text =~ /{{[bB]ots\s*\|\s*optout\s*=\s*(.*?)\s*}}/s){
return 0 if $1 eq 'all';
my @opt = split(/\s*,\s*/, $1);
return (grep $ARG eq $opt, @opt)?0:1;
}
return 1;
}
sub whodunnit ($$) {
my ($article, $nomination) = @ARG;
my $old;
my @history = $editor->get_history ($nomination) or
error_exit ("Unable to get history of '$nomination'");
foreach my $revision (@history) {
# print Dumper $revision, "\n";
my $text = $editor->get_text ($nomination, $revision->{revid}) or
error_exit ("Unable to find '$nomination:$revision->{revid}')");
die "no bots allowed on '$nomination'" unless allow_bots ($text, $cred->user);
if ($text !~ /{{FLCClosed\|(.+?)}}/) {
my $action = $1;
print $log "\t$article was $action by $old->{user} at $old->{timestamp_date} $old->{timestamp_time}\n";
my $diff = "https://en.wikipedia.org/w/index.php?title=$nomination\&diff=$old->{revid}\&oldid=$revision->{revid}";
$diff =~ s/ /_/g;
# print $diff, "\n";
return ($old->{user}, $old->{timestamp_date}, $old->{timestamp_time}, $diff, $revision->{revid});
} else {
$old = $revision;
}
}
}
sub has_been_closed ($$) {
my ($article, $nomination) = @ARG;
print $log "checking if $nomination has been promoted...\n";
my $text = $editor->get_text ($nomination) or do {
# Nomination in progress?
showtime ("Unable to find nomination page '$nomination')");
return ();
};
if ($text =~ /{{FLCClosed\|(.+?)}}/) {
# No timestamp - get it from whodunnit
my $status = $1;
my ($coordinator, $date, $time, $diff, $revid) = whodunnit ($article, $nomination);
# print "date='$date' time='$time'\n";
$date =~ /(\d+)-(\d+)-(\d+)/;
my ($year, $month, $day) = ($1, $2, $3);
$day =~ s/^0//;
$month = $months[$month-1];
my %timestamp;
$timestamp{DISPLAY_DATE} = "$time $day $month $year";
$timestamp{TIME} = $time;
$timestamp{DATE} = $date;
$timestamp{DAY} = $day;
$timestamp{MONTH} = $month;
$timestamp{YEAR} = $year;
$timestamp{USER} = $coordinator;
$timestamp{DIFF} = $diff;
$timestamp{REVID} = $revid;
$timestamp{STATUS} = $status;
return ($status, \%timestamp);
}
return ();
}
sub find_the_nomination_page ($) {
my ($talk) = @ARG;
if ($sandbox_test) {
return $nomination_page;
}
my $text = $editor->get_text ($talk) or
error_exit ("Unable to find '$talk')");
die "no bots allowed on '$talk'" unless allow_bots ($text, $cred->user);
$text =~ /{{featured list candidates\|(.+?\/archive\d+)}}/ or do {
error_exit ("Unable to find nomination page for '$talk'");
};
my $nomination = "Wikipedia:Featured list candidates/$1";
my $encoded_nomination = $nomination;
$nomination =~ s/&#([0-9a-f]+);/chr($1)/ige;
print "\t$nomination\n";
return ($nomination, $encoded_nomination);
}
sub archive_nomination ($$$$) {
print "\tArchiving the nomination\n";
my ($nomination, $comment, $summary,$timestamp) = @ARG;
my $text = $editor->get_text ($nomination) or
error_exit ("Unable to find '$nomination'");
die "no bots allowed on '$nomination'" unless allow_bots ($text, $cred->user);
my $status = $timestamp->{STATUS};
my $coordinator = $timestamp->{USER};
my $diff = $timestamp->{DIFF};
$text = join "\n",
"{{subst:Fl top |result= '''$status''' by [[User:$coordinator|$coordinator]] via ~~~~ [$diff]}}",
$text,
'{{subst:Fl bottom}}';
$editor->edit ({
page => $nomination,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$nomination'");
}
sub remove_nomination_from_candidates_page ($$) {
print "\tRemoving the nomination from the candidates page\n";
my ($nomination, $summary) = @ARG;
my $text = $editor->get_text ($candidates) or
error_exit ("Unable to find '$candidates'");
die "no bots allowed on '$candidates'" unless allow_bots ($text, $cred->user);
$text =~ s/{{\Q$nomination\E}}\s*\n//s;
$editor->edit ({
page => $candidates,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$candidates'");
}
sub add_nomination_to_log_page ($$$$) {
print "\tAdding the nomination to the log page\n";
my ($nomination, $summary, $timestamp, $log) = @ARG;
$log =~ s/%M/$timestamp->{MONTH}/;
$log =~ s/%Y/$timestamp->{YEAR}/;
my $text = $editor->get_text ($log);
if (!defined $text) {
$text = "{{Featured list log}}\n{{TOClimit|3}}\n\n";
}
die "no bots allowed on '$log'" unless allow_bots ($text, $cred->user);
$text =~ s/({{TOClimit\|\d+}}\n\n)/$1\{\{$nomination\}\}\n/s;
$editor->edit ({
page => $log,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$log'");
}
sub update_article_page ($$) {
print "\tUpdating the article page\n";
my ($article, $summary) = @ARG;
my $text = $editor->get_text ($article) or
error_exit ("Unable to find '$article'");
die "no bots allowed on '$article'" unless allow_bots ($text, $cred->user);
my $tag = '{{featured list}}';
if ($text !~ s/(\{\{DEFAULTSORT)/$tag\n$1/i) {
if ($text !~ s/(\[\[Category:)/$tag\n$1/i) {
$text .= "$tag\n";
}
}
$editor->edit ({
page => $article,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$article'");
}
sub update_announcements_page ($$) {
print "\tUpdating the announcements page\n";
my ($article, $summary) = @ARG;
my $text = $editor->get_text ($announcements) or
error_exit ("Unable to find '$announcements'");
die "no bots allowed on '$announcements'" unless allow_bots ($text, $cred->user);
my $in_list_section = 0;
my $section_max;
my @input_lines = split /\n/, $text;
my @output_lines;
foreach (@input_lines) {
if (/<!-- Lists \((\d+), most recent first\) -->/) {
$section_max = $1;
$in_list_section++;
my $a = $article;
if ($a =~ s/List of //) {
push @output_lines, $ARG, "* [[$article|$a]]";
} else {
push @output_lines, $ARG, "* [[$article]]";
}
next;
}
if ($in_list_section) {
if (/^$|<\/div>|<!-- Pictures \(\d+, most recent first\) -->/) {
$in_list_section = 0;
} elsif ($in_list_section < $section_max) {
$in_list_section++;
} else {
next;
}
}
push @output_lines, $ARG;
}
$text = join "\n", @output_lines;
$editor->edit ({
page => $announcements,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$announcements'");
}
sub text_to_month ($) {
my ($month) = @ARG;
for my $i (0..11) {
if ($months[$i] eq $month) {
return $i + 1;
}
}
}
sub update_goings_on_page ($$$) {
print "\tUpdating the goings_on page\n";
my ($article, $summary, $timestamp) = @ARG;
my $text = $editor->get_text ($goings_on) or
error_exit ("Unable to find '$goings_on'");
die "no bots allowed on '$goings_on'" unless allow_bots ($text, $cred->user);
my ($m, $d, $y) = $text =~ /week starting Sunday, \[\[(\w+) (\d+)\]\], \[\[(\d+)\]\]/;
# print "$d $m $y\n";
my $delta_days = Delta_Days ($y, text_to_month ($m), $d, $timestamp->{YEAR}, text_to_month ($timestamp ->{MONTH}), $timestamp->{DAY});
# print "delta days=$delta_days\n"; # Normally positive
if ($delta_days < 0) {
print $log "\t\tArticle dated $timestamp->{DAY} $timestamp ->{MONTH} $timestamp->{YEAR} but page is $d $m $y -- skipping\n";
return;
}
my $abbr = substr ($timestamp ->{MONTH}, 0, 3);
my $date = "$timestamp->{DAY} $abbr";
my $in_list_section = 0;
my @input_lines = split /\n/, $text;
my @output_lines;
foreach (@input_lines) {
if (/Wikipedia:Featured lists/) {
$in_list_section = 1;
}
if ($in_list_section) {
if (/^$|Wikipedia:Featured pictures/) {
$in_list_section = 0;
push @output_lines, "* [[$article]] ($date)";
}
}
push @output_lines, $ARG;
}
$text = join "\n", @output_lines;
$editor->edit ({
page => $goings_on,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$goings_on'");
}
sub update_featured_list_log ($$$) {
print "\tUpdating the featured list log\n";
my ($status, $summary, $timestamp) = @ARG;
my $text = $editor->get_text ($featured_list_log) or
error_exit ("Unable to find '$featured_list_log'");
die "no bots allowed on '$featured_list_log'" unless allow_bots ($text, $cred->user);
my $year = $timestamp->{YEAR};
my $month = $timestamp->{MONTH};
sub new_month ($$$$$$) {
my ($month, $year, $promoted, $failed, $kept, $removed) = @ARG;
my @new_year = ($month eq 'January') ? ('|-', "|colspan=\"3\"|'''$year'''") : ();
return join "\n", @new_year, '|-', "|$month",
"|[[Wikipedia:Featured list candidates/Featured log/$month $year|$promoted promoted]]",
"|[[Wikipedia:Featured list candidates/Failed log/$month $year|$failed failed]]",
"|[[Wikipedia:Featured list removal candidates/log/$month $year|$removed removed/$kept kept]]",
"|}";
}
foreach ($text) {
if ($status eq 'promoted') {
if (/(\|\[\[Wikipedia:Featured list candidates\/Featured log\/$month $year\|)(\d+) promoted/) {
my $match = $1;
my $count = $2 + 1;
s/\Q$match\E\d+ promoted/$match$count promoted/;
} else {
my $new_month = new_month ($month, $year, 1, 0, 0, 0);
s/\|}/$new_month/;
}
} elsif ($status eq 'failed') {
if (/(\|\[\[Wikipedia:Featured list candidates\/Failed log\/$month $year\|)(\d+) failed/) {
my $match = $1;
my $count = $2 + 1;
s/\Q$match\E\d+ failed/$match$count failed/;
} else {
my $new_month = new_month ($month, $year, 0, 1, 0, 0);
s/\|}/$new_month/;
}
} elsif ($status eq 'kept') {
if (/(\|\[\[Wikipedia:Featured list removal candidates\/log\/$month $year\|)(\d+) removed\/(\d+) kept/) {
my $match = $1;
my $removed = $2;
my $kept = $3 + 1;
s/\Q$match\E.+/$match$removed removed\/$kept kept]]/;
} else {
my $new_month = new_month ($month, $year, 0, 0, 1, 0);
s/\|}/$new_month/;
}
} elsif ($status eq 'removed') {
if (/(\|\[\[Wikipedia:Featured list removal candidates\/log\/$month $year\|)(\d+) removed\/(\d+) kept/) {
my $match = $1;
my $removed = $2 + 1;
my $kept = $3;
s/\Q$match\E.+/$match$removed removed\/$kept kept]]/;
} else {
my $new_month = new_month ($month, $year, 0, 0, 0, 1);
s/\|}/$new_month/;
}
} else {
die "unknown status: '$status'";
}
}
$editor->edit ({
page => $featured_list_log,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$featured_list_log'");
}
sub parse_template ($@) {
my ($text, @args) = @ARG;
my %p;
while ($text =~ s/\|(\w+)\s*=\s*([^}|]+)//is) {
$p{$1}=$2;
}
my @p = split '\|', $text;
param:foreach my $p (@p) {
next param unless $p;
foreach my $arg (@args) {
if (!defined $p{$arg}) {
$p{$arg} = $p;
next param;
}
}
}
# foreach my $p (keys %p) {
# print "$p => $p{$p}\n";
# }
return %p;
}
sub get_revid ($$$) {
my ($page, $date, $time) = @ARG;
my @history = $editor->get_history ($page) or
error_exit ("Unable to get history of '$page'");
foreach my $history (@history) {
if ($history->{timestamp_date} le $date ||
($history->{timestamp_date} eq $date && $history->{timestamp_time} le $time)) {
return $history->{revid};
}
}
error_exit ("Unable to get revid of '$page')");
}
sub newaction ($$$$$$) {
my ($action, $date, $link, $result, $revid, $id) = @ARG;
my $newaction = join "\n",
"|action${id}=$action",
"|action${id}date=$date",
"|action${id}link=$link",
"|action${id}result=$result",
"|action${id}oldid=$revid";
return $newaction;
}
sub update_current_status ($$) {
my ($text, $current_status) = @ARG;
if ($current_status) {
foreach ($text) {
unless (s/(currentstatus=\s*\w+)/currentstatus=$current_status/) {
s/{{(ArticleHistory.+?)}}/{{$1\n|currentstatus=$current_status\n}}/s;
}
}
}
return $text;
}
sub update_article_history ($$$$$$$) {
my ($text, $action, $date, $link, $result, $revid, $current_status) = @ARG;
$text =~ s/{{Article\s*History/{{ArticleHistory/is;
my ($articleHistory) = $text =~ /{{ArticleHistory(.+?)}}/gis;
if ($articleHistory) {
my $has_nested_text = 0;
while ($text =~ /{{ArticleHistory[^}]+({{[^}]+}})/) {
# print "Nested text!!!!\n";
my $nested_text = $1;
# print "nested text=$nested_text\n";
my $transformed_text = $nested_text;
$transformed_text =~ s/{{(.+)}}/%%<$1>%%/;
# print "transformed text=$transformed_text\n";
$text =~ s/\Q$nested_text\E/$transformed_text/;
$has_nested_text = 1;
}
# print "articlehistory='$articleHistory'\n";
id:for (my $id = 1;; ++$id) {
if ($articleHistory =~ /action$id/) {
# print "\t\tfound action$id\n";
} else {
# print "\t\tno $id - going with that\n";
my $newaction = newaction ($action, $date, $link, $result, $revid, $id);
$text =~ s/{{Article\s*History(.+?)}}/{{ArticleHistory$1\n$newaction\n}}/is;
last id;
}
}
$text = update_current_status ($text, $current_status);
if ($has_nested_text) {
$text =~ s/%%</{{/g;
$text =~ s/>%%/}}/g;
}
} else {
my $newaction = newaction ($action, $date, $link, $result, $revid, 1);
$text =~ s/^/{{ArticleHistory\n$newaction\n}}\n/is;
$text = update_current_status ($text, $current_status);
}
return $text;
}
sub add_pr_to_history ($$) {
my ($article, $text) = @ARG;
print $log "\tFound old Peer Review\n";
while ($text =~ s/{{oldpeerreview(.+?)}}//is) {
my %h = parse_template ($1, 'name', 'archive');
my $name = $h{name} // $article;
my $archive = defined $h{archive} ? "/archive$h{archive}" : '';
my $link = "Wikipedia:Peer_review/$name$archive";
my ($history) = $editor->get_history ($link) or
error_exit ("Unable to get history of '$link'");
my $date = $history->{timestamp_date};
my $time = $history->{timestamp_time};
my $revid = get_revid ($article, $date, $time);
$text = update_article_history ($text, 'PR', $date, $link, 'reviewed ', $revid, undef);
}
return $text;
}
sub update_talk_page ($$$$$$) {
print "\tUpdating the talk page\n";
my ($status, $article, $talk, $nomination, $summary, $timestamp) = @ARG;
my $text = $editor->get_text ($talk) or
error_exit ("Unable to find '$talk'");
die "no bots allowed on '$talk'" unless allow_bots ($text, $cred->user);
my $revid = get_revid ($article, $timestamp->{DATE}, $timestamp->{TIME});
# add an old Peer review, if any, to the article history
if ($text =~ /{{oldpeerreview.+?}}/gis) {
$text = add_pr_to_history ($article, $text);
}
if ($status eq 'promoted') {
# Remove the FLC card
$text =~ s/{{featured list candidates.+?}}\s*//;
# Update the class for all projects
$text =~ s/([^-]class)\s*=\s*(\w+)/$1=FL/igs;
$text = update_article_history ($text, 'FLC', $timestamp->{DISPLAY_DATE}, $nomination, 'promoted', $revid, 'FL');
} elsif ($status eq 'failed') {
# Remove the FLC card
$text =~ s/{{featured list candidates.+?}}\s*//;
$text = update_article_history ($text, 'FLC', $timestamp->{DISPLAY_DATE}, $nomination, 'failed', $revid, 'FFLC');
} elsif ($status eq 'kept') {
# Remove the FLR card
$text =~ s/{{featured list removal candidates.+?}}\s*//;
$text = update_article_history ($text, 'FLR', $timestamp->{DISPLAY_DATE}, $nomination, 'kept', $revid, 'FL');
} elsif ($status eq 'removed') {
# Remove the FLR card
$text =~ s/{{featured list removal candidates.+?}}\s*//;
# Update the class for all projects
$text =~ s/([^-]class)\s*=\s*(\w+)/$1=List/igs;
$text = update_article_history ($text, 'FLR', $timestamp->{DISPLAY_DATE}, $nomination, 'removed', $revid, 'FFL');
} else {
die "unknown status: '$status'";
}
$editor->edit ({
page => $talk,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$talk'");
}
sub find_the_removal_nomination_page ($) {
my ($talk) = @ARG;
if ($sandbox_test) {
return $nomination_page;
}
my $text = $editor->get_text ($talk) or
error_exit ("Unable to find '$talk')");
die "no bots allowed on '$talk'" unless allow_bots ($text, $cred->user);
$text =~ /{{featured list removal candidates\|(.+?\/archive\d+)}}/ or do {
error_exit ("Unable to find nomination page for '$talk'");
};
my $nomination = "Wikipedia:Featured list removal candidates/$1";
my $encoded_nomination = $nomination;
$nomination =~ s/&#([0-9a-f]+);/chr($1)/ige;
print "\t$nomination\n";
return ($nomination, $encoded_nomination);
}
sub remove_nomination_from_removal_candidates_page ($$) {
print "\tRemoving the nomination from the removal candidates page\n";
my ($nomination, $summary) = @ARG;
my $text = $editor->get_text ($removal_candidates) or
error_exit ("Unable to find '$removal_candidates'");
die "no bots allowed on '$removal_candidates'" unless allow_bots ($text, $cred->user);
$text =~ s/{{\Q$nomination\E}}\s*\n//s;
$editor->edit ({
page => $removal_candidates,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$candidates'");
}
sub add_nomination_to_removal_log_page ($$$$) {
print "\tAdding the nomination to the log page\n";
my ($nomination, $summary, $timestamp, $log) = @ARG;
my $status = $timestamp->{STATUS};
$log =~ s/%M/$timestamp->{MONTH}/;
$log =~ s/%Y/$timestamp->{YEAR}/;
my $text = $editor->get_text ($log);
if (!defined $text) {
$text = "{{Featured list log}}\n{{TOClimit|3}}\n==Keep==\n\n==Delist==\n";
}
die "no bots allowed on '$log'" unless allow_bots ($text, $cred->user);
if ($status eq 'kept') {
$text =~ s/(==Keep==\n)/$1\{\{$nomination\}\}\n/s;
} elsif ($status eq 'removed') {
$text =~ s/(==Delist==\n)/$1\{\{$nomination\}\}\n/s;
}
$editor->edit ({
page => $log,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$log'");
}
sub remove_star_from_article_page ($$) {
print "\tUpdating the article page\n";
my ($article, $summary) = @ARG;
my $text = $editor->get_text ($article) or
error_exit ("Unable to find '$article'");
die "no bots allowed on '$article'" unless allow_bots ($text, $cred->user);
$text =~ s/\{\{featured list\}\}\s*\n//is;
$editor->edit ({
page => $article,
text => $text,
summary => $summary,
minor => 0,
}) or
error_exit ("unable to edit '$article'");
}
sub promoted ($$$$) {
my ($article, $talk, $nomination, $timestamp) = @ARG;
my $coordinator = $timestamp->{USER};
my $status = $timestamp->{STATUS};
print "\tpromoting $article\n";
my $comment = "The list was '''$status''' by $coordinator via";
my $summary = "$article promoted to Featured List";
update_talk_page ('promoted', $article, $talk, $nomination, $summary, $timestamp);
remove_nomination_from_candidates_page ($nomination, $summary);
archive_nomination ($nomination, $comment, $summary, $timestamp);
add_nomination_to_log_page ($nomination, $summary, $timestamp, $promoted_log);
update_article_page ($article, $summary);
update_announcements_page ($article, $summary);
update_goings_on_page ($article, $summary, $timestamp);
update_featured_list_log ('promoted', $summary, $timestamp);
print "\tdone\n";
}
sub failed ($$$$) {
my ($article, $talk, $nomination, $timestamp) = @ARG;
my $coordinator = $timestamp->{USER};
my $status = $timestamp->{STATUS};
print "\tnot promoting $article\n";
my $comment = "The list was '''$status''' by $coordinator via";
my $summary = "$article not promoted to Featured List";
update_talk_page ('failed', $article, $talk, $nomination, $summary, $timestamp);
remove_nomination_from_candidates_page ($nomination, $summary);
archive_nomination ($nomination, $comment, $summary, $timestamp);
add_nomination_to_log_page ($nomination, $summary, $timestamp, $failed_log);
update_featured_list_log ('failed', $summary, $timestamp);
print "\tdone\n";
}
sub kept ($$$$) {
my ($article, $talk, $nomination, $timestamp) = @ARG;
my $coordinator = $timestamp->{USER};
my $status = $timestamp->{STATUS};
print "\tkeeping $article\n";
my $comment = "The list was '''$status''' by $coordinator via";
my $summary = "$article kept as Featured List";
update_talk_page ('kept', $article, $talk, $nomination, $summary, $timestamp);
remove_nomination_from_removal_candidates_page ($nomination, $summary);
archive_nomination ($nomination, $comment, $summary, $timestamp);
add_nomination_to_removal_log_page ($nomination, $summary, $timestamp, $removal_log);
update_featured_list_log ('kept', $summary, $timestamp);
print "\tdone\n";
}
sub removed ($$$$) {
my ($article, $talk, $nomination, $timestamp) = @ARG;
my $coordinator = $timestamp->{USER};
my $status = $timestamp->{STATUS};
print "\tremoving $article\n";
my $comment = "The list was '''$status''' by $coordinator via";
my $summary = "$article removed from Featured List";
update_talk_page ('removed', $article, $talk, $nomination, $summary, $timestamp);
remove_nomination_from_removal_candidates_page ($nomination, $summary);
archive_nomination ($nomination, $comment, $summary, $timestamp);
add_nomination_to_removal_log_page ($nomination, $summary, $timestamp, $removal_log);
remove_star_from_article_page ($article, $summary);
update_featured_list_log ('removed', $summary, $timestamp);
print "\tdone\n";
}
sub is_older_nomination ($$) {
my ($nomination, $twenty_days_ago) = @ARG;
print $nomination, "\n";
my @history = $editor->get_history ($nomination) or
error_exit ("Unable to get history of '$nomination'");
my $revision = pop @history;
print "\t", $revision->{timestamp_date};
my $is_older_nomination = $revision->{timestamp_date} lt $twenty_days_ago;
print $is_older_nomination ? " is older than twenty_days_ago\n" : " is NOT older than twenty_days_ago\n" ;
return $is_older_nomination;
}
sub move_the_daily_marker () {
my $text = $editor->get_text ($candidates) or
error_exit ("Unable to find '$candidates'");
die "no bots allowed on '$candidates'" unless allow_bots ($text, $cred->user);
my @input = split /\n/, $text;
my @output;
my $nominations = 0;
my @older_nominations;
my $older_nominations = 0;
my $twenty_days_ago = strftime ('%Y-%m-%d', gmtime(time () - 20 * 24 * 60 * 60));
print "twenty days ago was $twenty_days_ago\n";
foreach (@input) {
if (/<!--|-->/) {
}elsif (/==Nominations==/) {
$nominations = 1;
} elsif (/==Older nominations==/) {
$older_nominations = 1;
$nominations = 0;
} elsif ($nominations) {
if (/{{(Wikipedia:Featured list candidates.+)}}/) {
my $nomination = $1;
if (is_older_nomination ($nomination, $twenty_days_ago)) {
push @older_nominations, "{{$nomination}}";
next;
}
}
} elsif ($older_nominations) {
if (@older_nominations) {
push @output, @older_nominations;
$older_nominations = 0;
}
}
push @output, $ARG;
}
return unless (@older_nominations);
$text = join "\n", @output;
$editor->edit ({
page => $candidates,
text => $text,
summary => 'update daily marker',
minor => 0,
}) or
error_exit ("unable to edit '$candidates'");
}
sub featured_list_candidates () {
my @candidates = $editor->get_pages_in_category ($category);
foreach my $talk (@candidates) {
eval {
my $article = $talk;
if ($article =~ s/Talk://) {
print $article, "\n";
my ($nomination, $encoded_nomination) = find_the_nomination_page ($talk);
print "\t", $nomination, "\n";
if (my ($status, $timestamp) = has_been_closed ($article, $nomination)) {
print $log "\t$nomination closed on $timestamp->{DISPLAY_DATE}\n";
print "\tnomination closed on $timestamp->{DISPLAY_DATE}\n";
if ($status eq 'promoted') {
promoted ($article, $talk, $nomination, $timestamp);
} elsif ($status eq 'not promoted' || $status eq 'failed' || $status eq 'withdrawn' || $status eq 'archived') {
failed ($article, $talk, $nomination, $timestamp);
} else {
print $log "WARNING: $nomination has unknown status '$status'\n";
warn "WARNING: $nomination has unknown status '$status'\n";
}
} else {
print $log "\tnomination is still current\n";
print "\t$nomination is still current\n";
}
}
};
if ($EVAL_ERROR) {
warn $EVAL_ERROR;
}
}
}
sub featured_list_removal_candidates () {
my @candidates = $editor->get_pages_in_category ($removal_category);
foreach my $talk (@candidates) {
eval {
my $article = $talk;
if ($article =~ s/Talk://) {
print $article, "\n";
my ($nomination, $encoded_nomination) = find_the_removal_nomination_page ($talk);
if (my ($status, $timestamp) = has_been_closed ($article, $nomination)) {
print $log "\t$nomination closed on $timestamp->{DISPLAY_DATE}\n";
print "\tnomination closed on $timestamp->{DISPLAY_DATE}\n";
if ($status eq 'kept') {
kept ($article, $talk, $nomination, $timestamp);
} elsif ($status eq 'removed') {
removed ($article, $talk, $nomination, $timestamp);
} else {
print $log "WARNING: $nomination has unknown status '$status'\n";
warn "WARNING: $nomination has unknown status '$status'\n";
}
} else {
print $log "\tnomination is still current\n";
print "\t$nomination is still current\n";
}
}
};
if ($EVAL_ERROR) {
warn $EVAL_ERROR;
}
}
}
$editor->login ({
username => $cred->user,
password => $cred->password
}) or die $editor->{error}->{code} . ': ' . $editor->{error}->{details};
showtime ("========== Commenced ==========\n");
move_the_daily_marker () unless ($sandbox_test);
featured_list_candidates ();
featured_list_removal_candidates ();
exit 0;