My Node-RED flow to pull the trending topics from the Mastodon API and publish a toot for social discoverability
My Node-RED flow to pull the trending topics from the Mastodon API and publish a toot for social discoverability

While you may have read that I love Mastodon, what you didn’t read me mention is that discoverability is not the same on an open platform. Commercial social media uses a series of fine tuned (and deceptive) algorithms to connect users to people they may know or show topics they may be interested in.

While there is harm in Facebook deciding for us who our friends should be or Twitter telling us what the news of the day is we should be able to leverage some of those same tools in an open social media platform. While we’re not yet building an algorithm for Mastodon we can build a simple trending topic bot to toot out what’s hot.

Baked into the platform already is the ability to generate and display the trending tags. You can see this on the home page of your instance either in advanced (tootsuite) mode or in the simplified view. What I have found however is that most users don’t see this and don’t pay attention to it. The sidebar component in the advanced view will cover this up (when clicking toots) and the apps for Mastodon don’t show this typically in their UI.

Solution – Call the Mastodon API with Node-RED

What we can do instead is tell users what the trending tags are. Our followers will get to see, on schedule, the top trending tag and we’ll also be telling followers federated from other instances what we’re discovering to be a hot topic. One thing I’m finding too is that this is also a good way to increase the usage of hashtags on our instance to help our own users get discovered – I’m seeing about a five percent increase of toots with tags (hashtags) applied.

Someone adding a trending toot to their favorites.

Since the UI doesn’t show the data we’re after to our users and our followers we can pull it from the Mastodon API with Node-RED and manipulate the information as we see fit. With Node-RED we’ll pull the data using an HTTP request node and this time craft a toot back to our server using the Mastodon Node-RED messaging nodes and some function node magic.

Calling the API

To call the API we’re going to start with the HTTP request node mentioned above. This is a simple task, though it requires us to have an Access Token from Mastodon to read and write to the Mastodon API and is done without a token, but we’ll need one later to write to the API. Luckily we’ve probably already generated one and if not the Mastodon developers have made it easy to generate API tokens in the user setup. See the screenshot below for the Mastodon API key setup page (Settings, Development).

Adding an application to Mastodon will give you an access token to use to call the API
Adding an application to Mastodon will give you an access token to use to call the API

Once you have the access token from your Mastodon instance you can start calling the API with the HTTP request node. You’ll be making a GET request to the API calling the trends function. This will give you back at least one trending tag if the server is not having issues but you could get up to three.

When we get the request back we want to keep it in JSON format to play with in Node-RED so make sure you update the Return option if it doesn’t default to “a parsed JSON object” as shown below. Once we are making the GET request to the proper API URL (yours will differ based on instance location) and keep what we pull back in JSON format in Node-RED we’re all set.

HTTP Request node in Node-RED built to call the Mastodon API and pull the trending tags

Copying the Top Tag

If we turned on debugging after this node we’d see that we get JSON structured data as we expect (note to debug properly we’d need to debug the entire message object, not just the msg.payload). And in those nodes that come back we have an array of trending tags as mentioned – we can get up to three trending tags from the API. What we want for our example here is the top trending tag and this rests at location msg.payload[0].name in the array of tags. If we wanted the second, we’d look for msg.payload[1].name for example.

Since this is all we care about, let’s move it to msg.payload and simplify things a little bit, we can forget the rest of the payload for our simple message. To do this we’ll use a change node in Node-RED to move the JSON attribute from the array to our payload. Now msg.payload is going to reflect the top trending tag – debug it to check and see.

Crafting our Toot

Now that we have isolated from the Mastodon API the top trending tag and turned it into our msg.payload we can start doing cool things with it like crafting a toot to Mastodon. To do this we’ll use our msg.payload as a variable and concatenate a string to it in JavaScript. By concatenating a string to a variable in JavaScript we can take the existing data and merge it with out own message.

In the Node-RED function node that comes next we’ll update our message by doing exactly that;

hashtag = "The top trending tag is #"+msg.payload+" this message automatically generated every 24 hours. #trending #automated";
msg.payload = hashtag;
return msg;

