Right off the bat, I read the following two articles to get a sense of how to do this so I won't go over too much of the same content but rather add some thoughts of my own about the process. I would highly recommend reading both of these.
Also, I feel like I muddled through this to get it to work. JSON and API requests I can understand. The how of webmentions I'm still a bit confused on but we will proceed nonetheless.
a simple way to notify any URL when you link to it from your site
Sounds simple enough. It's a link (or a comment, or reply, or like) to an article on my site. What I do know of webmentions from sites like Sophie's and Zach's is that somehow I can have likes, boosts, and replies from Mastodon show up on my blog posts.
This bit has confused me for months (I had a note about this dated November last year). Admittedly I didn't look too far into it but pretty much every article about webmentions will link to two sites: webmention.io and Bridgy.
Webmention.io is:
a hosted service created to easily receive webmentions on any web page
Webmention.io collects your webmentions and exposes an API to be able to fetch these mentions but to start off you need to login which was my first hurdle: We couldn't find any way to authenticate you using your website.
. Webmention.io uses IndieLogin to log you in and apparently I hadn't added rel="me"
to any of my social links. So I added a Github link, deployed it, and was then able to login. Success.
<a style="display: none;" href="https://github.com/rknightuk" rel="me">github.com/rknightuk</a>
Once I logged in I was presented with tags to add to my site to accept webmentions so I promptly added those:
<link rel="webmention" href="https://webmention.io/rknight.me/webmention" />
<link rel="pingback" href="https://webmention.io/rknight.me/xmlrpc" />
At this point, this system will work for "proper" webmentions (although not added to my site yet) but what about conversations on Mastodon? That's where Bridgy comes in.
Bridgy "connects your web site to social media" according to the home page but moreso it converts conversations on social media to webmentions to send back to webmention.io.
I went ahead and signed in with my Mastodon account. After a few minutes Bridgy started showing responses to some recent Mastodon posts that had links to my blog posts. I then went back to webmention.io and there they all were. I'm not sure of the limitations of the initial scan but it only showed the last few posts I had done so I manually went and grabbed some Mastodon posts linking to my blog and put them in the "Resend for post" input box to grab some earlier responses.
I'm using Eleventy data files extensively to power my now page so I knew this would be trivial to pull into my site. Because I already have my "api" I decided to fetch it there instead and then pull it into my site. For the sake of simplicity assume you don't like making your life difficult and all this would exist in Eleventy (or your site of choice).
Webmention.io has an API to fetch mentions. There are a few different endpoints but I wasn't able to work out what all the differences were so I went with the mention.jf2
with no other options. Source on GitHub.
// this API also accepts `since_id` to only get new mentions
const response = await fetch(`https://webmention.io/api/mentions.jf2?token=${webmentionskey}&per-page=1000`);
const body = await response.json();
const newMentions = body.children
I've set this up to run every hour on a cron, merge new data with the existing data, and then write the data to a JSON file. Webmention.io does have an option for sending a webhook when a new mention comes in (under "settings") but that might be a lot of rebuilding if a post gets popular so I'm sticking with this method for now. Then in Eleventy, I fetch this data in src/_data/webmentions.js
.
To get this to render on my post pages, I lifted most of Max's solution with a few changes like grouping the different types of responses together. As an aside, I still don't know what a mention-of
webmention looks like or how one happens.
Update 07/07/2023
I finally got some data to see what mention-of
gives. I decided against showing these though because at least half of them didn't exist after only ~6 months.
{
"type": "entry",
"author": {
"type": "card",
"name": "",
"photo": "",
"url": ""
},
"url": "https://blog.luiscarlospando.com/coding/2023/02/hay-nuevo-sistema-de-comentarios-en-mi-blog-2/",
"published": null,
"wm-received": "2023-07-07T00:18:46Z",
"wm-id": 1693400,
"wm-source": "https://blog.luiscarlospando.com/coding/2023/02/hay-nuevo-sistema-de-comentarios-en-mi-blog-2/",
"wm-target": "https://rknight.me/adding-webmentions-to-your-site/",
"mention-of": "https://rknight.me/adding-webmentions-to-your-site/",
"wm-property": "mention-of",
"wm-private": false
}
// .eleventy.js
eleventyConfig.addFilter('webmentionsByUrl', function(webmentions, url) {
const allowedTypes = ['in-reply-to', 'like-of', 'repost-of']
const data = {
'like-of': [],
'repost-of': [],
'in-reply-to': [],
}
const hasRequiredFields = entry => {
const { author, published, content } = entry
return author.name && published && content
}
const filtered = webmentions
.filter(entry => entry['wm-target'] === `https://rknight.me${url}`)
.filter(entry => allowedTypes.includes(entry['wm-property']))
filtered.forEach(m => {
if (data[m['wm-property']])
{
const isReply = m['wm-property'] === 'in-reply-to'
const isValidReply = isReply && hasRequiredFields(m)
if (isReply)
{
if (isValidReply)
{
m.sanitized = sanitizeHTML(m.content.html)
data[m['wm-property']].unshift(m)
}
return
}
data[m['wm-property']].unshift(m)
}
})
return data
})
// _templates/post.njk
{%- set webmentionUrl -%}{{ page.url | stripIndex }}{%- endset -%}
{% include 'webmentions.njk' %}
// webmentions.njk
// Then the same again as this for `repost-of` and `in-reply-to`
{% if mentions['like-of']|length %}
<h4>Likes</h4>
<div class="webmentions--likeboost">
{%- for like in mentions['like-of'] %}
<a target="_blank" rel="noopener" href="{{like.author.url}}"><img src="{{like.author.photo}}" title="{{like.author.name}}"></a>
{% endfor%}
</div>
{% endif %}
A little bit of CSS later and we have lift off:
You might even see some webmentions below this post if I'm lucky.
💬 Adding Webmentions to Your Site https://rknight.me/adding-webmentions-to-your-site/
In which I fumble through and somehow get this working. I’ve tried to explain what I’ve learnt as best I can but I’m still not 100% on how this works if I’m honest 🤷♂️