# Experiences writing an ActivityPub server in Python with Django

This post is about how I wrote an ActivityPub server using the Django Python framework, to provide a fediverse account on the same domain as a WordPress blog.

## My motivation

On The Aperiodical, we do a monthly post collecting bits of maths news that we've seen. This is the compromise we came to as we realised that we're too old and busy to keep up with writing in-depth posts about individual things any more.

When we started doing this, I set up a /news slash command in our Slack channel which would take a URL and some explanatory text, and add it to the current draft post. Slack insists you give a username to the account that replies to this command, so we have our happy little Aperiodipal secretary.

Since we only publish the news posts once a month, we sometimes miss out on spreading the word about time-limited events, such as deadlines to register for conferences or mathematical holidays. And when there's Big Maths News such as a proof of an old conjecture, it'd be nice to put something out immediately rather than waiting until it's old news.

So I thought it would be a good idea to automatically toot on the fediverse each time one of us adds an item to the news post, as part of the /news Slack command.

Ideally, I'd like the fediverse account to belong to the aperiodical.com domain, instead of a Mastodon instance such as mathstodon.xyz.

That meant I'd have to serve the ActivityPub protocol on aperiodical.com. This is the kind of thing you do when really you're more curious about how the protocol works than making a pragmatic decision of the best course of action.

Mastodon has really taken off this month, as a result of Twitter collapsing.

The instance I run with Colin Wright, mathstodon.xyz, has grown from about a thousand active users to just over 5,000 as I write.

There are lots of people running small Mastodon instances who suddenly need to support lots more activity than they're used to. I've had to learn a lot about making a webserver run at scale, so I thought it would be worth writing down what I've learnt.

I didn't take proper notes while fixing things, so I've probably forgotten some important non-obvious stuff. Soz!

This is just the things that stuck in my mind recently.

# Can I also use?

While updating Numbas, I wanted to know which new CSS and JavaScript features we could use while maintaining compatibility with almost all browsers in use. Until now, we'd been stuck hanging on to support for Internet Explorer 11, but it seems to have finally faded away into insignificance.

So we needed a new target for compatibility. We agreed that aiming for compatibility with 95% of devices in use would be a reasonable target.

I have been using caniuse.com to check individual features against their usage stats. It's a really useful site!

But our policy shouldn't just be "any feature supported by 95% of browsers can be used": if two features each have 95% support, the set of browsers supporting both features could be anything between 95% and 90%. With more features, the intersection of all those sets could end up being much lower than our target, and make Numbas inaccessible for a significant number of people.

I looked around for something that would let me specify a collection of features and see what percentage of browsers support them all. I'm sure it exists, but I couldn't find it, so I spent a happy day making it.

The tool is online at somethingorotherwhatever.com/can-i-also-use.

I gathered MDN's compatibility data and caniuse.com's usage data, and presented it as a massive searchable tree of features you can tick off. The other half of the screen shows you which browsers support all of the selected features, and their relative usage and release dates. For each browser, you're shown which features force that particular version, so you know what to drop in order to be supported by older versions.

I've realised that I have to be quite careful about which features I tick when using this tool, particularly with respect to CSS: browsers usually silently ignore CSS rules that they don't support, so I should only tick features that are absolutely required for the page to function.

There's a function to download the set of selected features so you can documente what your project requires. I haven't added this yet, but next time I need to do this job I'll add a feature to load in that file, so I don't have to start again from scratch!

# Adventures in building an Electron app on Windows

I've been working on a new version of the Numbas lockdown browser app.

It's a very basic Electron app: the point is to provide a web browser with none of the developer tools that would let a student fiddle with the internals of a Numbas exam.

Packaging it up has been a nightmare!

For Windows, we need to produce a .msi file for our IT people to install automatically on cluster PCs.

The electron-builder documentation seems comprehensive, but it's extremely hard to read. It took me ages to work out whether and how certain things could be configured.

At least now I know a little bit more about how .msi files work!

Our IT person said that the app automatically launches after installation, which he doesn't want to happen.

I couldn't find an option for that in the configuration documentation, so I decided to look inside the .msi file to see how it's defined, then trace things backwards to a configuration option.

I installed the msitools package on Ubuntu, then ran msiinfo tables Numbas-lockdown.msi to see what's inside.

The InstallExecuteSequence table turns out to contain a list of things that happen during installation.

There's a runAfterFinish option which seems to be what I want.

I traced that back through the source code of electron-builder, and eventually found out that in the build.msi object in my project's package.json, I can set runAfterFinish: false.

