How I built a RemindMe bot for Mastodon

How I built a RemindMe bot for Mastodon

In November 2022, I deleted my Twitter account. Like many others at the time, I had decided that the direction the platform was heading no longer aligned with how I wanted to participate in social media.

Mastodon was the obvious alternative, open, federated, and community-driven. I set up my own instance at toot.re, and started building a life there on the Fediverse.

What I quickly noticed was that one little thing I’d come to rely on was missing: a reminder bot. Twitter had a few of these floating around, and I’d used Reddit’s RemindMeBot frequently. The idea is simple, you tag a bot, tell it when and why to remind you, and it sends you a notification at the right time. On Mastodon, nothing like this existed. So I decided to build one myself.

I want to be upfront: I’m not a professional developer. I describe myself as a “code amateur who builds software for fun.” This project has been exactly that, a fun, frustrating, educational, and ultimately rewarding hobby project. This blog post is the story of it.

Why I built it

The trigger was simple: I saw a toot I wanted to come back to later, and I had no good way to do that. Mastodon doesn’t have a native “remind me” or snooze feature. You can bookmark toots, but bookmarks are a passive list you have to actively visit. What I wanted was something that would tap me on the shoulder at the right moment and say: “hey, remember that thing?”

The use case felt obvious: you’re scrolling through your timeline, you see an interesting article, an event announcement, or a thread you want to revisit, and you just want to tag @remindme and say “in 3 days” or “next Monday”. No app switching. No copy-pasting links into a calendar. Just a quick mention and you’re done.

I built this because I wanted to use it. That’s probably the best reason to build anything.

The first version: Polling and its problems

The first iteration of the bot was built in 2022, and it worked by polling. In practical terms: the bot would run on a cron job every minute, fetch any new mentions or direct messages from the Mastodon API, parse them, store them, and send reminders when they were due.

Polling works. It’s simple to implement and easy to reason about. But it also has real downsides. You’re constantly contacting the API even when there’s nothing new to process. It’s not very efficient, and on a self-hosted Mastodon instance, that felt like an unnecessary load.

So in early 2023, I started looking at using the Web Push Notification API instead. A way for the Mastodon server to proactively push notifications to the bot rather than the bot going to fetch them. This is a much more elegant architecture on paper. In practice, it turned out to be one of the hardest things I’ve ever tried to implement. And I failed.

The hard part: Decrypting Web Push notifications

Web Push Notifications use end-to-end encryption. That’s a good thing from a privacy standpoint, but it means that when a notification lands at your endpoint, it arrives as encrypted bytes. You have to decrypt it before you can read what’s inside.

The encryption scheme used is called ECDH (Elliptic Curve Diffie-Hellman), combined with AES-GCM. It sounds intimidating, and implementing it correctly is genuinely non-trivial. I went down a long rabbit hole trying to get the decryption working, posting on StackOverflow, reading specs, debugging byte arrays. At various points the bot would decrypt something that looked almost right but had garbled characters, or fail entirely with a cryptic error.

I paused the bot while I worked on this refactor. It felt wrong to keep running a polling version that I knew I wanted to replace, but the push version wasn’t ready yet. As I wrote in my 2023 recap: “Decryption of Web Push API Notifications is a struggle. But, I will succeed!”. (Spoiler: I didn’t).

I did eventually decide to move on by taking a pragmatic step back. The current version of the bot is back to a polling approach, this time implemented cleanly in PHP with a proper architecture and much better documentation. Sometimes shipping something that works is more valuable than waiting for the perfect implementation.

How it works today

The current version of the bot, which shipped as v0.2.1 on March 28th, 2026, is written in PHP and built around two simple workers that run on cron jobs. Here’s how the pieces fit together.

Two workers, one job

The bot runs two PHP scripts via cron:

  • poll.php runs every minute and fetches new direct messages and mentions from the Mastodon API. It uses “since_id” paging so it only picks up notifications it hasn’t seen before. It parses the message, routes it to the right command handler (or creates a reminder if it’s a time-based request), and replies to the user with a confirmation.
  • due.php also runs every minute and queries the database for any reminders that are now due. It sends each one as a direct message, marks it as sent, and moves on.

Natural language parsing

One of the things I’m most happy with in the current version is the natural language parser. You can send the bot messages like:

  • @remindme in 2 hours about the meeting
  • @remindme tomorrow at 9am to send the invoice
  • @remindme next monday about the project deadline
  • @remindme on 2026-04-15 about my dentist appointment
  • @remindme in 3 days

The bot uses the Carbon library for date/time math, which handles the heavy lifting of converting “in 3 days” or “next monday” into a concrete UTC timestamp.

