Using Mastodon to power my blog comments

×Mastodon Powered Blog Comments

Since my article earlier this year about integrating my blog's comments with Twitter, I've now switched to power my blog comments with discussions happening on Mastodon.

My goal

I wanted a simple way to display relevant discussions that may naturally happen on Mastodon related to my articles, without the complexity of setting up my blog as an ActivityPub server/actor.

How does it work?

  1. On my website, at the bottom of each article, I built a way to display relevant discussion from Mastodon and link to join that discussion.
  2. On my server, I built an endpoint to fetch relevant status updates about an article. This uses the Mastodon API to find updates that link to the article and replies to those updates.
  3. In my blog's CMS, I added a way to manually record status updates that are relevant to each article. This is useful if there are interesting discussions without a direct link to the article.

How are discussions selected?

My solution uses a combination of public and private endpoints of the Mastodon REST API:

  • /account/:id/statuses – I get my own status updates that include a link to the article
  • /notifications – I filter my mentions for any that include a link to the article
  • /statuses/:id – I get a list of status updates that I've manually added for each article
  • /statuses/:id/context – I get all the replies to all the above status updates

Here's a simplified version of the server-side code:

// Get hard-coded statuses for this article
const knownStatuses = await getStatusesByIds(knownStatusIds);

// Get statuses that include the article URL from my mentions and my own account
const relevantStatuses = await getRelevantStatusesForPath(path);
const relevantStatusIds = relevantStatuses.map(status => status.id);

// Get replies to all of the above statuses
const statusReplies = (await Promise.all(
  [...knownStatusIds, ...relevantStatusIds].map(id => getStatusReplies(id))
)).flat();

// Return the status updates and relevant status IDs
return {
  statusCode: 200,
  body: JSON.stringify({
    conversation_ids: [
      ...knownStatusIds, 
      ...relevantStatuses
        .filter(status => 
          status.account.acct === `${MASTODON_USERNAME}@${MASTODON_INSTANCE_DOMAIN}`)
        .map(status => status.id)
    ],
    replies: getSanitizedStatuses([
      ...knownStatuses, 
      ...relevantStatuses, 
      ...statusReplies,
    ]),
  }),
};

For the full code, see this Gist. The implementation is specific to Netlify Functions environment, but it can be modified to work in many different JavaScript-based server environments.

You can also read my previous article on integrating my blog with Twitter for comments to see how a similar approach can be applied to Twitter.

Please reach out if you have questions.

Quick server selection

×Quick Mastodon Server Selection

One important aspect of Mastodon that affects the user experience is that it runs on many servers, each with their own domain (e.g. mastodon.social or mas.to), rather than a single domain like twitter.com or facebook.com. To let someone join the discussion, we need to know which Mastodon server they have an account on.

I used a few principles to guide my approach to this:

  1. Hide the complexity until people engage – People shouldn't need to think about about the complexity of Mastodon servers if they're just reading the comments. The complexity should be progressively disclosed.

  2. Make the selection as quick as possible –  For users familiar with Mastodon, it should be quick to enter or select their server. To help, I suggest common servers, and keyboard navigation is suppported.

  3. Remove the need for repetitive actions – Once a user has chosen their server, it should be remembered. Future replies should just take a click. (Of course, there's a way to go back and edit the selected server.)

  4. Give everyone a path forward – Mastodon is not as well known as other social media sites, so it's important to give those who don't use it a path forward. I opted for an "I don't have a Mastodon account" option which opens the latest discussion on the server I use. The user then has a path to see how Mastodon works, and they may choose to sign up.

Notable limitations

The main difficulty I had when using Mastodon as a comments system was reliably finding all shares of a URL across Mastodon. Ultimately, I couldn't do this, so unless my Mastodon account is involved in a discussion (i.e. gmph@mas.to is mentioned), it won't be displayed.

The Mastodon Search API isn't as robust as the Twitter Search API:

  • Full text search is poor, which means searching a URL (or any keywords really) rarely returns relevant results.
  • There aren't many filters, which means narrowing down to the most relevant updates is challenging.
  • Cross-server search isn't well supported, which means only discussions my Mastodon server has knowledge of can be found.

All in all, this meant I had to query shares in more limited, manual ways.

Feedback

If you have any feedback on this implementation or you're working on something similar, I'd love to hear from you. You can reply to me on Mastodon below 😉

Have a lovely day.
© 2023, Graham Macphee.