Build your own Mastodon client

Tutorial – Mastodon

Article from Issue 230/2020
Author(s):

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.

Figure 1: Outputting toots to the command line using your custom-made Mastodon client.

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).

Figure 2: Scroll through the whole shebang with the new and improved readtoots.py!

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

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Social Skills

    Creating a custom application that toots text to Mastodon (the Fediverse's version of Twitter) is simple and straightforward. But we can mix it up by adding images and video, scheduling posts, and changing privacy settings.

  • Tutorial – Fediverse

    If you're looking for social media options where the user has more control, you'll find a range of options to explore in the Fediverse, including the popular Mastodon.

  • Mastodon Clients Post Line

    The open and simple Mastodon API makes it easy to create applications to interact with this federated microblogging platform. Here are some of the clients that the community has come up with and how you can use them.

comments powered by Disqus

Direct Download

Read full article as PDF:

Price $2.95

News