Phew!

At the moment I'm busy organising the EAMS 2022 conference. This is the fifth time the conference has run, and the third time fully online.

I'm pretty comfortable with what needs to happen now, and so far it seems to be going smoothly.

Over the years I've gradually automated more and more of the admin. I thought it might be interesting to write up what I've done.

This is very much a 'CLP knows how to write scripts to do things, so has done that' situation, rather than something I've done out of absolute necessity.

Do what works for you, I suppose!

# Thinking about typing mathematical notation on a phone

This post is incomplete; I'll change it and add to it as I work on this problem.

I'd really like to be able to type mathematical notation on my phone.

There are many contexts in which I'd like to do that: writing blog posts; posting on social media; taking notes for myself.

At the moment, whenever I want to toot something on mathstodon.xyz with maths in it, I wait until I'm at my PC, because writing TeX on a touchscreen keyboard is such a pain.

I'd also like to question some of the fundamentals about how TeX works, and investigate if there are other way that work just as well or better.

I've had a go at implementing some of these ideas.

# Tractor Befunge

Befunge is a classic esoteric programming language. The idea is that it's a bit like a Turing machine, but instead of the instruction pointer moving along a one-dimensional tape, it moves around a two-dimensional grid.

A happy image occurred to me of the instruction pointer as a tractor, driving around a field.

So I decided to make it happen!

I found a really nice pixel art editor called Pixelorama, which I used to draw a v v shonky tractor, patches of grass, and bales of hay representing numbers on the stack.

It's cute!

The controls were more complicated than I expected. I wanted everything to feel as "in the world" as possible, so the program should be edited by interacting with the graphics rather than typing in a code box. I ended up giving the caret a direction, so it's just as easy to type upwards or backwards as it is left-to-right. To avoid having a "reset" button, you can just pick the tractor up and move it, and right-click to empty the trailer.

Other Life stuff came up before I was completely finished with it, but I'm quite happy with my cute Turing-complete tractor.

It's online at somethingorotherwhatever.com/tractor-befunge, and the source code is on GitHub.

# Convert a sequence of images to a single PDF

Somebody sent me their slides for a talk as a collection of .png images.

I wanted to collect them into one PDF. It turns out that ImageMagick's convert tool can do it:

convert slide-*.png slides.pdf

I had to change a security policy in ImageMagick which was apparently instated due to a bug in Ghostscript.

# Kdenlive for video

I used Kdenlive to make a new Numbas demo video.

I was pleased with how easy it was.

• Wrote a script, using the old video and the text from the Numbas front page.

• Did some screen recordings to cover each of the bits.

• Made 1920×1080 images using the drawings from the front page.

• Recorded the script in gnome-sound-recorder.

• Dragged all the files into kdenlive.

• Cut out just the bits I wanted from the videos and the sound by making zones: play a clip, press I to set the start point, and O to set the end point, then Ctrl-I to save that as a zone.

• Eventually realised V inserts the selected zone into the timeline.

• Dragging things around on the timeline is easy. The move tool with M shifts a contiguous block of things around to make space.

• Worked out that you can trim bits by dragging their sides in the timeline, and add fades or wipes by clicking the dots that appear in the corners of a clip.

I used the new screen recorder in ubuntu 22.04 (I think it's really the Gnome 3 built-in recorder) to record my screen, then wanted to share it on twitter and mastodon.

It produces a webM file with a lot of missing metadata, so I had to convert it. A lot of searching finally got me something that worked.

First, make an mp4 with a fixed frame rate:

ffmpeg -i recording.webm -c:v libvpx-vp9 -minrate 2M -maxrate 2M -b:v 2M -pix_fmt yuv420p -r 16 recording.mp4

This makes something that mastodon will accept. Twitter won't, so you need to convert that. I also had the problem that my video was an odd number of pixels wide, so I did this:

ffmpeg -i recording.mp4 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" recording.twitter.mp4

Since I'm probably going to do this a few times, I've put these steps in a script in ~/bin/convert-screencast, which finds the most recent recording and converts it to mp4:

#!/bin/bash

LATEST=ls -tr ~/Videos/Screencasts/Screencast*.webm | tail -1
OUT=${LATEST%.webm}.mp4 ffmpeg -i "$LATEST" -c:v libvpx-vp9 -minrate 2M -maxrate 2M -b:v 2M -pix_fmt yuv420p -r 16 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" "${LATEST%.webm}.mp4" echo$OUT