Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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:
- Building the code base that creates and posts the toots,
- Deploying the code to run automatically somewhere.
This post is about the first part, and I’ll share my methods for the deployment of the code in a third blog post.
Understanding the Architecture of My Mastodon Bot
In its current state, the bot does three different things:
- Boosting and favoriting posts that are tagged with
#pyladies' or
#rladies’. - Boosting and favoriting posts explicitly tagged by the bot.
- Promoting blog posts by R-Ladies and PyLadies from two curated lists (yes, those are the Awesome PyLadies and Awesome R-Ladies repositories!)
And this is what it looks like:
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.
Setting Up a Mastodon Bot Account
- Set up a Mastodon account for your bot – the botsin.space instance is highly recommended. It’s a dedicated instance for bots, and it’s easy to set up an account there. If you are not sure if your bot is a good fit, contact the maintainer. I did this for my bot because I wanted to make sure it was in line with the policies of the instance. Getting a response was really quick, and discussing open questions was great.
- Settings on Mastodon
- Upload Description
- Upload an image
- Upload a background image
- Mark your bot as “bot” (this is netiquette to make it clear to other users that they are interacting with a bot)
In practice it looks like this
Screenshot of what you can add once your account is active:
- Upload Description
- Upload an image and a background image
- Mark your bot as “bot” (this is netiquette to make it clear to other users that they are interacting with a bot)
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 Post
As you can see in the diagram above, each script follows a similar scheme:
- I authenticate with Mastodon
- Create the post
- Publish the post
Diagram showing the architecture of the Mastodon bots’ scripts. They typically follow the following sequence:
- Authenticate with Mastodon API
- (Reading helper data)
- Build post
- Publish post to Mastodon
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.
Authenticating With Mastodon
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:
- Go to the “Development” section in your botsin.space account
- Add an application name (it can be any 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.
- And then your application is successfully created (make sure it has read and write permissions!)
rladies_bot
was successfully created with the scopes “read write follow”. “Follow” is optional and not required for our purpose.
- And this is where you get your keys (make sure not to post them anywhere publicly – and if you do, renew them)
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()
Capture Feed and Create Post (and Publish Them)
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.
< details> < summary>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.")< !-- raw HTML omitted -->< !-- raw HTML omitted -->
What’s Next?
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.
References
- Corso, Frank: Deploying a Twitter bot with AWS Lambda
- Duggan, Mathew: Deploying a Mastodon bot with AWS Lambda
- Eden, Terence: Building a Mastodon bot
- Quirk, Joshua: Rebooster bot
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.