MediaWiki::Bot(3pm)

๐Ÿฆ‹ MediaWiki::Bot ๐Ÿ’–

okay so hi bestie!! this is the manual page for MediaWiki::Bot(3pm), which lives in the User Contributed Perl Documentation section. it's a whole framework for writing bots that talk to MediaWiki wikis, and honestly? it's giving powerful. let's get into it ๐Ÿซถ


โœจ 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');

like, that's it. that's the whole vibe. a bot that logs in, finds the last revision by someone, and reverts to it. iconic โœจ


๐Ÿ’œ DESCRIPTION ๐Ÿ’œ

MediaWiki::Bot is a framework you use to write bots that interface with the MediaWiki API (http://en.wikipedia.org/w/api.php). no cap, it handles a lot of the annoying stuff so you don't have to ๐Ÿ™ˆ


๐Ÿ‘‘ METHODS ๐Ÿ‘‘

๐ŸŒˆ new

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

calling MediaWiki::Bot->new() creates a brand-new MediaWiki::Bot object, bestie. the only parameter is a hashref, and these are its keys:

  • agent โ€” sets a custom useragent. but lowkey, use operator instead, because that's all we need to do the right thing for you ๐Ÿ’…. if you really want to do it yourself, go read https://meta.wikimedia.org/wiki/User-agent_policy for guidance on what info must be included.
  • assert โ€” sets a parameter for the AssertEdit extension (commonly 'bot').

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

  • operator โ€” lets the bot send you a message when it fails an assert. this is also the recommended way to customize the user agent string, which the Wikimedia Foundation requires. if you omit this, a warning gets emitted ๐Ÿ‘€
  • maxlag โ€” lets you set the maxlag parameter (default is the recommended 5s).

please, please refer to the MediaWiki documentation before you change this from the default. fr fr.

  • protocol โ€” lets you specify 'http' or 'https' (default is 'http')
  • host โ€” sets the domain name of the wiki to connect to
  • path โ€” sets the path to api.php (with no leading or trailing slash)
  • login_data โ€” a hashref of credentials to pass to login.
  • debug โ€” whether to provide debug output.

1 provides only error messages; 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 โ€” it will never do auto-login or autoconfiguration, and it emits deprecation warnings. so likeโ€ฆ don't, bestie ๐Ÿ˜ญ

for further reading:


๐Ÿฆ‹ set_wiki

set what wiki to use, bestie. the parameter is a hashref with these keys:

  • host โ€” the domain name
  • path โ€” the part of the path before api.php (usually 'w')
  • protocol โ€” either 'http' or 'https'.

if you don't set any parameter, its previous value is used. and if it has never been set, the default settings are 'http', 'en.wikipedia.org' and 'w'. defaults are slaying โœจ

for example:

$bot->set_wiki({
    protocol    => 'https',
    host        => 'secure.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 it will emit deprecation warnings. noted, moving on ๐Ÿ’…

๐Ÿ“ login

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

it logs the user $username in, optionally using $password. first, an attempt is made to use cookies to log in ๐Ÿช. if that fails, an attempt is made to use the password you provided to log in, if any. if the login was successful, it returns true; false otherwise.

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

once you're logged in, it attempts 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 it as

$bot->login($username, $password);
๐Ÿšจ this form is deprecated, and it 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 it's 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're logged in on all ~800 content wikis. since *.wikimedia.org isn't possible, we explicitly include meta, commons, incubator, and wikispecies. the way that this covers basically everything?? obsessed ๐ŸŒˆ

๐Ÿ” 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. no edits, no vibes, just copy it exactly ๐Ÿ’…

references: API:Login https://www.mediawiki.org/wiki/API:Login, Logging in https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Logging-in


๐ŸŒ™ logout

$bot->logout();

the logout method logs the bot out of the wiki. this invalidates all login cookies. bye bestie ๐Ÿ‘‹๐Ÿช

references: API:Logging out https://www.mediawiki.org/wiki/API:Logout


๐ŸŒŸ 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 these 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. safety first, always ๐Ÿ›ก๏ธโœจ

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 https://en.wikipedia.org/wiki/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'll want to grab the keys 'url' (a relative URL to the image) and 'id' (the ID of the CAPTCHA). once you've 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;
        $status = $bot->edit($edit);
    }
}

references: Editing pages https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Editing-pages, API:Edit https://www.mediawiki.org/wiki/API:Edit, API:Tokens https://www.mediawiki.org/wiki/API:Tokens


๐Ÿงš move

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

this moves a wiki page. relocation era ๐Ÿ’…

if you wish to specify more options (like whether to suppress creation of a redirect), use $options_hashref, which has these 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 โ€” these add or remove the page and the redirect from your watchlist. both of them. yes both. ๐Ÿ’…
  • ignorewarnings โ€” ignores warnings. that's literally the whole job. iconic behaviour honestly.

so like, watch it happen:

my @pages = ("Humor", "Rumor");
foreach my $page (@pages) {
    my $to = $page;
    $to =~ s/or$/our/;
    $bot->move($page, $to, "silly 'merricans");
}

References: API:Move โ€” https://www.mediawiki.org/wiki/API:Move


get_history

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

bestie. this hands you back an array with the history of the page you asked for, $title. that's it, that's the vibe. ๐Ÿฆ‹

the optional hash ref $additional_params lets you fine-tune the query with API parameters โ€” think 'rvlimit' if you only want that many revisions back (default is as many as possible, but ngl the query itself might cap you), or 'rvdir' to control which way time flows. โณ

example, pls observe:

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

the array you get is full of hashrefs, and those hashrefs have the keys: revid, user, comment, minor, timestamp_date, and timestamp_time. no cap. ๐Ÿ’–

and for backward compatibility you can still pass up to four parameters, old-school style:

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

References: Getting page history โ€” https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Getting-page-history, API:Properties#revisions โ€” https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv


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 energy as get_history(), BUT โ€” and this is the moment โ€” it does not dump the entire history on you at once. instead it lets you loop through it piece by piece. very demure, very mindful. ๐Ÿงš

the optional call-by-reference hash ref $additional_params is your tool for walking a page's whole history, using the 'continue' param that the API hands back. ๐Ÿ”

example, because yes:

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 โ€” https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Getting-page-history, API:Properties#revisions โ€” https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv


get_text

returns the wikitext of the $page_title you asked for. only the first parameter $page_title is actually required โ€” everything else is a bonus. โœจ

the second parameter is a hashref, and these keys are all independent + all optional:

  • "rvstartid" โ€” if it's defined, this function gives you the text of that revision. if it's not, you get the newest revision. that's the default, periodt.
  • "rvsection" โ€” if it's defined, you get the text of that section. otherwise you get the whole page text. all of it. ๐ŸŽ€
  • "pageid" โ€” heads up, this one is an output parameter. you can use it to grab a page's id without having to call get_id separately. note that whatever value you put in here gets ignored and overwritten by this function. it's giving "i'm just here to be filled in". ๐Ÿ™ˆ
  • "rv..." โ€” any param starting with rv gets forwarded straight to the api call.

okay important distinction time, write this down ๐Ÿ“: a blank page returns wikitext of "" โ€” which evaluates to false in Perl but is still defined. a nonexistent page returns undef โ€” which also evaluates to false in Perl, but is obviously undefined. so if you need to tell them apart, use 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 โ€” https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Fetching-page-text, API:Properties#revisions โ€” https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv

and for backward-compatibility, the params revid and section_number can also be passed as plain scalar parameters, like this:

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

get_id

returns the id of the $page_title you asked for. if the page doesn't exist? you get undef. that's the whole deal. ๐Ÿ’€

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

Revisions: API:Properties#info โ€” https://www.mediawiki.org/wiki/API:Properties#info_.2F_in


get_pages

gives you the text of all the pages you listed, bundled up in a hashref. if the content is undef, that page doesn't exist. it also handles redirects, and article names that use namespace aliases. multi-tasking queen. ๐Ÿ‘‘

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 โ€” https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Fetching-page-text, API:Properties#revisions โ€” https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv


get_image

$buffer = $bot->get_image('File:Foo.jpg', { width=>256, height=>256 });

downloads an image off a wiki. this one is derived from a similar function in MediaWiki::API, except this version lets you scale the image down by passing a hashref with height & width parameters. glow-up on demand. ๐ŸŒธ

it returns raw data in the original format. you can just spew it straight into a file, or process it directly with a library like Imager:

use File::Slurp qw(write_file);
my $img_data = $bot->get_image('File:Foo.jpg');
write_file( 'Foo.jpg', {binmode => ':raw'}, \$img_data );

images get scaled proportionally. (height/width) stays constant โ€” except for rounding errors, obviously, math is math. ๐Ÿงฎ

the height and width parameters describe the maximum dimensions. a 400x200 image will never be scaled up to greater dimensions. and yeah, you could scale it yourself โ€” but having the wiki do it is just lazy & selfish. their words, not mine. ๐Ÿ˜ญ

References: API:Properties#imageinfo โ€” https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii


revert

reverts the $page_title you named back to $revid, with an edit summary of $summary. if you skip $summary, a default edit summary gets used instead. ๐Ÿซถ

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');

References: API:Edit โ€” https://www.mediawiki.org/wiki/API:Edit


undo

$bot->undo($title, $revid, $summary, $after);

reverts the specified $revid, with an edit summary of $summary, using the undo function. ๐Ÿช„

here's the twist: to undo all revisions from $revid up to but not including this one, set $after to another revid. if you don't set it, you're just undoing the one single revision ($revid). one and done.

References: API:Edit โ€” https://www.mediawiki.org/wiki/API:Edit


get_last

returns the revid of the last revision to $page that was not made by $user. if nothing turns up โ€” like, say, the page is deleted โ€” you get undef. ๐Ÿคท

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 โ€” https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv


update_rc

โš ๏ธ this method is deprecated, and it will emit deprecation warnings. swap your update_rc() calls out for the newer recentchanges(), which returns all the available data, including rcid. consider this your official notice bestie.

returns an array with the $limit most recent changes to the wiki's main namespace. the array is full of hashrefs with the keys title, revid, old_revid, and timestamp.

my @rc = $bot->update_rc(5);
foreach my $hashref (@rc) {
    my $title = $hash->{'title'};
    print "$title\n";
}

the Options hashref is also available, if that's your thing:

# Use a callback for incremental processing:
my $options = { hook => \&mysub, };
$bot->update_rc($options);
sub mysub {
    my ($res) = @_;
    foreach my $hashref (@$res) {
        my $page = $hashref->{'title'};
        print "$page\n";
    }
}

recentchanges($wiki_hashref, $options_hashref)

returns an array of hashrefs stuffed with recentchanges data. ๐Ÿ“‹

the first parameter is a hashref, and here's what it can hold:

  • ns โ€” the namespace number. or an arrayref of numbers if you want several. default is the main namespace.
  • limit โ€” how many rows to fetch. default is 50.
  • user โ€” only list changes made by this user.
  • show โ€” itself a hashref, where the key is a category and the value is a boolean. true means the category gets included, false means it's excluded. the categories are kinds of edits: minor, bot, anon, redirect, patrolled. see "rcshow" at http://www.mediawiki.org/wiki/API:Recentchanges#Parameters.

an Options hashref can ride along as the second parameter:

my @rc = $bot->recentchanges({ ns => 4, limit => 100 });
foreach my $hashref (@rc) {
    print $hashref->{title} . "\n";
}

# Or, use a callback for incremental processing:
$bot->recentchanges({ ns => [0,1], limit => 500 }, { hook => \&mysub });
sub mysub {
    my ($res) = @_;
    foreach my $hashref (@$res) {
        my $page = $hashref->{title};
        print "$page\n";
    }
}

the hashref you get back might contain the following keys:

  • ns โ€” the namespace number
  • revid
  • old_revid
  • timestamp
  • rcid โ€” can be used with patrol
  • pageid
  • type โ€” one of edit, new, log (there may be others ๐Ÿ‘€)
  • title

for backwards compatibility, the previous method signature is still supported:

$bot->recentchanges($ns, $limit, $options_hashref);

References: API:Recentchanges โ€” https://www.mediawiki.org/wiki/API:Recentchanges


returns an array with a list of every page linking to $page. ๐Ÿ”—

extra optional parameters, since we love options:

  • 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;
    }
}
okay important glow-up alert ๐Ÿ’… Transclusions are no longer handled by what_links_here() โ€” use list_transclusions instead. The old way is giving legacy, bestie.