We created a new variable, said it’s equal to our message text and payload and returned it as the new payload. If you don’t know JavaScript that’s okay if this doesn’t make sense 100% – you can just change what’s in “quotes” in the code to meet the needs of your own instance’s message.

Moving our Payload to our Toot

Now that our msg.payload reflects our toot we just need to prep it for the sendMessage node coming later in the flow. If we left the data in msg.payload and didn’t move it to where the node expects the data our toot would simply read “undefined“. The Mastodon sendMessage node is expecting the data in msg.payload.text (as it can also accept an image, by full path name, as msg.payload.image). So we’ll use a simple change node to move our message from payload to payload.text;

Node-RED change node that moves our msg.payload to msg.payload.text
Let’s move the message to where the later node is expecting it.

Rate Limiting

I didn’t cover in this guide how to start the flow – we jumped into the API. I find that there are different tools, such as the simple startup trigger, or hacks such as using inject nodes to start a flow. The reader typically will use what they find is best after debugging and testing – what I’d like to focus on here is rate limiting instead.

Aside from thinking about starting your flow you should not offend your users – and the best way to do this is to control how often you publish a toot by calling the Mastodon API. Luckily we can use out of the box nodes to control this with a delay node later in the process no matter what the up-front portion of the flow looks like. I guess as a best practice I’d say to put this in (though we haven’t even discussed error handling!) just to control what you’re sending out.

You can do this by setting the delay node as I do below;

Node-Red Delay Node setup to Rate Limit toots sent to the Mastodon API
In our configuration we don’t allow more than one toot every 23 hours as we wanted a daily trending topic bot.

Sending our Toot

This is where we’ll need two things – the API endpoint we called prior in our HTTP request node and our Access Token generated from the Mastodon API developer page earlier. By putting both into the Mastodon sendMessage node we have everything we need to send our toot;

Writing to the Mastodon API with the sendMessage node in Node-RED

What we just need to do is ensure in our earlier step (before rate limiting) that we are moving the msg.payload value into msg.payload.text – this is what the sendMessage node is looking for.

Putting it Together – API to Toot

Here you go – if you wanted to you can copy and paste the below and import this example flow into your own Node-RED setup and start pulling the trending tags from the Mastodon API and tooting today! That’s right an entire copy of the flow is below for you to use:

[{"id":"3bed8894.2fb198","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"fc343196.ad3d3","type":"http request","z":"3bed8894.2fb198","name":"trends","method":"GET","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":270,"y":260,"wires":[["bfc019a2.f71c7"]]},{"id":"68e6dff7.7f64b","type":"inject","z":"3bed8894.2fb198","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"172800","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":140,"wires":[["c8c606fb.24b2f8"]]},{"id":"bfc019a2.f71c7","type":"change","z":"3bed8894.2fb198","name":"","rules":[{"t":"move","p":"payload[0].name","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":140,"wires":[["5c31a2bd.5d7394"]]},{"id":"5c31a2bd.5d7394","type":"function","z":"3bed8894.2fb198","name":"","func":"hashtag = \"The top trending tag is #\"+msg.payload+\" this message automatically generated every 24 hours. #trending #automated\";\nmsg.payload = hashtag;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":720,"y":140,"wires":[["c81a5aab.9c6c18"]]},{"id":"ab2592db.70e218","type":"sendMessage","z":"3bed8894.2fb198","name":"","access_token":"-","timeout_ms":"60000","api_url":"","x":780,"y":280,"wires":[[]]},{"id":"c81a5aab.9c6c18","type":"change","z":"3bed8894.2fb198","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.text","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":140,"wires":[["316a926f.fecde6"]]},{"id":"c8c606fb.24b2f8","type":"trigger","z":"3bed8894.2fb198","name":"","op1":"1","op2":"0","op1type":"str","op2type":"str","duration":"-24","extend":false,"units":"hr","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":190,"y":200,"wires":[["fc343196.ad3d3"]]},{"id":"316a926f.fecde6","type":"delay","z":"3bed8894.2fb198","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"23","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":1060,"y":200,"wires":[["ab2592db.70e218"]]}]