As mentioned in the first post in this series, this post will cover how to build a Mastodon bot. This can be fairly straightforward, but - as always - there are multiple ways to “make it work”. Fortunately, there are already some fantastic tutorials out there that can help you get started (and they have definitely helped me out a lot!). I have listed them in the References for you to get some more inspiration. There are two main components to building Mastodon bots:
This post is about the first part, and I’ll share my methods for the deployment of the code in a third blog post.
In its current state, the bot does three different things:
#pyladies' or
#rladies'.And this is what it looks like:
Alternative text
Diagram showing the architecture of the Mastodon bots (PyLadies and R-Ladies bot). Both bots rely on several Python scripts that allow them to collect metadata (including RSS feeds) and use that information to promote, reblog, and boost posts and mentions, among other things.
We’ll talk more about how the individual scripts are orchestrated and executed automatically in the next post (in short, you can use whatever framework works best for you - I mainly rely on GitHub Actions, but started with AWS Lambda) - this is also intertwined with what the first part of the diagram covers. This post will cover how to set up a script that allows your Mastodon bot (the far right rectangle in the diagram) to act.
But before we get there, we need to create a bot first. There are a few instances that allow bots - I chose botsin.space. Setting up a bot there is a fairly straightforward process.
Alternative text
Screenshot of what is required when setting up a Mastodon bot at botsin.space (display name, username, email address, password, and reason for wanting to join.)
In practice it looks like this
Screenshot of what you can add once your account is active:
To make sure the bot is recommended to others and reaches a wider audience, I also checked the suggestion box. In the description, I’ve also added who created the bot (and a disclaimer that it’s still experimental). This is to make sure that people know who to contact if they need to.
mastodon
- The Perfect Python API That Helps You Publish Your PostAs you can see in the diagram above, each script follows a similar scheme:
Diagram showing the architecture of the Mastodon bots' scripts. They typically follow the following sequence:Alternative text
We’ll walk through these steps with a simple example that will allow you to build your own bot later.
A common task for a bot might be to repost posts that mention the bot, and that’s what we know. We will use the [boost_mentions.py
script] (https://github.com/cosimameyer/mastodon-bots/blob/main/src/boost_mentions.py). We’ll dive into the three core parts individually and then bring them together. The code is heavily inspired by Joshua Quirk’s Rebooster
bot - if you want to see another example that follows a similar setup, head over to the Rebooster
repository.
Here we rely on a config file (for general settings) and environment variables (for secrets and credentials). Let’s take a look at config.py
first:
##########################################################
# Config file
##########################################################
# The URL of the mastodon instance your bot is on
API_BASE_URL = 'https://botsin.space'
# Define the visibility on Mastodon
MASTODON_VISIBILITY = 'public'
The rest of the information is stored in your environment. You can get all the information in the Mastodon interface by following these steps:
Alternative text
Screenshot showing the “Your applications” page at Mastodon. The “Development” tab is selected on the left-hand side and allows the user to click the “New application” button.
Alternative text
Screenshot showing the “New application” page at Mastodon. The name rladies_bot
is already filled in but it can be any name as it just helps you to identify for which application you use your newly generated keys.
Alternative text
Screenshot showing the “Your applications page”. It shows that the application rladies_bot
was successfully created with the scopes “read write follow”. “Follow” is optional and not required for our purpose.
Alternative text
Screenshot showing the “Application: rladies_bot” page at Mastodon. The client key, client secret and access token are blurred but those will be the ones that you will use to authenticate to Mastodon.
To pass them to the bot, add them to your environment by putting them in an `.env' file.
With this information, we then run the boost_mentions.py
script and authenticate with Mastodon. You can easily rely on the Python library mastodon
, which has many features to make your life easier when posting.
import os
import config
from mastodon import Mastodon
if __name__ == "__main__":
MASTODON_VISIBILITY = config.MASTODON_VISIBILITY
MASTODON_URL = config.API_BASE_URL
MASTODON_VISIBILITY = config.MASTODON_VISIBILITY
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
PASSWORD = os.getenv("PASSWORD")
USERNAME = os.getenv("USERNAME")
CLIENT_NAME = os.getenv("CLIENT_NAME")
CLIENT_CRED_FILE = os.getenv('BOT_CLIENTCRED_SECRET')
TIMELINE_DEPTH_LIMIT = 40 # How many of the latest statuses to pull per tag.
print(f"Initializing {CLIENT_NAME} Bot")
print("-----------------------------------------------")
print(f" > Connecting to {MASTODON_URL}")
client_id, client_secret = Mastodon.create_app(
CLIENT_NAME,
api_base_url = MASTODON_URL)
# Create client
mastodon = Mastodon(
client_id = client_id,
access_token = ACCESS_TOKEN,
client_secret = client_secret,
api_base_url = MASTODON_URL,
)
print(f" > Logging in as {USERNAME}.")
mastodon.log_in(
USERNAME,
PASSWORD
)
print(" > Successfully logged in")
print(" > Fetching account data")
account = mastodon.me()
Now that we have authenticated with Mastodon, we can read the feed. There are several options - since we want to capture all posts that mention the bot, we choose types=['mention']
.
notifications = mastodon.notifications(types=['mention'])
print(f" > Fetched account data for {account.acct}")
print("-----------------------------------------------")
Now that we have all the notifications collected, we can go through them, check if the bot has already favorited or boosted them (we don’t want to boost or favorite them again), and then post them using mastodon.status_reblog
and mastodon.status_favorite
. There are other features that help you write and publish posts. I have collected the ones I use for the bots below:
Function | What it does |
---|---|
mastodon.status_reblog |
Reblogging statuses previously catched |
mastodon.status_favourite |
Favoriting statuses previously catched |
mastodon.status_post(toot_txt) |
Post a text (the text is a simple string in toot_txt ) |
mastodon.media_post(filename) |
Capturing a file (filename ), |
mastodon.media_update(media_upload_mastodon, description = en['alt_text']) |
updating it by adding an alternative text, |
mastodon.status_post(toot_txt, media_ids = media_upload_mastodon) |
and posting a post with an image (the post is a string in the object toot_txt ) |
I added a try-catch to make sure the bot does not fail, but posts a message if it does (there may be scenarios where we definitely want the bot to fail. In those cases a try-catch isn’t the best way to go, but for this setting it’s fine).
print(" > Reading statuses to identify tootable statuses")
for notification in notifications:
if not notification.status.favourited and \
notification.status.account.acct != account.acct:
try:
print(f"- Boosting new toot by {notification.account.username} viewable at: {notification.status.url}")
mastodon.status_reblog(notification.status.id)
mastodon.status_favourite(notification.status.id)
except:
print(f"- Boosting new toot by {notification.account.username} did not work - going to the next toot.")
As mentioned above, the code is heavily inspired by Joshua Quirk’s Rebooster
bot. I follow his approach and print some logs directly. We could also initiate a logger and capture all logs, which is definitely a good idea when putting things into production.
Here are all relevant files together
config.py
##########################################################
# Config file
##########################################################
# The URL of the mastodon instance your bot is on
API_BASE_URL = 'https://botsin.space'
# Define the visibility on Mastodon
MASTODON_VISIBILITY = 'public'
boost_mentions.py
import os
import config
from mastodon import Mastodon
if __name__ == "__main__":
MASTODON_VISIBILITY = config.MASTODON_VISIBILITY
MASTODON_URL = config.API_BASE_URL
MASTODON_VISIBILITY = config.MASTODON_VISIBILITY
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
PASSWORD = os.getenv("PASSWORD")
USERNAME = os.getenv("USERNAME")
CLIENT_NAME = os.getenv("CLIENT_NAME")
CLIENT_CRED_FILE = os.getenv('BOT_CLIENTCRED_SECRET')
TIMELINE_DEPTH_LIMIT = 40 # How many of the latest statuses to pull per tag.
print(f"Initializing {CLIENT_NAME} Bot")
print("-----------------------------------------------")
print(f" > Connecting to {MASTODON_URL}")
client_id, client_secret = Mastodon.create_app(
CLIENT_NAME,
api_base_url = MASTODON_URL)
# Create client
mastodon = Mastodon(
client_id = client_id,
access_token = ACCESS_TOKEN,
client_secret = client_secret,
api_base_url = MASTODON_URL,
)
print(f" > Logging in as {USERNAME}.")
mastodon.log_in(
USERNAME,
PASSWORD
)
print(" > Successfully logged in")
print(" > Fetching account data")
account = mastodon.me()
notifications = mastodon.notifications(types=['mention'])
print(f" > Fetched account data for {account.acct}")
print("-----------------------------------------------")
print(" > Reading statuses to identify tootable statuses")
for notification in notifications:
if not notification.status.favourited and \
notification.status.account.acct != account.acct:
try:
print(f"- Boosting new toot by {notification.account.username} viewable at: {notification.status.url}")
mastodon.status_reblog(notification.status.id)
mastodon.status_favourite(notification.status.id)
except:
print(f"- Boosting new toot by {notification.account.username} did not work - going to the next toot.")
Overall, the setup is now complete, bringing your bot to life and making it fully operational. To launch your bot, you can run the scripts locally. Alternatively, as I will explain in an upcoming post, you have the option to streamline and automate this process. This can be done using many different tools - I used both AWS Lambda and GitHub Actions, but you can essentially tailor the choice to your specific needs and preferences.