Build your own Mastodon client
Tutorial – Mastodon
Creating your own clients to interact with your friends in the Fediverse is easy. A bit of Python and an off-the-shelf module will do the trick.
Although there are plenty of Mastodon [1] clients out there (as we saw in the prior installment [2]), sometimes you just want that something special to satisfy your needs. Fortunately, Mastodon's API is open and well-organized and there is a Python wrapper [3] that makes it even easier.
Registration
First, you need to install the Mastodon.py
wrapper. The easiest way to do this is by using pip
:
pip install Mastodon.py
or, if pip
complains about permissions:
sudo pip install Mastodon.py
Next you want to register your application with the Mastodon network. You can do this from the Mastodon website where you have opened your account. We talked about how to do this in Linux Magazine, issue 227 [4].
Or you could do it all from the comfort of your command line with something like what you see in Listing 1.
Listing 1
readtoots_register.py
01 #!/usr/bin/env python 02 03 from mastodon import Mastodon 04 05 Mastodon.create_app( 06 'readtoots', 07 scopes=['read'], 08 api_base_url = 'https://your.mastodon.server', 09 to_file = '.secrets' 10 )
Line 1 of Listing 1 just sets the interpreter you want to use to run this script (your default Python interpreter).
Line 3 pulls in the bits of the mastodon
module you need. Lines 5 through 10 registers the app, giving it a name the server can identify it by (readtoots
on line 6). On line 7, you tell the server what sort of actions it will be able to carry out (in this case only 'read'
– other actions are 'write'
, 'follow'
, and 'push'
). The api_base_url
on line 8 tells the application what instance of Mastodon you want to go through. This is the address of a Mastodon server – you would usually use the instance where you have your account for this. Finally, the to_file
argument tells readtoots_register.py
where to store the credentials information the server will send back down the line for the application.
Save the file as readtoots_register.py
, make it executable with
chmod a+x readtoots_register.py
and run it with:
./readtoots_register.py
After running it for the first time, nothing apparently happens, but you will find a new .secrets
file in your app's directory. If you look inside, you will see something similar to Listing 2.
Listing 2
.secrets
01 53eM1n9lyHR4nd0mUnUMB3r5vAN6G133TeR5X4g 02 KM0r340FdTh3p54M3A60nTnUkKN0WC0hweMigLcng6U 03 https://your.mastodon.server
The Mastodon.create_app()
function shown in Listing 1 not only registers the application, it also returns a client_id
(line 1 in Listing 2) and a client_secret
(line 2 in Listing 2) that you will be able to use to identify your application each time it has to interact with the Mastodon instance. It also kindly adds the address of the instance the app is registered with, making it super convenient as you will see later on.
You will only need to run readtoots_register.py
once, and, when you're done, you can crack on with readtoots.py
proper.
Reading Toots
Let's build a command-line Mastodon client that reads, by default, the toots of the account you log into, but that will also let you read toots from any federated account you pass as a parameter to the script.
As you will also need login credentials to access an account, you are going to need at least three options on the command line. As command line arguments tend to pile up as you add features, let's keep things organized and use the optparse
Python module to keep things tidy.
Take a look at Listing 3 to see how it all works.
Listing 3
readtoots.py
01 #!/usr/bin/env python 02 03 from mastodon import Mastodon 04 from optparse import OptionParser 05 06 if __name__ == '__main__': 07 08 parser = OptionParser () 09 parser.add_option ('-u', '--username', help = 'your email', dest = 'maUser') 10 parser.add_option ('-p', '--password', help = 'your password', dest = 'maPassword') 11 parser.add_option ('-a', '--account', help = 'account you want to read', dest = 'sosAccount', default = 'me') 12 13 (options, args) = parser.parse_args() 14 15 app=Mastodon ( 16 client_id = '.secrets' 17 ) 18 19 app.log_in ( 20 username = options.maUser, 21 password = options.maPassword, 22 to_file = '.token', 23 scopes = ['read'] 24 ) 25 26 if (options.sosAccount == 'me'): 27 maId = app.me ()['id'] 28 29 else: 30 maId = app.account_search (options.sosAccount)[0]['id'] 31 32 maToots = app.account_statuses (maId) 33 34 for status in maToots: 35 print ('==================== Status ' + str (status['id']) + ' ====================') 36 print (status['content'])
Once you have imported the modules you need (lines 3 and 4) and set up the OptionParser()
to manage your command line arguments (lines 8 through 13), it is time to activate your application (lines 15 through 17).
See how convenient this is: If you hadn't registered and stored the credentials and the address of the Mastodon instance in .secrets
, you would have had to hard code them in with the client_secret
, client_id
, and api_base_url
option when you instantiate the Mastodon
class.
Next, you log into your account (lines 19 through 24). This is not always necessary. In theory, you can have an application read public toots from public accounts without having to log in, but this only works on some Mastodon servers. Turns out mine is not one of them.
Whether logging in is necessary or not will depend on the version of the Mastodon software the server is running and how the administrator has configured it. To be on the safe side, always log in, and your application will not fail.
The logging in itself is straightforward: pass the username and password collected from the command line to Mastodon.py's log_in()
function and you will get an access token in return (which you can store in a file for when you need it – line 22).
For some reason not explained in Mastodon's API documentation, my server also required the application's scope as defined when registering it (see Listing 1, line 7). Oh well! I added it on line 23 of Listing 3, and everything worked.
Next you get the id
of the account you want to read. The id
is not the same as the name of the account (@someusername@some.mastodon.instance), but a unique numerical value assigned to each account. There is no easy way to discover an account's id on a Mastodon website, but you can discover what yours is with Mastodon.py
's me()
function (line 27).
The me()
function returns a Mastodon user
dictionary. A user
dictionary contains information about the account. It includes, among other things, the display_name
, the username
, when it was created, how many followers it has, and, yes, the account's id
.
In case the user has decided to peruse the statuses of another account, you can use the app.account_search()
to find the account passed on by the -a
option on the command line. As app.account_search()
returns a list of user
dictionaries, you have to pick the first one (line 30).
The account_statuses()
function returns a list of toot
dictionaries containing all the toots of an account identified by maId
(line 32). You then loop through them (lines 34 through 36), extract the content
field (line 36), and print it to the command line.
Save the file as readtoots.py
and make it executable with:
chmod a+x readtoots.py
You can then run it like this:
./readtoots.py -u your@email.com -p "Your secret password"
where your@email.com
is the email you used when you registered for your Mastodon account and "Your secret password"
is the password you use to log into your account.
This will output your account's 20 latest toots to the command line (Figure 1). It doesn't look pretty, but it works.
In the first run, you don't specify any account, so the application goes with the default ("me"
) and prints out the statuses of the account it is logged in to.
If you run readtools.py
like this:
./readtoots.py -u your@email.com -p"Your secret password" -a @kde@mastodon.technology
you will see the latest 20 toots from the KDE community account.
I have mentioned several times that, by default, all you get is the 20 latest statuses. You can push up that limit to a maximum of 40 by adding limit = 40
as an argument in account_statuses()
on line 32, but that is still a paltry feed if ever there was one. Who posts only 40 statuses and stops there? Incidentally, this is a limitation of the Mastodon API, not of Mastodon.py
. You can get past this limitation, however. Take a look at Listing 4.
Listing 4
readtoots.py (better version)
01 #!/usr/bin/env python 02 03 from mastodon import Mastodon 04 from optparse import OptionParser 05 06 if __name__ == '__main__': 07 08 parser = OptionParser () 09 parser.add_option ('-u', '--username', help = 'your email', dest = 'maUser') 10 parser.add_option ('-p', '--password', help = 'your password', dest = 'maPassword') 11 parser.add_option ('-a', '--account', help = 'account you want to read', dest = 'sosAccount', default = 'me') 12 13 (options, args) = parser.parse_args() 14 15 app=Mastodon ( 16 client_id = '.secrets' 17 ) 18 19 app.log_in ( 20 username = options.maUser, 21 password = options.maPassword, 22 to_file = '.token', 23 scopes = ['read'] 24 ) 25 26 if (options.sosAccount == 'me'): 27 maId = app.me ()['id'] 28 29 else: 30 maId = app.account_search (options.sosAccount)[0]['id'] 31 32 maMaxId = None 33 wannaRepeat = "Y" 34 35 while (wannaRepeat in "YyYesyesYeahyeah"): 36 37 maToots = app.account_statuses (maId, max_id = maMaxId) 38 39 for status in maToots: 40 print ('==================== Status ' + str (status['id']) + ' ====================') 41 print (status['content']) 42 43 maMaxId = status['id'] 44 print ('========================================') 45 print ("More?") 46 wannaRepeat = input()
The trick is using account_statuses()
's max_id
argument (line 37). All you have to do is put the for
loop inside a while
loop (lines 35 through 46) and collect the last status id after the for
loop exits (line 43). Then you ask the user if they want to continue (lines 45 and 46) and, if they say Yes (line 35), use the status id in account_statuses()
and start over.
When you run readtoots.py
now, it prints out the 20 latest toots and then prints More?; if the user enters Y, Yes, or Yeah, it prints out the next 20 toots and asks again. If the user types in No, the application exits (Figure 2).
Improvements
You will immediately notice that statuses come laced with HTML tags. To make them more readable, you could strip them out. Or, even better, use the HTML to format the text as the author intended. Modern terminal emulators accept colored, bold, and highlighted text, even emojis. Most also allow for "live" links the user can click.
Another thing is that images, videos, and other media files included in toots are lost. To be able to see them, you would have to design a client with a graphical interface, with boxes that display pictures and clips. Implementing a client with these features goes well beyond the scope of this article.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Fedora Asahi Remix 41 Available for Apple Silicon
If you have an Apple Silicon Mac and you're hoping to install Fedora, you're in luck because the latest release supports the M1 and M2 chips.
-
Systemd Fixes Bug While Facing New Challenger in GNU Shepherd
The systemd developers have fixed a really nasty bug amid the release of the new GNU Shepherd init system.
-
AlmaLinux 10.0 Beta Released
The AlmaLinux OS Foundation has announced the availability of AlmaLinux 10.0 Beta ("Purple Lion") for all supported devices with significant changes.
-
Gnome 47.2 Now Available
Gnome 47.2 is now available for general use but don't expect much in the way of newness, as this is all about improvements and bug fixes.
-
Latest Cinnamon Desktop Releases with a Bold New Look
Just in time for the holidays, the developer of the Cinnamon desktop has shipped a new release to help spice up your eggnog with new features and a new look.
-
Armbian 24.11 Released with Expanded Hardware Support
If you've been waiting for Armbian to support OrangePi 5 Max and Radxa ROCK 5B+, the wait is over.
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.