Creating a Telegram Bot with Python

Anderson Servat
9 min readFeb 17, 2024

--

Updates from the last year

In this article, I’ll delve into the process of creating a Telegram bot using Python, ensuring compatibility with both group and private chats.

Given the API updates that rendered the 2022 version obsolete, this 2023 guide aims to provide you with the latest method to keep your bot functioning smoothly.

Prerequisites

To start, you’ll need Python 3.11 installed on your system. I’ll be using Sublime as the IDE for its comprehensive Python support, but feel free to use any IDE of your choice. Additionally, ensure you have Telegram installed on either your mobile device or computer.

Step 1: Setting Up Your Bot

First, open Telegram and search for “BotFather” to create your bot, and press /start. This message will appears:

I can help you create and manage Telegram bots. 
If you're new to the Bot API, please see the manual.
You can control me by sending these commands:
/newbot - create a new bot
/mybots - edit your bots [beta]
Edit Bots
/setname - change a bot's name
/setdescription - change bot description
/setabouttext - change bot about info
/setuserpic - change bot profile photo
/setcommands - change the list of commands
/deletebot - delete a bot
Bot Settings
/token - generate authorization token
/revoke - revoke bot access token
/setinline - toggle inline mode
/setinlinegeo - toggle inline location requests
/setinlinefeedback - change inline feedback settings
/setjoingroups - can your bot be added to groups?
/setprivacy - toggle privacy mode in groups
Games
/mygames - edit your games [beta]
/newgame - create a new game
/listgames - get a list of your games
/editgame - edit a game
/deletegame - delete an existing game

After starting a chat with BotFather, use the /newbot command to initiate the creation process.

You’ll be prompted to name your bot; let’s call ours “anamenotusedyetbot” for this tutorial. Remember, your bot’s username must end with bot, such as anamenotusedyetbot.

If your chosen username is taken, try a variation until you find an available one.

Upon successfully creating your bot, BotFather will provide an access token.

This token is crucial for connecting your bot to your Python script.

Step 2: Installing Dependencies

Before diving into coding, you need to install the Python Telegram Bot library.

Open your terminal or command prompt and run:

pip install python-telegram-bot

Step 3: Writing Your Bot’s Code

Here’s how you can start setting up your script:

from typing import Final
TOKEN: Final = "your_bot_token_here"
BOT_USERNAME: Final = "@anamenotusedyetbot"

Import the necessary modules from the Python Telegram Bot library:

from telegram import Update, Application
from telegram.ext import CommandHandler, MessageHandler, filters, ContextTypes

Creating Command Handlers

Define async functions for your bot commands.

For instance, to handle the /start command, as first interaction of the user with the bot, create the following function:

To begin, our objective is to establish a /start command for our bot. Given the updates in the API, it's imperative that we employ asynchronous programming. Thus, we'll prefix our function definition with async to indicate its asynchronous nature. The function will be named start_command.

Within this function, we’re required to include parameters for both update, of the Update type, and context, of the ContextTypes.DEFAULT_TYPE.

The next step involves utilizing the await keyword, which is crucial for asynchronous execution.

We’ll then access the reply_text method through update.message to send a response back to the user.

The text passed into the reply_text method should be the message you wish the bot to deliver immediately after a user initiates interaction with it, either by clicking the start button in the Telegram UI or by typing the /start command.

For instance, the bot could greet the user with a message like “Hello, thanks for chatting with me.” This sets a welcoming tone for the interaction right from the beginning.

async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Hello! Thanks for chatting with me. I am Anamenotusedyetbot!')

Repeat this process for other commands like /help or custom commands you wish to implement.

Processing Messages

To process and respond to messages, define a function that handles incoming text.

For simplicity, we’ll create a basic response system:

def handle_response(text: str) -> str:
text = text.lower()

if "hi" in text:
return "Hey there!"

elif "how are you" in text:
return "I am good."

else:
return "I do not understand what you wrote."

Handling Messages

Our next step involves crafting a function responsible for processing incoming messages and ensuring the bot’s response is tailored to the context of the interaction, be it within a group chat or a private conversation.

To achieve this, we’ll introduce a new asynchronous function named handle_message.

Within this function, we’ll include parameters for update, of the Update type, and context, designated as ContextTypes.DEFAULT_TYPE.

The initial task is to determine the nature of the chat — group or private — by inspecting update.message.chat.type, which we'll store as a string variable named message_type.

Following this, we’ll capture the incoming message text from update.message.text and assign it to a variable named text.

To aid in debugging, a print statement will be crafted to log the sender’s user ID (update.message.chat.id) and the message content, providing visibility into the interactions the bot is managing.

The core functionality hinges on the bot’s ability to discern whether it’s operating within a group chat or a private conversation.

Specifically, in group settings, the bot will only respond when explicitly mentioned or addressed directly. This distinction is made by checking if the message includes the bot’s username.

If so, and the conversation is happening in a group, we’ll proceed to strip the bot’s username from the message to focus on the substantive text. This processed text is then fed into a handle_response function to generate an appropriate reply.

Should the bot not be directly invoked in a group chat, it will abstain from responding, exiting the function early.

Conversely, in private chats or when directly addressed in groups, the bot will employ the handle_response function to craft its reply based on the unaltered message content.

After determining the response, the bot will utilize an await statement to send the reply back to the user via update.message.reply_text, passing in the generated response.

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
message_type: str = update.message.chat.type
text = str = update.message.text

