GitHub

NAME

MediaWiki::Bot - a high-level bot framework for interacting with MediaWiki wikis

VERSION

version 5.007000

SYNOPSIS

use MediaWiki::Bot qw(:constants);
my $bot = MediaWiki::Bot->new({
    assert      => 'bot',
    host        => 'de.wikimedia.org',
    login_data  => { username => "Mike's bot account", password => "password" },
});
my $revid = $bot->get_last("User:Mike.lifeguard/sandbox", "Mike.lifeguard");
print "Reverting to $revid\n" if defined($revid);
$bot->revert('User:Mike.lifeguard', $revid, 'rvv');

DESCRIPTION

MediaWiki::Bot is a framework that can be used to write bots which interface with the MediaWiki API (http://en.wikipedia.org/w/api.php).

METHODS

Initialization

new

my $bot = MediaWiki::Bot({
    host     => 'en.wikipedia.org',
    operator => 'Mike.lifeguard',
});

Calling MediaWiki::Bot->new() will create a new MediaWiki::Bot object. The only parameter is a hashref with keys:

  • agent sets a custom useragent. It is recommended to use operator instead, which is all we need to do the right thing for you. If you really want to do it yourself, see https://meta.wikimedia.org/wiki/User-agent_policy for guidance on what information must be included.

  • assert sets a parameter for the AssertEdit extension (commonly 'bot')

    Refer to http://mediawiki.org/wiki/Extension:AssertEdit.

  • operator allows the bot to send you a message when it fails an assert. This is also the recommended way to customize the user agent string, which is required by the Wikimedia Foundation. A warning will be emitted if you omit this.

  • maxlag allows you to set the maxlag parameter (default is the recommended 5s).

    Please refer to the MediaWiki documentation prior to changing this from the default.

  • protocol allows you to specify 'http' or 'https' (default is 'https')

  • host sets the domain name of the wiki to connect to (e.g. 'en.wikipedia.org')

  • path sets the path to api.php (with no leading or trailing slash, e.g. 'w')

  • login_data is a hashref of credentials to pass to "login" (see "login" for more information).

  • debug - whether to provide debug output (default is 0).

    1 provides some more warnings; 2 provides further detail on internal operations.

For example:

my $bot = MediaWiki::Bot->new({
    assert      => 'bot',
    protocol    => 'https',
    host        => 'en.wikimedia.org',
    agent       => sprintf(
        'PerlWikiBot/%s (https://metacpan.org/MediaWiki::Bot; User:Mike.lifeguard)',
        MediaWiki::Bot->VERSION
    ),
    login_data  => { username => "Mike's bot account", password => "password" },
});

For backward compatibility, you can specify up to three parameters:

my $bot = MediaWiki::Bot->new('My custom useragent string', $assert, $operator);

This form is deprecated, will never do auto-login or autoconfiguration, and emits deprecation warnings.

For further reading:

set_wiki

Set what wiki to use. The parameter is a hashref with keys:

  • host - the domain name (e.g. en.wikipedia.org)
  • path - the part of the path before api.php (e.g. 'w')
  • protocol is either 'http' or 'https'.

If you don't set any parameter, it's previous value is used. If it has never been set, the default settings are 'https', 'en.wikipedia.org' and 'w'.

For example:

$bot->set_wiki({
    protocol    => 'https',
    host        => 'en.wikimedia.org',
    path        => 'wikipedia/meta/w',
});

For backward compatibility, you can specify up to two parameters:

$bot->set_wiki($host, $path);

This form is deprecated and will emit deprecation warnings.

login

This method takes a hashref with keys username and password at a minimum. See "Single User Login" and "Basic authentication" for additional options.

Logs the $username in, optionally using $password. First, an attempt will be made to use cookies to log in. If this fails, an attempt will be made to use the password provided to log in, if any. If the login was successful, returns true; false otherwise.

$bot->login({
    username => $username,
    password => $password,
}) or die "Login failed";

Once logged in, attempt to do some simple auto-configuration. At present, this consists of:

  • Warning if the account doesn't have the bot flag, and isn't a sysop account.
  • Setting an appropriate default assert.

You can skip this autoconfiguration by passing autoconfig => 0

For backward compatibility, you can call this as

$bot->login($username, $password);

This form is deprecated, and will emit deprecation warnings. It will never do autoconfiguration or SUL login.

Single User Login

On WMF wikis, do_sul specifies whether to log in on all projects. The default is false. But even when false, you still get a CentralAuth cookie for, and are thus logged in on, all languages of a given domain (*.wikipedia.org, for example). When set, a login is done on each WMF domain so you are logged in on all ~800 content wikis. Since *.wikimedia.org is not possible, we explicitly include meta, commons, incubator, and wikispecies.

Basic authentication

If you need to supply basic auth credentials, pass a hashref of data as described by LWP::UserAgent:

$bot->login({
    username    => $username,
    password    => $password,
    basic_auth  => {    netloc  => "private.wiki.com:80",
                        realm   => "Authentication Realm",
                        uname   => "Basic auth username",
                        pass    => "password",
                    }
}) or die "Couldn't log in";

Bot passwords

MediaWiki::Bot doesn't yet support the more complicated (but more secure) oAuth login flow for bots. Instead, we support a simpler "bot password", which is a generated password connected to a (possibly-reduced) set of on-wiki privileges, and IP ranges from which it can be used.

To create one, visit Special:BotPasswords on the wiki. Enter a label for the password, then select the privileges you want to use with that password. This set should be as restricted as possible; most bots only edit existing pages. Keeping the set of privileges as restricted as possible limits the possible damage if the password were ever compromised.

Submit the form, and you'll be given a new "username" that looks like "AccountUsername@bot_password_label", and a generated bot password. To log in, provide those to MediaWiki::Bot verbatim.

References: API:Login, Logging in

logout

$bot->logout();

The logout method logs the bot out of the wiki. This invalidates all login cookies.

References: API:Logging out

Getting information about pages

diff

This allows retrieval of a diff from the API. The return is a scalar containing the HTML table of the diff. Options are passed as a hashref with keys:

  • title is the title to use. Provide either this or revid.
  • revid is any revid to diff from. If you also specified title, only title will be honoured.
  • oldid is an identifier to diff to. This can be a revid, or the special values 'cur', 'prev' or 'next'

References: API:Properties#revisions

get_history

my @hist = $bot->get_history($title);
my @hist = $bot->get_history($title, $additional_params);

Returns an array containing the history of the specified page $title.

The optional hash ref $additional_params can be used to tune the query by API parameters, such as 'rvlimit' to return only 'rvlimit' number of revisions (default is as many as possible, but may be limited per query) or 'rvdir' to set the chronological direction.

Example:

my @hist = $bot->get_history('Main Page', {'rvlimit' => 10, 'rvdir' => 'older'})

The array returned contains hashrefs with keys: revid, user, comment, minor, timestamp_date, and timestamp_time.

For backward compatibility, you can specify up to four parameters:

my @hist = $bot->get_history($title, $limit, $revid, $direction);

This form is deprecated, and will emit deprecation warnings.

References: Getting page history, API:Properties#revisions

get_history_step_by_step

my @hist = $bot->get_history_step_by_step($title);
my @hist = $bot->get_history_step_by_step($title, $additional_params);

Same as get_history(), but does not return the full history at once, but let's you loop through it.

The optional call-by-reference hash ref $additional_params can be used to loop through a page's full history by using the 'continue' param returned by the API.

Example:

my $ready;
my $filter_params = {};
while(!$ready){
    my @hist = $bot->get_history_step_by_step($page, $filter_params);
    if(@hist == 0 || !defined($filter_params->{'continue'})){
        $ready = 1;
    }
    # do something with @hist
}

References: Getting page history, API:Properties#revisions

what_links_here

Returns an array containing a list of all pages linking to $page.

Additional optional parameters are:

  • One of: all (default), redirects, or nonredirects.
  • A namespace number to search (pass an arrayref to search in multiple namespaces)
  • An "Options hashref".

A typical query:

my @links = $bot->what_links_here("Meta:Sandbox",
    undef, 1,
    { hook=>\&mysub }
);
sub mysub{
    my ($res) = @_;
    foreach my $hash (@$res) {
        my $title = $hash->{'title'};
        my $is_redir = $hash->{'redirect'};
        print "Redirect: $title\n" if $is_redir;
        print "Page: $title\n" unless $is_redir;
    }
}

Transclusions are no longer handled by what_links_here() - use "list_transclusions" instead.

References: Listing incoming links, API:Backlinks

get_id

Returns the id of the specified $page_title. Returns undef if page does not exist.

my $pageid = $bot->get_id("Main Page");
die "Page doesn't exist\n" if !defined($pageid);

Revisions: API:Properties#info

get_pages

Returns the text of the specified pages in a hashref. Content of undef means page does not exist. Also handles redirects or article names that use namespace aliases.

my @pages = ('Page 1', 'Page 2', 'Page 3');
my $thing = $bot->get_pages(\@pages);
foreach my $page (keys %$thing) {
    my $text = $thing->{$page};
    print "$text\n" if defined($text);
}

References: Fetching page text, API:Properties#revisions

prefixindex

This returns an array of hashrefs containing page titles that start with the given $prefix. The hashref has keys 'title' and 'redirect' (present if the page is a redirect, not present otherwise).

Additional parameters are:

  • One of all, redirects, or nonredirects

  • A single namespace number (unlike "linksearch" etc, which can accept an arrayref of numbers).

  • $options_hashref as described in "Options hashref".

    my @prefix_pages = $bot->prefixindex("User:Mike.lifeguard");

    Or, the more efficient equivalent

    my @prefix_pages = $bot->prefixindex("Mike.lifeguard", 2); foreach my $hashref (@pages) { my $title = $hashref->{'title'}; if $hashref->{'redirect'} { print "$title is a redirect\n"; } else { print "$title\n is not a redirect\n"; } }

References: API:Allpages

get_protection

Returns data on page protection as a array of up to two hashrefs. Each hashref has a type, level, and expiry. Levels are 'sysop' and 'autoconfirmed'; types are 'move' and 'edit'; expiry is a timestamp. Additionally, the key 'cascade' will exist if cascading protection is used.

my $page = 'Main Page';
$bot->edit({
    page    => $page,
    text    => rand(),
    summary => 'test',
}) unless $bot->get_protection($page);

You can also pass an arrayref of page titles to do bulk queries:

my @pages = ('Main Page', 'User:Mike.lifeguard', 'Project:Sandbox');
my $answer = $bot->get_protection(\@pages);
foreach my $title (keys %$answer) {
    my $protected = $answer->{$title};
    print "$title is protected\n" if $protected;
    print "$title is unprotected\n" unless $protected;
}

References: API:Properties#info

get_last

Returns the revid of the last revision to $page not made by $user. undef is returned if no result was found, as would be the case if the page is deleted.

my $revid = $bot->get_last('User:Mike.lifeguard/sandbox', 'Mike.lifeguard');
if defined($revid) {
    print "Reverting to $revid\n";
    $bot->revert('User:Mike.lifeguard', $revid, 'rvv');
}

References: API:Properties#revisions

recent_edit_to_page

 my ($timestamp, $user) = $bot->recent_edit_to_page($title);

Returns timestamp and username for most recent (top) edit to $page.

References: API:Properties#revisions

get_users

my @recent_editors = $bot->get_users($title, $limit, $revid, $direction);

Gets the most recent editors to $page, up to $limit, starting from $revision and going in $direction.

References: API:Properties#revisions

get_text

Returns the wikitext of the specified $page_title. The first parameter $page_title is the only required one.

The second parameter is a hashref with the following independent optional keys:

  • rvstartid - if defined, this function returns the text of that revision, otherwise the newest revision will be used.
  • rvsection - if defined, returns the text of that section. Otherwise the whole page text will be returned.
  • pageid - this is an output parameter and can be used to fetch the id of a page without the need of calling "get_id" additionally. Note that the value of this param is ignored and it will be overwritten by this function.
  • rv... - any param starting with 'rv' will be forwarded to the api call.

A blank page will return wikitext of "" (which evaluates to false in Perl, but is defined); a nonexistent page will return undef (which also evaluates to false in Perl, but is obviously undefined). You can distinguish between blank and nonexistent pages by using defined:

# simple example
my $wikitext = $bot->get_text('Page title');
print "Wikitext: $wikitext\n" if defined $wikitext;
# advanced example
my $options = {'revid'=>123456, 'section_number'=>2};
$wikitext = $bot->get_text('Page title', $options);
die "error, see API error message\n" unless defined $options->{'pageid'};
warn "page doesn't exist\n" if $options->{'pageid'} == MediaWiki::Bot::PAGE_NONEXISTENT;
print "Wikitext: $wikitext\n" if defined $wikitext;

References: Fetching page text, API:Properties#revisions

For backward-compatibility the params revid and section_number may also be given as scalar parameters:

my $wikitext = $bot->get_text('Page title', 123456, 2);
print "Wikitext: $wikitext\n" if defined $wikitext;

This form is deprecated, and will emit deprecation warnings.

is_protected

This is a synonym for "get_protection", which should be used in preference.

This method is deprecated and will emit deprecation warnings.

Modifying pages

edit

my $text = $bot->get_text('My page');
$text .= "\n\n* More text\n";
$bot->edit({
    page    => 'My page',
    text    => $text,
    summary => 'Adding new content',
    section => 'new',
});

This method edits a wiki page, and takes a hashref of data with keys:

  • page - the page title to edit
  • text - the page text to write
  • summary - an edit summary
  • minor - whether to mark the edit as minor or not (boolean)
  • bot - whether to mark the edit as a bot edit (boolean)
  • assertion - usually 'bot', but see http://mediawiki.org/wiki/Extension:AssertEdit.
  • section - edit a single section (identified by number) instead of the whole page

An MD5 hash is sent to guard against data corruption while in transit.

You can also call this as:

$bot->edit($page, $text, $summary, $is_minor, $assert, $markasbot);

This form is deprecated and will emit deprecation warnings.

CAPTCHAs

If a CAPTCHA is encountered, the call to edit will return false, with the error code set to ERR_CAPTCHA and the details informing you that solving a CAPTCHA is required for this action. The information you need to actually solve the captcha (for example the URL for the image) is given in $bot->{error}->{captcha} as a hash reference. You will want to grab the keys 'url' (a relative URL to the image) and 'id' (the ID of the CAPTCHA). Once you have solved the CAPTCHA (presumably by interacting with a human), retry the edit, adding captcha_id and captcha_solution parameters:

my $edit = {page => 'Main Page', text => 'got your nose'};
my $edit_status = $bot->edit($edit);
if (not $edit_status) {
    if ($bot->{error}{code} == ERR_CAPTCHA) {
        my @captcha_uri = split /\Q?/, $bot->{error}{captcha}{url}, 2;
        my $image = URI->new(sprintf '%s://%s%s?%s' =>
            $bot->{protocol}, $bot->{host}, $captcha_uri[0], $captcha_uri[1],
        );
        require Term::ReadLine;
        my $term = Term::ReadLine->new('Solve the captcha');
        $term->ornaments(0);
        my $answer = $term->readline("Please solve $image and type the answer: ");
        # Add new CAPTCHA params to the edit we're attempting
        $edit->{captcha_id} = $bot->{error}{captcha}{id};
        $edit->{captcha_solution} = $answer;
        $edit_status = $bot->edit($edit);
    }
}

References: Editing pages, API:Edit, API:Tokens

move

$bot->move($from_title, $to_title, $reason, $options_hashref);

This moves a wiki page.

If you wish to specify more options (like whether to suppress creation of a redirect), use $options_hashref, which has keys:

  • movetalk specifies whether to attempt to the talk page.

  • noredirect specifies whether to suppress creation of a redirect.

  • movesubpages specifies whether to move subpages, if applicable.

  • watch and unwatch add or remove the page and the redirect from your watchlist.

  • ignorewarnings ignores warnings.

    my @pages = ("Humor", "Rumor"); foreach my $page (@pages) { my $to = $page;

Read the original on github.com ↗