References: Listing incoming links https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Listing-incoming-links, API:Backlinks https://www.mediawiki.org/wiki/API:Backlinks


๐ŸŒธ list_transclusions ๐ŸŒธ

Returns an array holding a list of all the pages that transclude $page. Slay.

The other params are giving:

  • One of: all (that's the default, bestie), redirects, or nonredirects
  • A namespace number to search (pass an arrayref if you wanna search in multiple namespaces at once โœจ)
  • $options_hashref as described by MediaWiki::API:
    • Set max to limit how many queries get performed
    • Set hook to a subroutine reference to use a callback hook for incremental processing

Refer to the section on "linksearch" for examples, iykyk.

A typical query, looking cute:

$bot->list_transclusions("Template:Tlx", undef, 4, {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;
    }
}

References: Listing transclusions https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Listing-transclusions API:Embeddedin https://www.mediawiki.org/wiki/API:Embeddedin


๐ŸŽ€ get_pages_in_category ๐ŸŽ€

Returns an array holding the names of every page in the category you named (include the Category: prefix, don't forget it ๐Ÿ’–). Heads up: it does not recurse into sub-categories. One layer only, queen.

my @pages = $bot->get_pages_in_category('Category:People on stamps of Gabon');
print "The pages in Category:People on stamps of Gabon are:\n@pages\n";

The options hashref is as described in "Options hashref". Use { max => 0 } to get all the results โ€” all of them, no crumbs.

References: Listing category contents https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Listing-category-contents, API:Categorymembers https://www.mediawiki.org/wiki/API:Categorymembers


๐Ÿฆ‹ get_all_pages_in_category ๐Ÿฆ‹

my @pages = $bot->get_all_pages_in_category($category, $options_hashref);

Returns an array holding the names of every page in the specified category (include the Category: prefix), including sub-categories this time. She goes deep. The $options_hashref is described fully in "Options hashref".

References: Listing category contents https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Listing-category-contents, API:Categorymembers https://www.mediawiki.org/wiki/API:Categorymembers


โœจ get_all_categories โœจ

Returns an array holding the names of all the categories. Every single one.

my @categories = $bot->get_all_categories();
print "The categories are:\n@categories\n";

Use { max => 0 } to get all results. Lowkey important: the default number of categories returned is 10, and the maximum allowed is 500.

References: API:Allcategories https://www.mediawiki.org/wiki/API:Allcategories


๐Ÿ’… linksearch ๐Ÿ’…

Runs a linksearch on the $link you gave it and returns an array of anonymous hashes with the keys 'url' for the outbound URL and 'title' for the page the link is sitting on.

The additional params are:

  • A namespace number to search (pass an arrayref to search in multiple namespaces)
  • You can search by $protocol (http is the default, no cap)
  • $options_hashref is fully documented in "Options hashref":
    • Set max in $options to get more than one query's worth of results:
my $options = { max => 10, }; # I only want some results
my @links = $bot->linksearch("slashdot.org", 1, undef, $options);
foreach my $hash (@links) {
    my $url = $hash->{'url'};
    my $page = $hash->{'title'};
    print "$page: $url\n";
}
  • Set hook to a subroutine reference to use a callback hook for incremental processing:
my $options = { hook => \&mysub, }; # I want to do incremental processing
$bot->linksearch("slashdot.org", 1, undef, $options);
sub mysub {
    my ($res) = @_;
    foreach my $hashref (@$res) {
        my $url  = $hashref->{'url'};
        my $page = $hashref->{'title'};
        print "$page: $url\n";
    }
}

References: Finding external links https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Finding-external-links, API:Exturlusage https://www.mediawiki.org/wiki/API:Exturlusage


๐Ÿ”ฅ purge_page ๐Ÿ”ฅ

Purges the server cache of the $page you point at. Returns true on success, false on failure. Pass an array reference to purge multiple pages at once โ€” bulk mode, iconic.

If you really care (and honestly, main character energy), a true return value is the number of pages successfully purged. So you could check that it matches the number you wanted to purge โ€” maybe some pages don't exist, or you passed invalid titles, or you aren't allowed to purge the cache. Not every queen gets the crown ๐Ÿ‘‘.

my @to_purge = ('Main Page', 'A', 'B', 'C', 'Very unlikely to exist');
my $size = scalar @to_purge;

print "all-at-once:\n";
my $success = $bot->purge_page(\@to_purge);

if ($success == $size) {
    print "@to_purge: OK ($success/$size)\n";
}
else {
    my $missed = @to_purge - $success;
    print "We couldn't purge $missed pages (list was: "
        . join(', ', @to_purge)
        . ")\n";
}

# OR
print "\n\none-at-a-time:\n";
foreach my $page (@to_purge) {
    my $ok = $bot->purge_page($page);
    print "$page: $ok\n";
}

References: Purging the server cache https://github.com/MediaWiki-Bot/MediaWiki-Bot/wiki/Purging-the-server-cache, API:Purge https://www.mediawiki.org/wiki/API:Purge


๐Ÿ’œ get_namespace_names ๐Ÿ’œ

my %namespace_names = $bot->get_namespace_names();

Returns a hash linking the namespace id, like 1, to its named equivalent, like "Talk". It's giving dictionary but make it wiki.

References: API:Meta#siteinfo https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si


๐ŸŒˆ image_usage ๐ŸŒˆ

Gets a list of pages which include a certain $image. Pro tip fr fr: include the "File:" namespace prefix to avoid incurring an extra round-trip (which will also emit deprecation warnings ๐Ÿ˜ญ). Save yourself the drama.

The additional params are:

  • A namespace number to fetch results from (or an arrayref of multiple namespace numbers)
  • One of all, redirect, or nonredirects
  • $options is a hashref as described in the section for "linksearch"
my @pages = $bot->image_usage("File:Albert Einstein Head.jpg");

Or, make use of the "Options hashref" to do incremental processing, bestie:

$bot->image_usage("File:Albert Einstein Head.jpg",
    undef, undef,
    { hook=>\&mysub, max=>5 }
);
sub mysub {
    my $res = shift;
    foreach my $page (@$res) {
        my $title = $page->{'title'};
        print "$title\n";
    }
}

References: API:Imageusage https://www.mediawiki.org/wiki/API:Imageusage


๐Ÿ“ global_image_usage($image, $results, $filterlocal) ๐Ÿ“

Returns an array of hashrefs of data about pages which use the given image. Global girly.

my @data = $bot->global_image_usage('File:Albert Einstein Head.jpg');

The keys in each hashref are title, url, and wiki. $results is the maximum number of results that will be returned โ€” and no, it is not the maximum number of requests that will be sent, like "max" in the "Options hashref", don't get it twisted. The default is to attempt to fetch 500 (set it to 0 to get all results). $filterlocal will filter out local uses of the image.

References: Extension:GlobalUsage#API https://www.mediawiki.org/wiki/Extension:GlobalUsage#API


A backward-compatible call to "image_usage". You can provide only the image title.

This method is deprecated, and will emit deprecation warnings. She's retired, bestie, don't build your whole personality on her ๐Ÿ’€

๐ŸŒŸ is_blocked ๐ŸŒŸ

my $blocked = $bot->is_blocked('User:Mike.lifeguard');

Checks if a user is currently blocked. Simple. Clean. Vibes.

References: API:Blocks https://www.mediawiki.org/wiki/API:Blocks


๐Ÿ™ˆ test_blocked ๐Ÿ™ˆ

Retained for backwards compatibility. Use "is_blocked" for clarity โ€” that's the glow-up version.

This method is deprecated, and will emit deprecation warnings.

๐ŸŽ€ test_image_exists ๐ŸŽ€

Checks if an image exists at $page. Here's the tea:

  • "FILE_NONEXISTENT" (0) means "Nothing there"
  • "FILE_LOCAL" (1) means "Yes, an image exists locally"
  • "FILE_SHARED" (2) means "Yes, an image exists on Commons <http://commons.wikimedia.org>"
  • "FILE_PAGE_TEXT_ONLY" (3) means "No image exists, but there is text on the page"

If you pass in an arrayref of images, you'll get out an arrayref of results. Bulk queen behaviour ๐Ÿ’…

use MediaWiki::Bot::Constants;
my $exists = $bot->test_image_exists('File:Albert Einstein Head.jpg');
if ($exists == FILE_NONEXISTENT) {
    print "Doesn't exist\n";
}
elsif ($exists == FILE_LOCAL) {
    print "Exists locally\n";
}
elsif ($exists == FILE_SHARED) {
    print "Exists on Commons\n";
}
elsif ($exists == FILE_PAGE_TEXT_ONLY) {
    print "Page exists, but no image\n";
}

References: API:Properties#imageinfo https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii


๐ŸŒธ get_pages_in_namespace ๐ŸŒธ

$bot->get_pages_in_namespace($namespace, $limit, $options_hashref);

Returns an array holding the names of every page in the specified namespace. Important: the $namespace_id must be a number, not a namespace name. Numbers only, babe.

Setting $page_limit is optional, and it says how many items to retrieve at once. Setting this to 'max' is recommended, and this is the default if you omit it. If $page_limit is over 500, it will be rounded up to the next multiple of 500. If $page_limit is set higher than you are allowed to use, it will silently be reduced (she's just keeping you safe ๐Ÿซถ). Consider setting the key 'max' in the "Options hashref" to retrieve multiple sets of results:

# Gotta get 'em all!
my @pages = $bot->get_pages_in_namespace(6, 'max', { max => 0 });

References: API:Allpages https://www.mediawiki.org/wiki/API:Allpages


๐Ÿ’– count_contributions ๐Ÿ’–

my $count = $bot->count_contributions($user);

Uses the API to count $user's contributions. Numbers don't lie.

References: API:Users https://www.mediawiki.org/wiki/API:Users


โญ timed_count_contributions โญ

($timed_edits_count, $total_count) = $bot->timed_count_contributions($user, $days);

Uses the API to count $user's contributions in the last number of $days and the total number of the user's contributions (if needed). Two for the price of one, periodt.

Example: if you want to get user contribs for the last 30 and 365 days, plus the total number of edits, you'd write something like this:

my ($last30days, $total) = $bot->timed_count_contributions($user, 30);
my $last365days = $bot->timed_count_contributions($user, 365);

And yeah, you could also get the total number of edits by calling count_contributions separately, like this:

my $total = $bot->count_contributions($user);

and then use timed_count_contributions only in scalar context. But bestie, that's one more call to the server, which means more server load, and honestly you're excused from doing that โ€” because timed_count_contributions already hands you back an array with two parameters. ๐Ÿ’…

References: Extension:UserDailyContribs <https://www.mediawiki.org/wiki/Extension:UserDailyContribs>


๐Ÿ•ฐ๏ธ last_active

my $latest_timestamp = $bot->last_active($user);

Returns the last active time of $user in "YYYY-MM-DDTHH:MM:SSZ". โฐ

References: API:Usercontribs <https://www.mediawiki.org/wiki/API:Usercontribs>


โœจ recent_edit_to_page

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

Returns the timestamp and username for the most recent (top) edit to $page. ๐Ÿซถ

References: API:Properties#revisions <https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv>


๐Ÿ‘€ 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. Literally the stalker-ish-but-legal way to see who's been touching a page. ๐ŸŒธ

References: API:Properties#revisions <https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv>


๐Ÿšซ was_blocked

for ("Mike.lifeguard", "Jimbo Wales") {
    print "$_ was blocked\n" if $bot->was_blocked($_);
}

Returns whether $user has ever been blocked. Receipts, fr fr. ๐Ÿ’€

References: API:Logevents <https://www.mediawiki.org/wiki/API:Logevents>


๐Ÿง“ test_block_hist

Retained for backwards compatibility. Use "was_blocked" for clarity, bestie.

โš ๏ธ This method is deprecated, and will emit deprecation warnings.

๐Ÿช„ expandtemplates

my $expanded = $bot->expandtemplates($title, $wikitext);

Expands templates on $page, using $text if provided, otherwise loading the page text automatically. No cap, it just does the thing. โœจ

References: API:Parsing wikitext <https://www.mediawiki.org/wiki/API:Parsing_wikitext>


๐ŸŒˆ get_allusers

my @users = $bot->get_allusers($limit, $user_group, $options_hashref);

Returns an array of all users. Default $limit is 500. Optionally specify a $group (like 'sysop') to list that group only. The last optional parameter is an "Options hashref". ๐Ÿ‘‘

References: API:Allusers <https://www.mediawiki.org/wiki/API:Allusers>


๐ŸŒ db_to_domain

Converts a wiki/database name (enwiki) to the domain name (en.wikipedia.org).

my @wikis = ("enwiki", "kowiki", "bat-smgwiki", "nonexistent");
foreach my $wiki (@wikis) {
    my $domain = $bot->db_to_domain($wiki);
    next if !defined($domain);
    print "$wiki: $domain\n";
}

You can pass an arrayref to do bulk lookup:

my @wikis = ("enwiki", "kowiki", "bat-smgwiki", "nonexistent");
my $domains = $bot->db_to_domain(\@wikis);
foreach my $domain (@$domains) {
    next if !defined($domain);
    print "$domain\n";
}

References: Extension:SiteMatrix <https://www.mediawiki.org/wiki/Extension:SiteMatrix>


๐Ÿ” domain_to_db

my $db = $bot->domain_to_db($domain_name);

As you might expect, does the opposite of "domain_to_db": converts a domain name (meta.wikimedia.org) into a database/wiki name (metawiki). Yes bestie, the sentence is a little cursed, but that's the sentence. ๐Ÿ’…

References: Extension:SiteMatrix <https://www.mediawiki.org/wiki/Extension:SiteMatrix>


๐Ÿชž 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 <https://www.mediawiki.org/wiki/API:Properties#revisions_.2F_rv>


๐Ÿ”ค 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 <https://www.mediawiki.org/wiki/API:Allpages>


This is a simple search for your $search_term in page text. It returns an array of page titles matching. ๐ŸŽ€

Additional optional parameters are:

  • A namespace number to search in, or an arrayref of numbers (default is the main namespace)
  • $options_hashref is a hashref as described in "Options hashref":
my @pages = $bot->search("Mike.lifeguard", 2);
print "@pages\n";

Or, use a callback for incremental processing:

my @pages = $bot->search("Mike.lifeguard", 2, { hook => \&mysub });
sub mysub {
    my ($res) = @_;
    foreach my $hashref (@$res) {
        my $page = $hashref->{'title'};
        print "$page\n";
    }
}

References: API:Search <https://www.mediawiki.org/wiki/API:Search>


๐Ÿ“œ get_log

This fetches log entries, and returns results as an array of hashes. The first parameter is a hashref with keys:

  • type is the log type (block, delete...)
  • user is the user who performed the action. Do not include the User: prefix
  • target is the target of the action. Where an action was performed to a page, it is the page title. Where an action was performed to a user, it is User:$username.

The second is the familiar "Options hashref".

my $log = $bot->get_log({
        type => 'block',
        user => 'User:Mike.lifeguard',
    });
foreach my $entry (@$log) {
    my $user = $entry->{'title'};
    print "$user\n";
}
$bot->get_log({
        type => 'block',
        user => 'User:Mike.lifeguard',
    },
    { hook => \&mysub, max => 10 }
);
sub mysub {
    my ($res) = @_;
    foreach my $hashref (@$res) {
        my $title = $hashref->{'title'};
        print "$title\n";
    }
}

References: API:Logevents <https://www.mediawiki.org/wiki/API:Logevents>


๐ŸŒ is_g_blocked

my $is_globally_blocked = $bot->is_g_blocked('127.0.0.1');

Returns what IP/range block currently in place affects the IP/range. The return is a scalar of an IP/range if found (evaluates to true in boolean context); undef otherwise (evaluates false in boolean context). Pass in a single IP or CIDR range. ๐Ÿฆ‹

References: Extension:GlobalBlocking <https://www.mediawiki.org/wiki/Extension:GlobalBlocking/API>


๐Ÿ•ต๏ธ was_g_blocked

print "127.0.0.1 was globally blocked\n" if $bot->was_g_blocked('127.0.0.1');

Returns whether an IP/range was ever globally blocked. You should probably call this method only when your bot is operating on Meta โ€” this method will warn if not. ๐Ÿ‘€

References: API:Logevents <https://www.mediawiki.org/wiki/API:Logevents>


๐Ÿ”’ was_locked

my $was_locked = $bot->was_locked('Mike.lifeguard');

Returns whether a user was ever locked. You should probably call this method only when your bot is operating on Meta โ€” this method will warn if not. ๐Ÿ’€

References: API:Logevents <https://www.mediawiki.org/wiki/API:Logevents>


๐Ÿ›ก๏ธ get_protection

Returns data on page protection as an 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 <https://www.mediawiki.org/wiki/API:Properties#info_.2F_in>


๐Ÿงฌ is_protected

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

โš ๏ธ This method is deprecated, and will emit deprecation warnings.

๐Ÿšจ patrol

$bot->patrol($rcid);

Marks a page or revision identified by the $rcid as patrolled. To mark several RCIDs as patrolled, you may pass an arrayref of them. Returns false and sets "$bot->{error}" if the account cannot patrol. ๐Ÿ”ฅ

References: API:Patrol <https://www.mediawiki.org/wiki/API:Patrol>


๐Ÿ’Œ email

$bot->email($user, $subject, $body);

This allows you to send emails through the wiki. All 3 of $user (without the User: prefix), $subject and $body are required. If $user is an arrayref, this will send the same email (subject and body) to all users. ๐Ÿซถ

References: API:Email <https://www.mediawiki.org/wiki/API:Email>


โญ top_edits

Returns an array of the page titles where the $user is the latest editor. The second parameter is the familiar $options_hashref.

my @pages = $bot->top_edits("Mike.lifeguard", {max => 5});
foreach my $page (@pages) {
    $bot->rollback($page, "Mike.lifeguard");
}

Note that accessing the data with a callback happens before filtering the top edits is done. For that reason, you should use "contributions" if you need to use a callback. If you use a callback with top_edits(), you will not necessarily get top edits returned. It is only safe to use a callback if you check that it is a top edit:

$bot->top_edits("Mike.lifeguard", { hook => \&rv });
sub rv {
    my $data = shift;
    foreach my $page (@$data) {
        if (exists($page->{'top'})) {
            $bot->rollback($page->{'title'}, "Mike.lifeguard");
        }
    }
}

References: API:Usercontribs โ€” https://www.mediawiki.org/wiki/API:Usercontribs


contributions ๐Ÿ’œ

my @contribs = $bot->contributions($user, $namespace, $options, $from, $to);

okay so this one hands you back an array of hashrefs โ€” one lil data blob per contribution that user made โœจ

$namespace can be an arrayref of namespace numbers, no biggie. $options works exactly like it does in linksearch. And $from + $to are optional timestamps, bestie.

ISO 8601 date and time is the recommendation: 2001-01-15T14:56:00Z. Wanna see every possible format? Go peep https://www.mediawiki.org/wiki/Timestamp.

โš ๏ธ heads up: $from (that's ucend) has to come before $to (that's ucstart). Which is, like, the opposite of how direct API access does it. Not me getting confused by this. ๐Ÿ™ˆ

And ooh โ€” if you pass an arrayref of users, you get results for multiple users at once. Iconic.

References: API:Usercontribs โ€” https://www.mediawiki.org/wiki/API:Usercontribs


upload ๐ŸŽ€

$bot->upload({ data => $file_contents, summary => 'uploading file' });
$bot->upload({ file => $file_name,     title   => 'Target filename.png' });

Upload a file to the wiki, periodt ๐Ÿ’… You can specify the file two ways: either give the filename and it'll get read in for you, or just hand over the data directly. Pick your fighter.

References: API:Upload โ€” https://www.mediawiki.org/wiki/API:Upload


upload_from_url ๐ŸŒˆ

Upload a file straight from a URL to the wiki. Give it the URL, the new filename, and a summary. The summary and the new filename are optional โ€” lowkey chill of them.

$bot->upload_from_url({
    url => 'http://some.domain.ext/pic.png',
    title => 'Target_filename.png',
    summary => 'uploading new pic',
});

So here's the tea โ˜• โ€” if uploading from a URL is enabled on your target wiki, meaning $wgAllowCopyUploads is set to true in LocalSettings.php, and you've got the appropriate user rights, you can use this function to yeet files onto your wiki directly from a remote server. Slay.

References: API:Upload#Uploading_from_URL โ€” https://www.mediawiki.org/wiki/API:Upload#Uploading_from_URL


usergroups ๐Ÿ‘‘

Returns a list of the usergroups a user is in:

my @usergroups = $bot->usergroups('Mike.lifeguard');

References: API:Users โ€” https://www.mediawiki.org/wiki/API:Users


get_mw_version ๐Ÿฆ‹

Returns a hash ref with the MediaWiki version. That hashref contains the keys major, minor, patch, and string. Returns undef on errors โ€” sad girl hours. ๐Ÿ˜ญ

my $mw_version = $bot->get_mw_version;

# get version as string
my $mw_ver_as_string = $mw_version->{'major'} . '.' . $mw_version->{'minor'};
if(defined $mw_version->{'patch'}){
    $mw_ver_as_string .= '.' . $mw_version->{'patch'};
}

# or simply
my $mw_ver_as_string = $mw_version->{'string'};

References: API:Siteinfo โ€” https://www.mediawiki.org/wiki/API:Siteinfo


Options hashref ๐Ÿ’–

This bad boy gets passed straight through to the lower-level interface MediaWiki::API, and it's fully documented over there. So like, iykyk.

The hashref can have 3 keys:

max

Specifies the maximum number of queries to retrieve data from the wiki. This is independent of the size of each query (aka how many items each query returns). Set it to 0 to retrieve all the results. go off.

hook

Specifies a coderef to a hook function that can be used to process large lists as they come in. When you use this, your subroutine gets the raw data โ€” and that's noted in the cases where it's known to be significant, okay?

For example: when you're using a hook with top_edits(), you gotta check whether the edit is the top edit yourself. Your subroutine gets results as they come in, and before they're filtered. so no, it's not gonna do it for you bestie ๐Ÿซถ

skip_encoding

MediaWiki's API uses UTF-8, and any 8 bit character string parameters get encoded automatically by the API call. If your parameters are already in UTF-8, that'll be detected and the encoding will be skipped.

But if your parameters for some reason contain UTF-8 data and no UTF-8 flag is set โ€” i.e. you did not use the use utf8; pragma โ€” you should prevent re-encoding by passing an option skip_encoding => 1. Like so:

$category ="Cat\x{e9}gorie:moyen_fran\x{e7}ais"; # latin1 string
$bot->get_all_pages_in_category($category); # OK

$category = "Cat". pack("U", 0xe9)."gorie:moyen_fran".pack("U",0xe7)."ais"; # unicode string
$bot->get_all_pages_in_category($category); # OK

$category ="Cat\x{c3}\x{a9}gorie:moyen_fran\x{c3}\x{a7}ais"; # unicode data without utf-8 flag
# $bot->get_all_pages_in_category($category); # NOT OK
$bot->get_all_pages_in_category($category, { skip_encoding => 1 }); # OK
๐Ÿซ  If you need this, it probably means you're doing something wrong. Feel free to ask for help though โ€” we don't judge.

โœจ ERROR HANDLING โœจ

All functions will return undef in any handled error situation. Further error data is stored in $bot->{error}->{code} and $bot->{error}->{details}. cute, contained, organized ๐Ÿ’…

Error codes are provided as constants in MediaWiki::Bot::Constants, and can also be imported through this module:

use MediaWiki::Bot qw(:constants);

๐ŸŒ AVAILABILITY ๐ŸŒ

The project homepage is https://metacpan.org/module/MediaWiki::Bot.

The latest version of this module is available from the Comprehensive Perl Archive Network (CPAN). Visit http://www.perl.com/CPAN/ to find a CPAN site near you, or see https://metacpan.org/module/MediaWiki::Bot/.


๐Ÿ“ฆ SOURCE ๐Ÿ“ฆ

The development version is on github at https://github.com/MediaWiki-Bot/MediaWiki-Bot and may be cloned from git://github.com/MediaWiki-Bot/MediaWiki-Bot.git


๐Ÿ’€ BUGS AND LIMITATIONS ๐Ÿ’€

You can make new bug reports, and view existing ones, through the web interface at https://github.com/MediaWiki-Bot/MediaWiki-Bot/issues.

(go tell them everything. they wanna know. fr fr.)


๐Ÿ‘‘ AUTHORS ๐Ÿ‘‘

The legends themselves, in no particular order of slay:

  • Dan Collins <dcollins@cpan.org>
  • Mike.lifeguard <lifeguard@cpan.org>
  • Alex Rowe <alex.d.rowe@gmail.com>
  • Oleg Alexandrov <oleg.alexandrov@gmail.com>
  • jmax.code <jmax.code@gmail.com>
  • Stefan Petrea <stefan.petrea@gmail.com>
  • kc2aei <kc2aei@gmail.com>
  • bosborne@alum.mit.edu
  • Brian Obio <brianobio@gmail.com>
  • patch and bug report contributors

This software is Copyright (c) 2021 by the MediaWiki::Bot team <perlwikibot@googlegroups.com>.

This is free software, licensed under:

The GNU General Public License, Version 3, June 2007

okay bestie that's the whole page, we ate and left no crumbs ๐Ÿ“โœจ

Signing off in proper manpage fashion: this is perl v5.32.1, dated 2021-11-12, and you've been reading MediaWiki::Bot(3pm). ๐ŸŽ€ go write something iconic, ily, bye ๐Ÿ’–