When I created this website and blog as a static website with Zola, I wondered how I could add a comment feature.
A quick search revealed the post Adding comments to your static blog with Mastodon from Carl Schwan, in which he explains how he shows responds to his Mastodon posts as comments to his blog. This was already very close to what I wanted to achieve, however, I would prefer a solution that works without the need to dynamically load the comments via JavaScript. Hence, based on his solution I created a completely static integration of Mastodon responses as comments into my website.
Prepare Static Website for Comments
The content of the static website is provided in Markdown files, which means that there is a Markdown file for each blog post. Comments could be directly added there as well, however, for a cleaner separation of blog content and comments, I decided to use a separate Markdown file for the comments for each blog post. For this post, which is located at content/blog/static-comments-for-static-website.md
, the comments would be added to the file content/blog-comments/static-comments-for-static-website.md
.
As long as the comments file has the same filename as the Markdown file of the blog post, the comments can be included for the blog post, by adding the following to the blog page template:
{#- Show comments and link to Mastodon post, if the comments variables are set for the page. -#}
{#- Generate comment filename from current filename (same name different path) and load its content. -#}
{#- Show comments, if there are any in the comments file. -#}
<h2> </h2>
{#- Show link to the corresponding Mastodon post. -#}
<a href="https:// /@ / "
title=" "
target="_blank" class="italic text-center mt-2"><i class="bi bi-mastodon mr-1"></i> </a>
For the comments itself, I added a shortcode, so that the presentation is separated from the content:
{{ fullname }}
{{ datetime }}
{{ body | safe }}
{% if favorites > 0 %}
{{ favorites }}
{% endif %}
The shortcode can be instantiated like this in the comments Markdown file:
This comment is just a test to show the Mastodon responses feature in my blog.
Which would lead to the following rendering:
With the templates and comment files being prepared, it was then possible to create a script to retrieve the comments from Mastodon and fill them into the comments files.
Retrieving Comments from Mastodon
As described by Carl Schwan, there is a simple API available on Mastodon, to retrieve all replies to a given post. I created a small Python script that iterates through all blog entries, retrieves the Mastodon post IDs from the entries and checks the Mastodon API for the responses to this post.
#!/usr/bin/env python3
# the TOML header of the Markdown files (as defined for Zola)
=
# path where the markdown files of the blog entries are stored
=
# path where the markdown files for the blog comments should be written
=
# the default language of the website as defined in the config.toml
=
# additional languages of the website as defined in the config.toml
=
"""
Returns a list of all markdown blog entry filenames from the given directory.
The index files are always excluded from this list and it can optionally be
requested with the exclude_translations parameter, to only return the files
of the default language, but exclude all translation files.
"""
# pattern for markdown file names in additional languages that contain a language code (e.g, blog-entry.de.md)
=
=
=
return
"""
Returns a dictionary with the Mastodon comments configuration from each
blog entry file from the given path.
It can optionally be requested with the exclude_translations parameter, to
only return the configuration from the files of the default language.
"""
=
=
=
=
return
"""
Creates the markdown files for the comments of each blog entry.
There are no comments in the files, only the header is written. Existing
files are not overwritten.
"""
= f
"""
Returns the display name from the given comment dict, but with all emojis
removed from the name.
"""
=
=
return
"""
Returns the string for the macro instance of the comment.
The macro is defined in templates/shortcodes/comment.html. For now, only
the date and time are translated according to the given locale.
"""
=
= 1
=
=
=
return f \
f \
f
# argument handling to allow for the parameter for creating the missing files
=
=
# create the markdown files for the comments of each blog entry (without comments)
=
# retrieve the comments from Mastodon and write them to the markdown files
=
=
# retrieve JSON from the Mastodon API
= f
=
# write the default language comment files
# write the additional language comment files
=
In addition to retrieving the responses, this script also provides the functionality to create the empty comment files for each blog entry. This is needed, because the used static site generator Zola does not support checking file existence before including a file. Hence, the comment files that are included as described before, all need to exist when building the site.
Integration with CI Pipelines
I didn't want to run the script manually, hence I added a Github workflow to regularly retrieve the responses from Mastodon and notify me by creating a pull-request as soon as there are any new, changed or deleted comments. The workflow calls the retrieve-comments.py
script and, if there are any changes in the files afterwards, it creates a new branch and a pull-request with those changes.
name: Add Comments
on:
schedule:
- cron: '30 20 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
comments:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Retrieve Comments
run: ./retrieve-comments.py
- name: Create Pull-Request
run: |
if [ ! -z "$(git status --untracked-files=no --porcelain)" ]; then
git config --local user.email "adrian@winterstein.biz"
git config --local user.name "Adrian Winterstein"
# checkout a branch, commit the comment changes and push the branch
git checkout -b "feature/comments-adaption"
git commit -a -m "Changed blog comments according to changes on Mastodon."
git push origin $(git branch --show-current) --force-with-lease -f
# create pull-request, if it dows not exist yet
if [ -z $(gh pr list --search "head:feature/comments-adaption" ) ]; then
gh pr create -B main -l comments -a awinterstein \
-t "Changed blog comments according to changes on Mastodon" \
-b "This is an automatically created pull-requests due to changes of the blog comments on Mastodon."
fi
fi
env:
GH_TOKEN: ${{ secrets.ACTIONS_ACCESS_TOKEN }}
Running the workflow once a day should keep the load on the Mastodon server low, while still being frequent enough for a small blog, where I do not expect a lot of comments.
Creating a Blog Post
After publishing a blog post, a corresponding post on Mastodon needs to be created then. So that readers have a place to respond on. When the Mastodon post was created, the following variables can be added to the blog post, to link the blog to the Mastodon post:
[]
= "mastodon.social" # my Mastodon instance
= "awinterstein" # my Mastodon username
= 114081713837118160 # the id of my Mastodon post
The retrieve-comments.py
script uses those and will retrieve all replies of the Mastodon post from then on. Hence, the next pipeline execution after someone responded, will create a pull-request to integrate the comment into the static blog. If someone updates or deletes their response, this will be updated automatically as well.
Kommentare

This response is just a test to show the Mastodon responses feature in my blog.
This comment is just a test to show the Mastodon responses feature in my blog.