It doesn’t get much easier than this
I don’t remember the last time I encountered a person who uses zero messaging services. Some people use WhatsApp or Signal, some use Twitter DMs or LinkedIn messages, others again use Instagram or TikTok.
A few friends of mine have zero social media, but even they use good old SMS. Messaging is important.
I have a few messengers on my devices, but my favorite one is definitely Telegram. It’s nice to use, you can participate in large chat groups, privacy is pretty well ensured, you can follow people’s public channels, and — best of all — it has incredible support for GIFs and silly gimmicks.
Telegram is basically like WhatsApp but two years ahead in terms of development. And it’s independent, without the vibes of a big tech behemoth like Meta.
The reason why I wanted to build a Telegram bot was, to put it simply, the war in Ukraine. In Eastern Europe, Telegram is the most-used messenger (these guys have good taste!), and I’m part of a network of volunteers and NGOs who are helping civilians who are affected by the war get access to basic stuff like food, medical supplies, and psychological support.
A Telegram bot would serve as a lifeline for affected individuals, and would help us streamline our work so that the things they need arrive as fast as possible.
At the beginning of the war, we had set up a very basic Telegram bot which integrates with a backend customer support system. This meant that all members of our team were able to access the chats with the bot and work on getting people what they need.
This was good enough in the early days because every person’s situation was individual and complex, and needed immediate human attendance. De facto it wasn’t a real bot, it was a human customer support system of sorts.
As the war dragged on, people’s demands became more and more similar. Many people had found a safe place to live but needed financial assistance to buy food or repair their houses. These weren’t complex decisions anymore but relatively straightforward courses of action.
And so the engineer in me awoke and said, Let’s automate this!
First, you’ll need to contact the Telegram BotFather. If you have a Telegram account, just search for BotFather and press
/start. This is what you’ll see:
One then creates a new bot with, you guessed it,
/newbot. The BotFather will ask for a name for this bot (which must end with “bot”), and give you a token for the bot.
This token is a sequence of letters and numbers, and it’s your responsibility to keep it safe. Anyone who has the token can control the bot, so keep it away from prying eyes.
/setdescription, one can set the description which appears before a user presses
/start. It might contain basic instructions about how to use the bot.
/setabouttext, one changes the text that appears on the bot page. It’s like your Telegram status line. And with
/setuserpic one changes the profile photo.
This is what the result looked like (sorry about the shitty bot name; it’s a prototype!):
Once this is done, one is ready to use Python and work on the internal logic of the bot!
There are several Python packages which make writing Telegram bots easy as a charm. There are packages for other languages, too. But Python is my everyday programming language, so I decided to stick with it.
I chose the AIOGram package simply because there seemed to be enough tutorials out there to help me get started with it. Other packages might be more sophisticated, but I wanted something that is easy enough that I can some basic code in a couple of afternoons at most.
After creating a new Python file, I populated the header with the pieces of AIOGram that I would need, and some basic information about the bot:
First things first, I have changed the bot token in this file for safety reasons. I’m not giving my control of the bot to the whole internet 😯
The pieces from AIOGram are the
Bot which helps interact with the Telegram bot API, the
Dispatcher which handles all messages, the
executor which makes sure that the status of the messages is checked regularly, and the
types of objects that we might need to control the user experience.
types we’ll need the
ReplyKeyboardMarkup which adds a custom keyboard for the user’s responses, and the
KeyboardButton which specifies which buttons will go into a custom keyboard.
In principle there are many more parts to AIOGram, and more sophisticated keyboards. There’s also an internal memory handler, which should work much more efficiently than my
answers =  array which I started to save the users’ responses.
But I wanted to write something quickly, and our bot doesn’t get bombarded with thousands of daily requests. So this is what I’m going with for now.
Many people using the bot prefer to talk in Ukrainian. So the first question our bot will ask is which language they prefer. The users are able to select a language, like so:
In my code, I need to create the custom keyboard displayed above, and I need to tell the bot to ask about the language as soon as the user presses
/start. This is what this looks like:
You can see the different keyboard buttons, and how this is put together in the
ReplyKeyboardMarkup. The option
resize_keyboard=True ensures that it always looks good, and the option
one_time_keyboard=True ensures that it disappears once it’s no longer needed.
One can chain the commands
.add(lang1).add(lang2).add(lang3) to make the buttons appear underneath one another, or one can write
.add(lang1,lang2,lang3) to make the buttons appear next to one another.
Bot messages are initiated by using the
message_handler in a function decorator. One can reply to commands, or to specific messages.
I have also added a
help message which reacts to the command
/help just in case a user is unsure what they’re doing or accidentally started using the bot.
If the user has selected English after the language prompt, we next ask what they need. We thus need a new keyboard, and we need a message handler which reacts to the
English answer. This is what this looks like:
Of course, the same happens if the user selects Ukrainian as a language — the conversation will just be in Ukrainian.
The silly emojis in the keyboard buttons are not a gimmick. During testing I soon realized that the question What do you need? appears any time the user might mention English in an answer, now or later. In the worst case, this would make the bot run in loops and confuse the user, which is obviously not what I want.
I figured that it’s quite unlikely that a user might reply English 👍 in any answer when they’re typing free text, so this is what entered the keyboard button and the
regexp which triggers the question What do you need?
Ditto for all other keyboard buttons and triggered questions.
There are surely more elegant solutions for this, but I wanted a quick and dirty version that works.
From this question and beyond, I’m also storing all responses in an array called
answers because sometimes the bot needs to combine different pieces of information that the user has given in order to direct them to the right NGOs that would fulfill their needs.
The logic continues like that (overall the script is some 600 lines long), but I’ll spare you from the rest.
Now, all we need to do is run the script. This is as easy as entering
in your terminal, of course while swapping out my file name with yours.
There’s only one problem. My computer is old, doesn’t always have an optimal response time, and I switch it off at night. This is not ideal for an emergency bot which should be available 24/7.
So instead I deployed the bot on a cloud service. I went with Heroku because that’s where we handle our backend software as well. But you can use whichever cloud service you prefer.
In Heroku, I need to create a new project, add the Python script, set the buildpack to Python, and add the line of code above so that Heroku knows how to run the script. I won’t go into detail about doing this because it varies from one cloud service to another, and because their documentation is usually pretty good.
I’m using the rather limited capabilities of the free version of Heroku because it’s still a prototype. So my bot still isn’t responsive at all times, as the curious reader might have noticed, but it’s better than running it on my laptop.
Once we deploy these changes to our main bot which people are using, and integrate it with our backend system, we’ll have a lot less work to do — the script will do all the redundant tasks, and we can intervene when the complexity of a person’s situation makes it necessary. And the bot will be more responsive, because unlike humans, it won’t need to sleep and take breaks.
Many people use messengers, but not so many people know how to automate conversations when that’s needed.
This is one of the many privileges that software developers enjoy. We interact with software just like any other human, but we also have enough in-depth understanding of it that we can build on top of it.
For the bot that we built in response to the war, I hope that it saves our team a lot of time and accelerates the time it needs to get help for our users.
It’s not the most sophisticated chatbot out there, but it gets the job done. That’s what counts.
When I have more time on my hands, I might build something like this bot for other areas in the world. Ukraine isn’t the only country in grave distress; my thoughts are on Pakistan, Afghanistan, and Ethiopia, among many others.
May the future be fast, automated, and helpful.
Become a Medium member for full access to my content.