Exporting Mastodon Posts to My Blog

Twitter’s public implosion has redoubled my interest in blogging, and I’ve been posting more.

Through Micro.blog, I syndicate my posts to the fediverse, namely Mastodon at @chrisd@micro.blog. I have a separate Mastodon account I created years ago at @bronzehedwick@mastodon.social. I’m not yet sure how I ultimately want to structure these identities, but at the moment I have micro.blog content appear on the mastodon.social as well.

While Mastodon is great, it is not easy to migrate your content between instances, something which blogging excels at. So I wanted to import my mastodon.social archive into my blog, at least for better data ownership, but also in case I decide to make micro.blog my only active identity.

To that end, I downloaded my mastodon.social archive, and wrote a little Node script to output non-reply posts (I don’t currently have a self-hosted reply mechanism) as appropriately formatted markdown files for my Hugo site.

#!/usr/bin/env node

import { readFile, writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';
import { striptags } from 'striptags';

try {
    const filePath = new URL(
        '/Users/chris/Downloads/mastodon-archive/outbox.json',
        import.meta.url
    );
    const contents = await readFile(filePath, { encoding: 'utf8' });
    const data = JSON.parse(contents);
    data.orderedItems.forEach(item => {
        if (item.object.inReplyTo || !item.object.content) return;
        const unixTimestamp = Math.floor(
            new Date(item.published).getTime()/1000
        );
        const template = `+++
title = "Note on ${item.published}"
slug = "${unixTimestamp}"
date = ${item.published}
layout = 'post'
+++

${striptags(unescape(item.object.content), {allowedTags: new Set([
    'a',
    'strong',
    'em',
])})}`;

        const fileData = new Uint8Array(Buffer.from(template));
        const controller = new AbortController();
        const { signal } = controller;
        writeFile(
            `/Users/chris/Sites/Personal/chrisdeluca/content/note/${unixTimestamp}.md`,
            fileData, signal
        );
    });
} catch (err) {
    console.error(err.message);
}

That worked great, and I published my full mastodon.social archive yesterday!

Turns out, that even though all these items are well in the past, they were still ingested as new items, and five years of posts got spammed across the fediverse at once. Oops!

I don’t know how to avoid that in this situation, but apologies to everyone who had to withstand that fire hose. I don’t plan on doing it again, since I already have my archive, but any recommendations on how to avoid this for others would be really helpful.

Thanks for reading!