The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Web::Mention - Implementation of the IndieWeb Webmention protocol

SYNOPSIS

use Web::Mention;
use Try::Tiny;
use v5.10;

# Building a webmention from an incoming web request:

my $wm;
try {
    # $request can be any object that provides a 'param' method, such as
    # Catalyst::Request or Mojo::Message::Request.
    $wm = Web::Mention->new_from_request ( $request )
}
catch {
    say "Oops, this wasn't a webmention at all: $_";
};

if ( $wm && $wm->is_verified ) {
    my $source = $wm->original_source;
    my $target = $wm->target;
    my $author = $wm->author;

    my $name;
    if ( $author ) {
        $name = $author->name;
    }
    else {
        $name = $wm->source->host;
    }

    if ( $wm->is_like ) {
        say "Hooray, $name likes $target!";
    }
    elsif ( $wm->is_repost ) {
        say "Gadzooks, over at $source, $name reposted $target!";
    }
    elsif ( $wm->is_reply ) {
        say "Hmm, over at $source, $name said this about $target:";
        say $wm->content;
    }
    else {
        say "I'll be darned, $name mentioned $target at $source!";
    }
}
else {
   say "This webmention doesn't actually mention its target URL, "
       . "so it is not verified.";
}

# Manually buidling and sending a webmention:

$wm = Web::Mention->new(
   source => $url_of_the_thing_that_got_mentioned,
   target => $url_of_the_thing_that_did_the_mentioning,
);

my $success = $wm->send;
if ( $success ) {
    say "Webmention sent successfully!";
}
else {
    say "The webmention wasn't sent successfully.";
    say "Here's the response we got back..."
    say $wm->response->as_string;
}

# Batch-sending a bunch of webmentions based on some published HTML

my @wms = Web::Mention->new_from_html(
   source => $url_of_a_web_page_i_just_published,
   html   => $relevant_html_content_of_that_web_page,
)

for my $wm ( @wms ) {
   my $success = $wm->send;
}

DESCRIPTION

This class implements the Webmention protocol, as defined by the W3C and the IndieWeb community. (See this article by Chris Aldrich for an excellent high-level summary of Webmention and its applications.)

An object of this class represents a single webmention, with target and source URLs. It can verify itself, determining whether or not the document found at the source URL does indeed mention the target URL.

It can also use IndieWeb algorithms to attempt identification of the source document's author, and to provide a short summary of that document's content, using Microformats2 metadata when available.

METHODS

Class Methods

new

$wm = Web::Mention->new(
   source => $source_url,
   target => $target_url,
);

Basic constructor. The source and target URLs are both required arguments. Either one can either be a URI object, or a valid URL string.

