Making a trivia quiz chatbot on Mastodon
Tags: bots, mastodon, mastodonapi, chatbots
Published: 2023-03-06 13:36:45

---
title: Making a trivia quiz chatbot on Mastodon
published: true
description: Bringing a 7 year old bot to the fediverse
tags: bots,mastodon,MastodonAPI,ChatBots
# cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jf4hoo0m8gl6o8ma90za.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-03-02 19:14 +0000
---
![A screenshot of the bot posting a country flag and me responding with the correct answer, followed by the bot acknowledging the answer and posting the leaderboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m15aqv1e6n8exokza1nc.png)

**TLDR: [Here's the finished code](https://glitch.com/edit/#!/what-capital?path=bots%2Fwhat-capital.js) | [Get in touch if you get stuck](https://stefanbohacek.com/contact/)**

---

I made the original [@what_capital](https://botwiki.org/bot/what_capital/) bot, which lets you play a trivia game where you identify capitals based on a country's flag, about seven years ago, for a [tutorial](https://stefanbohacek.com/blog/how-to-make-a-twitter-bot-with-node-js-and-cloud9-and-openshift/) that you'd no longer be able to follow. For one, the web-based IDE I used was sold to Amazon, and I'm not sure you can easily use it for free. Second, the hosting service I used seem to no longer offer a free plan. And finally, Twitter, whose API I used, [is in disarray](https://botwiki.org/blog/twitter-shutting-down-free-access-to-their-api-on-february-9/).

Yes, seven years is a really long time on the web.

But as some technologies and platforms disappear, new ones arise. And one that I am particularly excited about is the [fediverse](https://en.wikipedia.org/wiki/Fediverse). It's an open standard for making a better social web with roots going back to 2008. And the way many folks are being introduced to it is via the rise of [Mastodon](https://en.wikipedia.org/wiki/Mastodon_(social_network)), both a social media site, and software to make your own version, that's built on this standard.

And a fun way to explore the fediverse is using Mastodon's API to re-create my bot.

And for that, I am going to use my [Creative Bots](https://stefanbohacek.com/project/creative-bots/) project. You have a few options how to host it, but for this tutorial I am going to go with [Glitch](https://glitch.com/) for its ease of use. Note that you don't need an account to follow along, but you will need a [paid plan](https://glitch.com/pricing) to keep your bot running after we're done.

First, you will need to [set up an account for your bot](https://botwiki.org/resource/tutorial/how-to-make-a-mastodon-botsin-space-app-bot/). Any Mastodon instance will do. Note that the popular [botsin.space](https://botsin.space/) instance has a manual review process in place, so you might have to wait a bit if you decide to go with that one.

Once we have our account ready, [let's make a remix](https://glitch.com/edit/#!/remix/creative-bots) of the Creative Bots Glitch app.  

Next, we'll update our `.env` file (see the sidebar on the left) and add the following variables.

```
WHAT_CAPITAL_BOT_MASTODON_API_URL="https://botsin.space/api/v1/"
WHAT_CAPITAL_BOT_MASTODON_ACCESS_TOKEN="123456789-abcd"
```

![Screenshot from glitch.com showing how to add environmental variables](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v60hyfmswxvtdg7luhrw.png)

Replace botsin.space with the name of your bot's instance, if you're not using that one. For example, if your bot is on [mastodon.social](https://mastodon.social/), you will use:

```bash
WHAT_CAPITAL_BOT_MASTODON_API_URL="https://mastodon.social/api/v1/"
WHAT_CAPITAL_BOT_MASTODON_ACCESS_TOKEN="123456789-abcd"
```

So far, so good.

Now, back in the sidebar, open `bots/reply-mastodon.js`. This is a simple example of a bot that replies to messages sent to it, and we will expand on it to make our bot.

But before we write any code, we will need to prepare some data. Using the sidebar again, add a file called `data/capitals.js`.

![A screenshot showing how to add a new file in Glitch.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65sv6xu8n48bpzhed7iv.png)

You can get the contents from the [finished project here](https://glitch.com/edit/#!/what-capital?path=data%2Fcapitals.js).

Note that the JSON object with answers also contains description of each flag. More on that in a bit.

Now, let's update your bot's code to use the correct access token and API URL.

```js
const mastodon = new mastodonClient({
   access_token: process.env.WHAT_CAPITAL_BOT_MASTODON_ACCESS_TOKEN,
   api_url: process.env.WHAT_CAPITAL_BOT_MASTODON_API_URL
});
```

At this point, you can already try speaking with your bot. Go ahead and say hello!

After you have a bit of fun with your new bot, let's get back to writing some more code. All that's next will come before the `module.exports` section.

[Here's the finished code again so that you can check your progress.](https://glitch.com/edit/#!/what-capital?path=bots%2Fwhat-capital.js)

To start, we will need to load our `capitals.js` file, so let's do just that.

```js
const fs = require("fs"),
      helpers = require(__dirname + "/../helpers/helpers.js"),
      capitals = require(__dirname + "/../data/capitals.js"),
      mastodonClient = require(__dirname + "/../helpers/mastodon.js");
```

Note that I also added the `fs` module. This will be useful to keep track of a leaderboard, to make the game a bit competitive.

Note that the `.data` folder, which we will use, is a [built-in way for Glitch to store data for your app](https://help.glitch.com/kb/article/22-do-you-have-built-in-persistence-or-a-database/).

```js
const savedDataPath = __dirname + "/../.data/what_capital.json";

let savedData = {
  country: "",
  capital: "",
  scores: {},
};

if (fs.existsSync(savedDataPath)) {
  savedData = JSON.parse(fs.readFileSync(savedDataPath, "utf8"));
}

const saveData = () => {
  fs.writeFileSync(savedDataPath, JSON.stringify(savedData, null, 2), "utf8");
};

const updateScores = (user) => {
  if (savedData.scores.hasOwnProperty(user)) {
    savedData.scores[user] = savedData.scores[user] + 1;
  } else {
    savedData.scores[user] = 1;
  }
  saveData();
};

```

Great. Now we'll need a pair of functions that will handle picking a new question and verifying the answer.

```javascript
const pickNewCapital = () => {
  const capital = helpers.randomFromArray(capitals);
  const flagUrl = `https://static.stefanbohacek.dev/images/flags/${capital.country.replace(
    / /g,
    "_"
  )}.png`;

  savedData.capital = capital.capital;
  savedData.country = capital.country;
  saveData();

  helpers.loadImage(flagUrl, (err, imgData) => {
    if (err) {
      console.log(err);
    } else {
      let altText = capital.flag_description;

      if (capital.flag_description.length > 1000) {
        altText = capital.flag_description.slice(0, 2) + "...";
      }

      mastodon.postImage({
        status: "What is the capital of this country?",
        image: imgData,
        alt_text: `An unspecified country flag: ${altText}`,
      });
    }
  });
};

const checkAnswer = (answer) => {
  answer = answer
    .trim()
    .toLowerCase()
    .normalize("NFD")
    .replace(/\p{Diacritic}/gu, "");
  const correctAnswer = savedData.capital
    .toLowerCase()
    .normalize("NFD")
    .replace(/\p{Diacritic}/gu, "");
  return answer.includes(correctAnswer);
};
```

This is the really tricky part. People will likely include words that are not part of the answer in their message, like "Hmm", or "I think...". And some city names may contain special characters. To make a bot that's fun to interact with, we need to account for all the possible ways people will talk to it.

We could actually go a step further here, and use an array with acceptable variations of each city's name, maybe even the translation in its native language. There's an idea for a future update.

And going back to the code above, you will notice that I am using the flag description as an alt text for the image. This way your bot can be [enjoyed by everyone](https://stefanbohacek.com/blog/creative-bots-and-accessibility/).

```js
let altText = capital.flag_description;
```

Now we're ready to bring this all together inside the `reply` function of our bot object.

```js
let reply = "";

if (
  fullMessage.data.status.visibility === "public" ||
  fullMessage.data.status.visibility === "unlisted"
) {
  if (checkAnswer(messageText)) {
    updateScores(from);
    reply = `Yes, ${savedData.capital} is the capital of ${
      savedData.country
    }, correct! ${getLeaderboard()}`;
    pickNewCapital();
  } else {
    reply = "That doesn't seem correct, sorry! Or perhaps a new flag was posted?";
  }
} else {
  reply = "Sorry, do you mind responding publicly?";
}

mastodon.reply(fullMessage, reply);
```

And there you have it, your very own interactive game bot, running on the open web.

Feel free to browse more examples of bots I've made [using my starter project](https://stefans-creative-bots.glitch.me/), and you can find even more resources for creative botmakers over at [botwiki.org](https://botwiki.org/), and join the [botmakers.org](https://botmakers.org/) Slack group.

I hope you find this tutorial fun. If you have any questions or suggestions, feel free to [reach out](https://stefanbohacek.com/contact/).

Until next time!