print(f'User ({update.message.chat.id})in {message_type}: "{text}"')

if message_type == 'group':
if BOT_USERNAME in text:
new_text: str = text.replace(BOT_USERNAME, '').strip()
response: str = handle_response(new_text)
else:
return
else:
response: str = handle_response(text)

print('Bot:', response)
await update.message.reply_text(response)

In the final step of setting up our bot, it’s essential to include a dedicated function for error handling to ensure robustness and ease of debugging. Thus, we’ll introduce an asynchronous function named handle_error.

This function will be tasked with capturing any errors encountered during the bot’s operation. To accomplish this, it requires parameters for both update, of the Update type, and context, specified as ContextTypes.DEFAULT_TYPE, which we'll reuse from our previous implementations.

Within the handle_error function, our primary action will be to log errors. We'll utilize a print statement designed to format the message dynamically, indicating that an error has occurred. This message will include the specific error encountered, extracted from context.error, providing insight into the issue for troubleshooting purposes.

By integrating this error logging function, we’ve laid down all critical components of our bot’s logic, ensuring it not only responds to messages effectively but also handles any unforeseen errors gracefully.

async def error(update: Update, context: ContextTypes.DEFAULT_TYPE):
print(f'Update {update} caused error {contex.error}')

Step 4: Bringing It All Together

To bring all the components of our bot together, we start with a conditional statement to check if the script is the main entry point of the application.

This is done by verifying if __name__ is equal to '__main__'. Inside this block, we initiate the assembly of our bot's functionalities.

First, we instantiate our bot application by assigning app to Application.builder(), passing our authentication token to it and then invoking the .build() method. This sets the foundation for our bot's operation.

Next, we proceed to register various handlers with the app object. This includes adding command handlers for specific commands like /start, /help, and any custom commands we wish to implement.

Each command is associated with its respective function without the parentheses, ensuring these commands are correctly recognized and handled by our bot. It’s beneficial to annotate these sections with comments, like # Commands, for better organization and readability.

Following the command handlers, we introduce a message handler to process text messages. This is achieved by using app.add_handler() with a MessageHandler configured to filter for text messages, and it's directed to our handle_message function.

Subsequently, we establish an error handler to log any exceptions or issues that occur during the bot’s runtime. This is simply done by calling app.add_error_handler() and passing the error logging function.

The final step in our setup involves initiating polling, which continuously checks for new messages or commands that need processing. This is configured via app.run_polling(), where we can optionally specify the polling interval to control how frequently the bot checks for updates. For illustrative purposes, we set this interval to three seconds.

To enhance user feedback and debugging, we include print statements indicating when the bot is starting and when it begins polling for messages. These statements, such as Starting bot and Polling, provide immediate console feedback, reassuring the developer that the bot is active and monitoring for user interactions.

This comprehensive setup ensures that our bot is fully operational, ready to respond to commands, process messages, and handle errors efficiently, all while providing clear feedback on its status and activity.

if __name__ == '__main__':
print('Starting bot...')
app = Application.builder().token(TOKEN).build()

# Add command handlers
application.add_handler(CommandHandler("start", start_command))
application.add_handler(CommandHandler("help", help_command))
application.add_handler(CommandHandler("custom", custom_command))

# Message handler for all text messages
application.add_handler(MessageHandler(filters.TEXT, handle_message))

# Errors
app.add_error_handler(error)
# Polls
print('Polling...')
app.run_polling(poll_interval=5)

Step 5: Customizing Your Bot in Telegram

Before running our bot, we must first define its commands within the Telegram app itself.

To do this, we return to the BotFather interface.

Here, we initiate the process by inputting /help to refresh our memory on the available commands.

The command of interest for us next is /setcommands, which allows us to define the bot's commands directly within the Telegram interface.

It’s not necessary to physically click on the command options presented by BotFather; typing /setcommands directly into the chat suffices. I prefer navigating through the options provided, so I'll select /setcommands from the list.

Upon selection, BotFather might momentarily restrict selection, but after a brief pause, it allows us to choose our bot, in this case, “anamenotusedyetbot”.

Our task then is to list the commands our bot supports. As per our script, we’ve implemented three primary commands: /start, /help, and a custom command.

For each command, we need to document it and provide a brief description. For instance, for /start, we could describe it as "Initiates interaction with the bot".

Following this, the /help command could be documented as "Provides assistance and usage instructions for anamenotusedyetbot."

Lastly, our custom command needs to be listed along with a description, such as “Executes a specific function unique to our bot.”

Documenting these commands is not strictly necessary, but it significantly enhances the user experience.

It introduces clarity and ease of use, as BotFather generates helpful prompts for these commands, guiding users on how to interact with the bot more effectively.

Conclusion

You’ve now created a basic Telegram bot capable of responding to specific commands and messages.

This tutorial covered the foundational steps, from setting up your bot with BotFather to coding its functionality in Python.

Remember, the Python Telegram Bot library is versatile, allowing for much more complex bots with custom behaviors and interactions.

As the Telegram API evolves, updates may be necessary.

Stay tuned for potential 2024 updates, and consider subscribing if you’re interested in more Python and Telegram bot tutorials. Happy coding, and see you in the next guide!

--

--

Anderson Servat
Anderson Servat

Written by Anderson Servat

ENTP, dyslexic. Paralegal, Texas Realtor. Back-end Developer. I was a Brazilian attorney, stockbroker, and clown.

Responses (2)