Per the Webmention protocol, the source URL represents the location of the document that made the mention described here, and target describes the location of the document that got mentioned. The two arguments cannot refer to the same URL (disregarding the #fragment part of either, if present).

new_from_html

@wms = Web::Mention->new_from_html(
   source => $source_url,
   html   => $html,
);

Convenience batch-construtor that returns a (possibly empty) list of Web::Mention objects based on the single source URL (or URI object) that you pass in, as well as a string containing HTML from which we can extract zero or more target URLs. These extracted URLs include the href attribute value of every <a> tag in the provided HTML.

Note that (as with all this class's constructors) this method won't proceed to actually send the generated webmentions; that step remains yours to take. (See "send".)

new_from_json

$wm = Web::Mention->new_from_json( $json );

Returns a new webmention based on the JSON output of "as_json".

new_from_request

$wm = Web::Mention->new_from_request( $request_object );

Convenience constructor that looks into the given web-request object for source and target parameters, and attempts to build a new Web::Mention object out of them.

The object must provide a param( $param_name ) method that returns the value of the named HTTP parameter. So it could be a Catalyst::Request object or a Mojo::Message::Request object, for example.

Throws an exception if the given argument doesn't meet this requirement, or if it does but does not define both required HTTP parameters.

content_truncation_marker

Web::Mention->content_truncation_marker( $new_truncation_marker )

The text that the content method will append to text that it has truncated, if it did truncate it. (See "content".)

Defaults to ....

max_content_length

Web::Mention->max_content_length( $new_max_length )

Gets or sets the maximum length, in characters, of the content displayed by that object method prior to truncation. (See "content".)

Defaults to 280.

Object Methods

as_json

$json = $wm->as_json;

Returns a JSON representation of the webmention.

See "SERIALIZATION", below, for more information.

author

$author = $wm->author;

A Web::Mention::Author object representing the author of this webmention's source document. You can get information about the author through its name, url, and photo methods.

If the webmention's author is unknown or unset, then this method returns a Web::Mention::Author object with all its fields set to undef.

content

$content = $wm->content;

Returns a string containing this object's best determination of this webmention's display-ready content, based on a number of factors.

If the source document uses Microformats2 metadata and contains an h-entry MF2 item, then returned content may come from a variety of its constituent properties, according to the IndieWeb comment-display algorithm.

If not, then it returns the content of the source document's <title> element, with any further HTML stripped away.

In any case, the string will get truncated if it's too long. See "max_content_length" and "content_truncation_marker".

endpoint

my $uri = $wm->endpoint;

Attempts to determine the webmention endpoint URL of this webmention's target. On success, returns a URI object. On failure, returns undef.

(If the endpoint is set to localhost or a loopback IP, will return undef and also emit a warning, because that's terribly rude behavior on the target's part.)

is_tested

$bool = $wm->is_tested;

Returns 1 if this object's "verify" method has been called at least once, regardless of the results of that call. Returns 0 otherwise.

is_verified

$bool = $wm->is_verified;

Returns 1 if the webmention's source document actually does seem to mention the target URL. Otherwise returns 0.

The first time this is called on a given webmention object, it will try to fetch the source document at its designated URL by way of the "verify" method.

original_source

$original_url = $wm->original_source;

If the document fetched from the source URL seems to point at yet another URL as its original source, then this returns that URL. If not, this has the same return value as "source".

(It makes this determination based on the possible presence a u-url property in an h-entry found within the source document.)

response

my $response = $wm->response;

Returns the HTTP::Response object representing the response received by this webmention instance during its most recent attempt to send itself.

Returns undef if this webmention instance hasn't tried to send itself.

rsvp_type

my $rsvp = $wm->rsvp_type;

If this webmention is of type rsvp (see "type", below), then this method returns the type of RSVP represented. It will be one of:

  • yes

  • no

  • maybe

  • interested

Otherwise, returns undef.

send

my $bool = $wm->send;

Attempts to send an HTTP-request representation of this webmention to its target's designated webmention endpoint. This involves querying the target URL to discover said endpoint's URL (via the endpoint object method), and then sending the actual webmention request via HTTP to that endpoint.

If that whole process goes through successfully and the endpoint returns a success response (meaning that it has acknowledged the webmention, and most likely queued it for later processing), then this method returns true. Otherwise, it returns false.

To determine why a webmention did not send itself successfully, consult the value of response. If it is defined, then you can call HTTP::Response methods (such as code or message) to learn more about the problem. Otherwise, if response is not defined, then the target URL did not advertise a Webmention endpoint.

source

$source_url = $wm->source;

Returns the webmention's source URL, as a URI object.

source_html

$html = $wm->source_html;

The HTML of the document fetched from the source URL. If nothing got fetched successfully, returns undef.

source_mf2_document

$mf2_doc = $wm->source_mf2_document;

The Web::Microformats2::Document object that resulted from parsing the source document for Microformats2 metadata. If no such result, returns undef.

target

$target_url = $wm->target;

Returns the webmention's target URL, as a URI object.

time_published

$published_dt = $wm->time_published;

If the document fetched from the source URL explicitly declares a publication time via microformats, then this will return an appropriate DateTime object.

If not (or if the declared time seems to be invalid), then this will instead have the same return value as "time_received".

(It makes this determination based on the possible presence a dt-published property in an h-entry found within the source document.)

time_received

$received_dt = $wm->time_received;

A DateTime object corresponding to this object's creation time.

time_verified

$verified_dt = $wm->time_verified;

If this webmention has been verified, then this will return a DateTime object corresponding to the time of verification. (Otherwise, returns undef.)

title

my $title = $wm->title;

Returns a string containing this object's best determination of the display-ready title of this webmention's source document, considered separately from its content. (You can get its more complete content via the "content" method.

If the source document uses Microformats2 metadata and contains an h-entry MF2 item, and that item has a name property, then this method will return the text content of that name property.

If not, then it will return the content of the source document's <title> element, with any further HTML stripped away.

In any case, the string will get truncated if it's too long. See "max_content_length" and "content_truncation_marker".

Note that in some circumstances, the title and content methods might return identical values. (If, for example, the source document defines an entry with an explicit name property and no summary or content properties.)

type

$type = $wm->type;

The type of webmention this is. One of:

  • mention (default)

  • reply

  • like

  • repost

  • quotation

  • rsvp

This list is based on the W3C Post Type Discovery document (https://www.w3.org/TR/post-type-discovery/#response-algorithm), and adds a "quotation" type.

verify

my $is_verified = $wm->verify

This verifies the webmention, confirming that the content located at the source URL contains the target URL. Returns 1 if so, and 0 otherwise. Will also return 0 if it cannot fetch the content at all, after one try.

Sets is_tested to 1 as a side-effect.

See also "is_verified".

SERIALIZATION

To serialize a Web::Mention object, use "as_json", which returns a JSON string that you can store in any way you wish. You can later "inflate" it into a Web::Mention object through the "new_from_json" class method.

Note that a verified webmention might serialize to a significantly larger JSON string than an unverified one: it might include a complete copy of the source document, its parsed microformats (if any), author information, and various other metadata. Unverified webmentions, on the other hand, will likely contain little data other that their source and target URLs.

This is all normal; verified webmentions just have more luggage.

NOTES AND BUGS

This software is beta; its interface continues to develop and remains subject to change, but not without some effort at supporting its current API.

This library does not, at this time, support the proposed "Vouch" anti-spam extension for Webmention.

SUPPORT

To file issues or submit pull requests, please see this module's repository on GitHub.

The author also welcomes any direct questions about this module via email.

AUTHOR

Jason McIntosh (jmac@jmac.org)

CONTRIBUTORS

  • Mohammad S Anwar (mohammad.anwar@yahoo.com)

  • Yanick Champoux

  • Tomaž Šolc (tomaz.solc@tablix.org)

COPYRIGHT AND LICENSE

This software is Copyright (c) 2018-2020 by Jason McIntosh.

This is free software, licensed under:

The MIT (X11) License

A PERSONAL REQUEST

My ability to share and maintain free, open-source software like this depends upon my living in a society that allows me the free time and personal liberty to create work benefiting people other than just myself or my immediate family. I recognize that I got a head start on this due to an accident of birth, and I strive to convert some of my unclaimed time and attention into work that, I hope, gives back to society in some small way.

Worryingly, I find myself today living in a country experiencing a profound and unwelcome political upheaval, with its already flawed democracy under grave threat from powerful authoritarian elements. These powers wish to undermine this society, remolding it according to their deeply cynical and strictly zero-sum philosophies, where nobody can gain without someone else losing.

Free and open-source software has no place in such a world. As such, these autocrats' further ascension would have a deleterious effect on my ability to continue working for the public good.

Therefore, if you would like to financially support my work, I would ask you to consider a donation to one of the following causes. It would mean a lot to me if you did. (You can tell me about it if you'd like to, but you don't have to.)