Commands

Beyond setting reminders, the bot understands a handful of commands:

  • help: Shows usage instructions
  • list: Lists your pending reminders
  • cancel [id]: Cancels a specific reminder
  • set timezone [timezone]: Configures your local timezone so reminders fire at the right local time

Commands might have changed in new releases, please check the docs for the latest information.

Privacy by design

Privacy was a deliberate choice from the start. All reminders are sent via direct message, not public toots. If you accidentally mention the bot publicly, it sends you a private DM explaining how to use it properly rather than replying publicly and potentially exposing what you wanted to be reminded about.

On the backend, the bot stores only what it needs: your Mastodon account ID, the reminder text, the due time, and a user-configurable timezone. Metrics logging uses HMAC-SHA256 hashing of user IDs, so the analytics log never contains identifiable information.

The database

The data layer is intentionally simple: a SQLite database with three tables.

  • A table that tracks the last processed notification ID.
  • A reminders table with all the reminder records
  • A user_settings table for per-user timezone preferences.

SQLite keeps the operational footprint tiny, no separate database server to maintain, no connection pooling, just a single file.

Challenges along the way

Beyond the encryption rabbit hole, a few other things were unexpectedly tricky.

Timezones

Time handling is famously hard. The bot needs to correctly interpret phrases like “tomorrow at 9am” in the context of the user’s local timezone, but store everything in UTC so the due worker can compare apples to apples. Getting this right required building a per-user timezone configuration system and being careful about every point where a local time gets converted to or from UTC. The architecture doc has a whole section just on time handling.

Parsing natural language

Natural language is messy. People write “in 2 hrs”, “2 hours from now”, “tomorrow morning”, and a hundred other variations. The MVP parser handles the most common patterns reliably, but there’s still plenty of room to fail gracefully on unusual input. The roadmap explicitly includes a hardening phase focused on negative tests and clearer error messages.

Rate limiting

Opening a bot up to the Fediverse means opening it up to abuse. I’ve built in per-user rate limiting, both per-minute and per-day caps, driven by environment variables so they can be tuned without a code change. It’s not sophisticated, but I think it’s a reasonable first layer of protection for an MVP.

The decision to rebuild

Technically, the hardest decision was choosing to restart from scratch with a cleaner architecture rather than continuing to patch the 2022 version. The original bot had accumulated technical debt and was built around assumptions that no longer held.

Starting fresh felt painful, but it let me write proper documentation, structure the code properly, add tests, and build something I’m not embarrassed to put on GitHub.

Current status

The bot is live as of February 2026 and running as @remindme@toot.re. Version 0.2.1 is deployed at the time of publishing this post, with basic reminder creation, command support, timezone configuration, and privacy-friendly logging all working.

The codebase is open source and available at github.com/mbootsman/remindme, licensed under the GPL-3.0. If you want to self-host this for your own instance, the README will get you started.

What’s next

The roadmap is broken into three phases after the MVP.

Phase 2: Quality focuses on making the existing features more robust. That means hardening the parser against edge cases, adding retry logic for failed reminder deliveries, and setting up monitoring so I know when something goes wrong.

Phase 3: V1 is where things get interesting. I want to add recurring reminders (“every Monday at 9am”), a web dashboard for managing your reminders outside of Mastodon, and multi-language support starting with Dutch and English.

There’s also the unresolved question of Web Push Notifications. I haven’t given up on the idea, it’s still architecturally the right approach for efficiency at scale. But the decryption challenge is real, and I’ll tackle it when I have the time and headspace to dig back in properly.

Why Open Source?

I put this on GitHub publicly because I believe in the spirit of the Open Source. Mastodon and ActivityPub are built on openness and federation, and it felt right to contribute something back in the same spirit. If someone else wants to run their own reminder bot for their instance, they should be able to. If someone spots a bug or has a better idea for the parser, I want to hear it.

The code isn’t perfect. The README is honest about the fact that it’s an MVP with rough edges. But it works and it’s useful. Sometimes that’s enough to start.

Try It. You can use the bot by sending a direct message to @remindme@toot.re from any Mastodon instance. Send it “help” to get started. And please, let me know if you get in to trouble, or experience unexpected responses.

Share on Mastodon

About Marcel Bootsman

Marcel discovered the web in 1995. Since then he has paid attention to and worked with lots of technologies and founded his own WordPress oriented business nostromo.nl in 2009.

Currently Marcel is Partnerships & Community Manager EMEA at Kinsta. where he helps clients and partners grow with their business with Managed Hosting for WordPress.

You can contact Marcel on a diverse range of online platforms. Please see the Connect section on the homepage for the details.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *