User:MilHistBot/announcements.pl

#!/usr/bin/perl -w

# announcements.pl -- Update the MilHist announcements page
# Usage: announcements.pl
#     3 Oct 14 Created
#    14 Oct 14 Sort entries in chronological order
#    17 Oct 14 Handle GA2 etc

use English;
use strict;
use utf8;
use warnings;

use Carp;
use File::Basename qw(dirname);
use Data::Dumper;
use IO::File;
use MediaWiki::Bot;
use POSIX;
use Time::Local;
use XML::Simple;

binmode (STDERR, ':utf8');
binmode (STDOUT, ':utf8');

my $dirname = dirname (__FILE__, '.pl');
push @INC, $dirname;
require Cred;
my $cred = new Cred ();
my $log = $cred->log ();

my %timestamp;
my %month = ('January' => 0, 'February' => 1, 'March' => 2, 'April' => 3, 'May' => 4, 'June' => 5,
    'July' => 6, 'August' => 7, 'September' => 8, 'October' => 9, 'November' => 10, 'December' => 11);

my $editor = MediaWiki::Bot->new ({
        assert        => 'bot',
        host        => 'en.wikipedia.org',
        protocol     => 'https',
}) or die "new failed";

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 error_exit ($) {
    my @message = @ARG;
    if ($editor->{error}->{code}) {
        push @message, ' (', $editor->{error}->{code} , ') : ' , $editor->{error}->{details};
    }
    $cred->error (@message);
}

sub is_milhist ($) {
    my ($page) = @ARG;
    my $talkpage = "Talk:$page";
    my $text = $editor->get_text ($talkpage) or do {
        $cred->showtime ("Unable to find '$talkpage'\n");
        return 0;
    };
    return $text =~ /{{WikiProject Military history|{{MILHIST|{{WPMILHIST/i;
}

sub timestamp ($) {
    my ($candidate) = @ARG;
    my @history = $editor->get_history ($candidate) or
        error_exit "Unable get history of '$candidate'";
    my $created = pop @history;
    my ($hour, $min, $sec) = $created->{timestamp_time} =~ /(\d+):(\d+):(\d+)/;
    my ($year, $mon, $day) = $created->{timestamp_date} =~ /(\d+)-(\d+)-(\d+)/;
    return timegm ($sec, $min, $hour, $day, $mon-1, $year);
}

sub aclass () {
    my $candidates = 'Wikipedia:WikiProject Military history/Assessment/A-Class review';
    my %candidates;
    my $text = $editor->get_text ($candidates) or
        error_exit "Unable to find '$candidates'";
    my @candidates = $text =~ /(Wikipedia:WikiProject Military history\/Assessment\/[^}]+)/g;
    foreach my $candidate (@candidates) {
        if ($candidate =~ /Wikipedia:WikiProject Military history\/Assessment\/([^}]+)/) {
            my $page = $1;
            next if $page =~ /^ACR/;
            $cred->showtime ("\t$page\n");
            $candidates{$page} = $candidate;
            $timestamp{$page} = timestamp ($candidate);
        }     
    }
    return %candidates;   
}

sub featured ($) {
    my ($candidates) = @ARG;
    my %candidates;
    my $text = $editor->get_text ($candidates) or
        error_exit "Unable to find '$candidates'";
    my @candidates = $text =~ /($candidates\/.+\/archive\d+)/g;
    foreach my $candidate (@candidates) {
        if ($candidate =~ /$candidates\/(.+)\/archive\d+/) {
            my $page = $1;
            if (is_milhist ($page)) {
                $cred->showtime ("\t$page\n");
                $candidates{$page} = $candidate;
                $timestamp{$page} = timestamp ($candidate);
            }
        }     
    }
    return %candidates;   
}

sub good_article_timestamp ($) {
    my ($candidate) = @ARG;
    my $text = $editor->get_text ($candidate) or
        error_exit "Unable to find '$candidate'";
    if ($text =~ /GA nominee\|(\d+):(\d+), (\d+) (\w+) (\d+)/) {
        my ($hour, $min, $day, $month, $year) = ($1, $2, $3, $4, $5);
        my $sec = 0;
        my $mon = $month{$month};
        return timegm ($sec, $min, $hour, $day, $mon, $year);    
    } else {
        error_exit "Unable to find GA nominee in '$candidate'";        
    }        
}

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 good_article_entry ($) {
    my ($page) = @ARG;
    my $entry = "\{\{WPMHA\/GAN\|$ARG\}\}";
    my $talk = "Talk:$page";
    my $text = $editor->get_text ($talk) or
        error_exit "Unable to find '$talk'";
    if ($text =~ s/{{GA nominee(.+?)}}//is) {
        my %h = parse_template ($1, 'temestamp', 'nominator', 'page', 'topic', 'status', 'note');
        my $number = $h{page};
        $entry = "{{WPMHA/GAN|$page|$number}}";
    }
    return $entry;         
}

sub good_article_nominees () {
    my $candidates = 'Good article nominees';
    my @milhist;
    my @candidates =  $editor->get_pages_in_category ($candidates) or
        error_exit "Unable to find '$candidates'";
    foreach my $candidate (@candidates) {
        if ($candidate =~ /Talk:(.+)/) {
            my $page = $1;
            if (is_milhist ($page)) {
                $cred->showtime ("\t$page\n");
                $timestamp{$page} = good_article_timestamp ($candidate);
                push @milhist, $page;
            }
        }
    }
    return join '  • ', map { good_article_entry ($ARG) } sort { $timestamp{$a} <=> $timestamp{$b} } @milhist;
}

sub peer_reviews () {
    my $candidates = 'Current peer reviews';
    my %candidates;
    my @candidates =  $editor->get_pages_in_category ($candidates) or
        error_exit "Unable to find '$candidates'";
    foreach my $candidate (@candidates) {
        if ($candidate =~ /Wikipedia:Peer review\/(.+)\/archive\d+/) {
            my $page = $1;
            if (is_milhist ($page)) {
                $cred->showtime ("\t$page\n");
                $candidates{$page} = $candidate;
                $timestamp{$page} = timestamp ($candidate);
            }
        }     
    }
    return %candidates;   
}

sub format_up (%) {
    my %pages = (@ARG);
    return join '  • ', map { "[[$pages{$ARG}|$ARG]]" } sort { $timestamp{$a} <=> $timestamp{$b} } keys %pages;
}

$cred->showtime (": started\n");
$editor->login ({
    username => $cred->user,
    password => $cred->password
}) or error_exit "unable to login";
    
my $announcements = 'Template:WPMILHIST Announcements';
my $text = $editor->get_text ($announcements) or
        die "Unable to find '$announcements'\n";
$cred->error ("no bots allowed on '$announcements'") unless allow_bots ($text, $cred->user);

my $bot = " <!-- Bot generated -->";

$cred->showtime ("A class reviews:\n");
my %acr = aclass ();
my $acr = format_up (%acr);
$text =~ s/A-Class_reviews=.*/A-Class_reviews=$acr $bot/;

$cred->showtime ("Featured article candidates:\n");
my %fac = featured ('Wikipedia:Featured article candidates');
my $fac = format_up (%fac);
$text =~ s/featured_article_candidates=.*/featured_article_candidates=$fac/;

$cred->showtime ("Featured article reviews:\n");
my %far = featured ('Wikipedia:Featured article review');
my $far = format_up (%far);
$text =~ s/featured_article_reviews=.*/featured_article_reviews=$far $bot/;

$cred->showtime ("Featured list candidates\n");
my %flc = featured ('Wikipedia:Featured list candidates');
my $flc = format_up (%flc);
$text =~ s/featured_list_candidates=.*/featured_list_candidates=$flc $bot/;

$cred->showtime ("Featured list removal candidates\n");
my %flr = featured ('Wikipedia:Featured list removal candidates');
my $flr = format_up (%flr);
$text =~ s/featured_list_removal_candidates=.*/featured_list_removal_candidates=$flr $bot/;

$cred->showtime ("Peer review:\n");
my %pr = peer_reviews ();
my $pr = format_up (%pr);
$text =~ s/peer_reviews=.*/peer_reviews=$pr $bot/;

$cred->showtime ("Good article nominees:\n");
my $gan = good_article_nominees ();
$text =~ s/good_article_nominees=.*/good_article_nominees=$gan $bot/;
    
$editor->edit ({
    page => $announcements,
    text => $text,
    summary => "Updating announcements page",
    bot => 1,
    minor => 0,
}) or
    error_exit ("unable to edit '$announcements'");
    
$cred->showtime (": finished\n");
$log->close ();

exit 0;