DEV Community

Gurkirat Singh
Gurkirat Singh

Posted on • Edited on

Making Telegram Bots with Python

Course Outline

Prerequisite Knowledge and Requirements

  1. Basic python knowledge
  2. Telegram Account

About Course

In this course, you will start creating a bot from scratch, discovering all possible settings to utilize all power of Bot API and creating a real-world application. All this by using Python and Telegram Bot

Codes / Projects

All the snippets and projects related to this course will be found in projects directory

NOTE : If you will be making any educational project regarding this course, I would like to encourage you to send a PR regarding the same, here

What are Bots

  • Simple telegram accounts operated by an application
  • Not actually designed for chatting
  • Can be used to simplify the task
    • Teach
    • Play
    • Search
    • Broadcast
    • Remind
    • Connect
  • Users can interact with them via
    • Message
    • Emoticons
    • Commands
    • Inline requests

Setting up Telegram Bot

  1. Open telegram and search for BotFather
  2. Send /start command to start and associate the account with yours
  3. Send /help command to show the help
  4. Send /newbot command and follow the instruction
  5. Send /mybots command to list your bot, select the desired bot
  6. Click on API Token

Understanding Other Bot Settings

  1. Send /mybots command to list your bot, select the desired bot
  2. Click on "Bot Settings" and you will see various options.
  3. Click on "Inline Mode". If you want the bot to accept inline messages beyond the commands.
  4. Now you can click on Back to Bot button and click on Edit Bot button. Here you can customize your bots like setting a bot description (what it does, what it is used for etc), setting a bot about and profile picture.

NOTE: While changing the profile picture, botfather expects you to upload a valid image file.

Limitation of the Bot

  1. Only user can initiate the chat
  2. The download limit is 20MB and upload limit is 50MB only
  3. Send messages limit is capped up to 30 msg per second. However, you can ask @botsupport to increase it.

With this limitation, comes another power, bots can edit own messages and another's messages in the channel where the bot has admin rights

Getting messages from Telegram

There are two ways to get user messages from telegram.

Polling

It's a way of asking Telegram every X seconds whether any new message has come, and receive messages or asks later from telegram.

Web Hooks

It's a way in which telegram sends all updates to the URL that has been configured to accept messages as soon as the user sends new messages.

HTTP Interface of Bots API

Documentation: https://core.telegram.org/bots/api

Available Method: https://core.telegram.org/bots/api#available-methods

Base URL: https://api.telegram.org/bot[BOT_API_KEY]/[methodName]

Getting bot description

Method Name: getMe

In my case,

{ "ok": true, "result": { "id": 920296790, "is_bot": true, "first_name": "TELE BOT", "username": "tel_ebot", "can_join_groups": true, "can_read_all_group_messages": false, "supports_inline_queries": false } } 
Enter fullscreen mode Exit fullscreen mode

Getting new messages

Available Methods: https://core.telegram.org/bots/api#getting-updates
Method Name: getUpdates

In my case

{ "ok": true, "result": [ { "update_id": 643663200, "message": { "message_id": 3, "from": { "id": 517852228, "is_bot": false, "first_name": "Gurkirat", "last_name": "Singh", "username": "username_of_the_sender", "language_code": "en" }, "chat": { "id": 517852228, "first_name": "Gurkirat", "last_name": "Singh", "username": "username_of_the_sender", "type": "private" }, "date": 1586779436, "text": "hello" } } ] } 
Enter fullscreen mode Exit fullscreen mode

More methods on Get Updates: https://core.telegram.org/bots/api#getting-updates

Sending Messages to the User

Method Name: sendMessage

In this, you will see me creating an echo bot that will be listening to all the messages sent by the user. I will then read the last message (-1 index in the python list) and send the same message with the format "You send me "{MESSAGE_TEXT}"" message.

The code is self-explanatory, and I have added some comments. However, if you couldn't get anything, leave a comment

import urllib.request as request from urllib.error import HTTPError from http.client import HTTPResponse from typing import Dict, List, Union import json from datetime import datetime import signal import os signal.signal(signal.SIGINT, signal.SIG_DFL) class TelegramEcho: def __init__(self, TG_KEY: str): self.TG_URL = "https://api.telegram.org/bot{key}/{method}" self.TG_KEY = TG_KEY self.__last = None self.__last_time = None pass def run(self): """ method to handle the incoming message and the send echo message to the user """ while True: try: # getting the incoming data  incoming = self.__handle_incoming() # checking if incoming message_id is same as of last, then skip  if self.__last == incoming["message"]["message_id"]: continue else: self.__last = incoming["message"]["message_id"] # adding more validation to prevent messaging the last message whenever the polling starts  if not self.__last_time: self.__last_time = incoming["message"]["date"] continue elif self.__last_time < incoming["message"]["date"]: self.__last_time = incoming["message"]["date"] else: continue # finally printing the incoming message  self.__print_incoming(incoming) # now sending the echo message  outgoing = self.__handle_outgoing( incoming["message"]["chat"]["id"], incoming["message"]["text"]) # finally printing the outgoing message  self.__print_outgoing(outgoing) pass except (HTTPError, IndexError): continue pass pass def __handle_incoming(self) -> Dict[str, Union[int, str]]: """ method fetch the recent messages """ # getting all messages  getUpdates = request.urlopen( self.TG_URL.format(key=self.TG_KEY, method="getUpdates")) # parsing results  results: List[Dict[str, Union[int, str]]] = json.loads( getUpdates.read().decode())["result"] # getting the last error  return results[-1] def __print_incoming(self, incoming: Dict[str, Union[int, str]]): """ method to print the incoming message on console """ print("[<<<] Message Recieved on %s" % datetime.fromtimestamp( incoming["message"]["date"]).strftime("%Y-%m-%d %H:%M:%S")) print("\tText: %s" % incoming["message"]["text"]) print("\tFrom: %s" % incoming["message"]["from"].get("first_name", "") + " " + incoming["message"]["from"].get("last_name", "")) print("\tMessage ID: %d" % incoming["message"]["message_id"]) print("-" * os.get_terminal_size().columns) pass def __handle_outgoing(self, chat_id: int, message_txt: str) -> Dict[str, Union[int, str]]: """ method to send the echo message to the same chat """ # making the post data  _data: Dict[str, Union[int, str]] = { "chat_id": chat_id, "text": "You sent me \"{MESSAGE_TEXT}\"".format(MESSAGE_TEXT=message_txt) } # creating the request  _request: request.Request = request.Request( self.TG_URL.format(key=self.TG_KEY, method="sendMessage"), data=json.dumps(_data).encode('utf8'), headers={"Content-Type": "application/json"}) # sending HTTP request, for sending message to the user  sendMessage: HTTPResponse = request.urlopen(_request) result: Dict[str, Union[int, str]] = json.loads( sendMessage.read().decode())["result"] return result def __print_outgoing(self, outgoing): """ method to print outgoing data on the console """ print("[>>>] Message Send on %s" % datetime.fromtimestamp( outgoing["date"]).strftime("%Y-%m-%d %H:%M:%S")) print("\tText: %s" % outgoing["text"]) print("\tFrom: %s" % outgoing["from"]["first_name"]) print("\tMessage ID: %d" % outgoing["message_id"]) print("-" * os.get_terminal_size().columns) pass pass if __name__ == "__main__": tg = TelegramEcho("YOUR API KEY") tg.run() 
Enter fullscreen mode Exit fullscreen mode

Make sure you add the API Key before running this code. After running the above code, you will get the output like below,

Telegram Bot Libraries

As you have seen, telegram provides a plethora of methods and APIs to handle the bot through any language, the open-source community has developed a python client using these API under the hood to increase developer's productivity.

The one I will be using is python-telegram-bot. While making this course, it has approx. 10k stars, 38 open issues and last commit was made 2 days ago

Installing the Module

# via pip pip install -U python-telegram-bot # via conda conda install -c conda-forge python-telegram-bot 
Enter fullscreen mode Exit fullscreen mode

Glance of the Usage

In this I will be implementing get_me method, returning the bot details

from telegram import Bot # initializing the bot with API bot = Bot("API KEY") # getting the bot details print(bot.get_me()) 
Enter fullscreen mode Exit fullscreen mode

How fascinating, the developers encapsulated all the backend, letting other developers write neat and clone. It not only provides neat code but also makes payment processing, handlers, and other more features even beyond sending HTTP Requests

The complete documentation of python-telegram-bot: https://python-telegram-bot.readthedocs.io/en/stable/

Using Handlers on Commands

I hope you would have created some commands, now let's make the bot handle those

The library python-telegram-bot provides various Handlers from which I will be using CommandHandler

from telegram.ext.commandhandler import CommandHandler from telegram.ext.updater import Updater from telegram.ext.dispatcher import Dispatcher from telegram.update import Update from telegram.ext.callbackcontext import CallbackContext from telegram.bot import Bot # initializing an updator # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.Updater updater = Updater("API KEY", use_context=True) # getting the dispatcher required to handle the command /start and send message back to the user # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.Dispatcher dispatcher: Dispatcher = updater.dispatcher def start(update: Update, context: CallbackContext): """ the callback for handling start command """ # getting the bot from context  # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html#telegram-bot  bot: Bot = context.bot # sending message to the chat from where it has received the message  # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html#telegram.Bot.send_message  bot.send_message(chat_id=update.effective_chat.id, text="You have just entered start command") # register a handler (here command handler) # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.Dispatcher.add_handler dispatcher.add_handler( # it can accept all the telegram.ext.Handler, CommandHandler inherits Handler class  # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html#telegram-ext-commandhandler  CommandHandler("start", start)) # starting polling updates from Telegram # documentation: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.Updater.start_polling updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Posting HTML Message

In this I will show how to send an HTML formatted message to the user. I will be using telegram.ParseMode.HTML parser for with send_message method.

Updating just one send_message line in above code

from telegram.ext.commandhandler import CommandHandler from telegram.ext.updater import Updater from telegram.ext.dispatcher import Dispatcher from telegram.update import Update from telegram.ext.callbackcontext import CallbackContext from telegram.bot import Bot from telegram.parsemode import ParseMode updater = Updater("API KEY", use_context=True) dispatcher: Dispatcher = updater.dispatcher def start(update: Update, context: CallbackContext): """ the callback for handling start command """ bot: Bot = context.bot # Added HTML Parser to the existing command handler  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.parsemode.html#telegram.ParseMode.HTML  bot.send_message( chat_id=update.effective_chat.id, text= "Hello User, You have used <b>start</b> command. Search about developer on google, <a href='https://www.google.com/search?q=tbhaxor'>@tbhaxor</a>", parse_mode=ParseMode.HTML, ) dispatcher.add_handler(CommandHandler("start", start)) updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

The message user will receive will be the rendered HTML you have passed in text argument

Using Keyboards to Build Menus

In this, you will embrace the power of another tool provided by telegram, Keyboard. Telegram has two types plain keyboard and inline, the first is attached to the group or chat. The second one is attached to the message

Simple Keyboard

In this, I will be using telegram.ReplyKeyboardMarkup to add new keyboard to the chat and telegram.ReplyKeyboardRemove to remove the keyboard from the chat

from telegram.ext.updater import Updater from telegram.update import Update from telegram.ext.callbackcontext import CallbackContext from telegram.ext.commandhandler import CommandHandler from telegram.replykeyboardmarkup import ReplyKeyboardMarkup from telegram.replykeyboardremove import ReplyKeyboardRemove from telegram.ext.messagehandler import MessageHandler from telegram.ext.filters import Filters updater = Updater("API KEY", use_context=True) def start(update: Update, context: CallbackContext): """ method to handle the /start command and create keyboard """ # defining the keyboard layout  kbd_layout = [['Option 1', 'Option 2'], ['Option 3', 'Option 4'], ["Option 5"]] # converting layout to markup  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.replykeyboardmarkup.html  kbd = ReplyKeyboardMarkup(kbd_layout) # sending the reply so as to activate the keyboard  update.message.reply_text(text="Select Options", reply_markup=kbd) def remove(update: Update, context: CallbackContext): """ method to handle /remove command to remove the keyboard and return back to text reply """ # making a reply markup to remove keyboard  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.replykeyboardremove.html  reply_markup = ReplyKeyboardRemove() # sending the reply so as to remove the keyboard  update.message.reply_text(text="I'm back.", reply_markup=reply_markup) pass def echo(update: Update, context: CallbackContext): """ message to handle any "Option [0-9]" Regrex. """ # sending the reply message with the selected option  update.message.reply_text("You just clicked on '%s'" % update.message.text) pass updater.dispatcher.add_handler(CommandHandler("start", start)) updater.dispatcher.add_handler(CommandHandler("remove", remove)) # adding the message handler with filter to handle the Option [0-9] regex input # documentation for MessageHandler: https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.messagehandler.html # documentation for Filter: https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html#telegram.ext.filters.Filters updater.dispatcher.add_handler(MessageHandler(Filters.regex(r"Option [0-9]"), echo)) updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Inline Keyboard

In this I will be using telegram.ext.CallbackQueryHandler which returns a telegram.CallbackQuery when user presses an telegram.InlineKeyboardButton

from telegram import InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext.updater import Updater from telegram.ext.commandhandler import CommandHandler from telegram.ext.callbackqueryhandler import CallbackQueryHandler from telegram.callbackquery import CallbackQuery from telegram.ext.callbackcontext import CallbackContext from telegram.update import Update from telegram.message import Message import sys # creating updater updater: Updater = Updater("API KEY", use_context=True) def error(update: Update, context: CallbackContext): """Log Errors caused by Updates.""" sys.stderr.write("ERROR: '%s' caused by '%s'" % context.error, update) pass def start(update: Update, context: CallbackContext): """ callback method handling /start command """ # creating list of input buttons  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.inlinekeyboardbutton.html  keyboard = [[ InlineKeyboardButton("Option 1", callback_data='1'), InlineKeyboardButton("Option 2", callback_data='2') ], [InlineKeyboardButton("Option 3", callback_data='3')]] # creating a reply markup of inline keyboard options  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.inlinekeyboardmarkup.html  reply_markup = InlineKeyboardMarkup(keyboard) # sending the message to the current chat id  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html#telegram.Message.reply_text  update.message.reply_text('Please choose:', reply_markup=reply_markup) pass def button(update, context): """ callback method handling button press """ # getting the callback query  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html  query: CallbackQuery = update.callback_query # CallbackQueries need to be answered, even if no notification to the user is needed  # Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html#telegram.CallbackQuery.answer  query.answer() # editing message sent by the bot  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.callbackquery.html#telegram.CallbackQuery.edit_message_text  query.edit_message_text(text="Selected option: {}".format(query.data)) # adding listeners updater.dispatcher.add_handler(CommandHandler('start', start)) # handling /start command updater.dispatcher.add_handler(CallbackQueryHandler(button)) # handling inline buttons pressing updater.dispatcher.add_error_handler(error) # error handling  # started polling updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Sending Force Replies

Upon recieving messages from user, telegram bot will display a reply interface to the user like if user has selected the bot message to reply back. This could be used in creating step-by-step user friendly interface.

I will be using telegram.ForceReply to send force reply

from telegram.forcereply import ForceReply from telegram.ext.filters import Filters from telegram.ext.updater import Updater from telegram.ext.messagehandler import MessageHandler from telegram.ext.callbackcontext import CallbackContext from telegram.update import Update updater = Updater("API KEY", use_context=True) def echo(update: Update, context: CallbackContext): # sending the force reply to the user  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.forcereply.html  update.message.reply_text(reply_markup=ForceReply(selective=True), text="Reply to this message") pass updater.dispatcher.add_handler(MessageHandler(Filters.text, echo)) updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Chat Action

Now suppose you created a bot that processes some files / information that could take some time. The user might think bot is broken and close it. The telegram provides very cool option ChatActions to send user messages immediately after his/her submission.

I will be using telegram.Bot.send_chat_action along with telegram.ChatAction

from telegram.bot import Bot from telegram.update import Update from telegram.ext.updater import Updater from telegram.ext.callbackcontext import CallbackContext from telegram.ext.messagehandler import MessageHandler from telegram.ext.filters import Filters from telegram.chataction import ChatAction from time import sleep updater = Updater("API KEY", use_context=True) def echo(update: Update, context: CallbackContext): # sending the chat action, under the name of bot it will show Typing...  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.chataction.html  context.bot.send_chat_action(chat_id=update.effective_chat.id, action=ChatAction.TYPING) # simulating some long processing  sleep(3) # sending reply when it's done  update.message.reply_text(text="Hey ya!! You sent me '%s'" % update.message.text) updater.dispatcher.add_handler(MessageHandler(Filters.text, echo)) updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Deep Linking

Telegram also supports deeplinking. It helps to create a refereal system to promote your bot or products via bot.

I will be using telegram.utils.helpers.create_deep_linked_url to create a deep link

from telegram.bot import Bot from telegram.utils.helpers import create_deep_linked_url from telegram.ext.commandhandler import CommandHandler from telegram.ext.callbackcontext import CallbackContext from telegram.update import Update from telegram.ext.updater import Updater import re updater = Updater("API KEY", use_context=True) def generate(update: Update, context: CallbackContext): """ method to create a deep link and send it to the current user for sharing """ # generating a sharable link with the payload  # documentation: https://python-telegram-bot.readthedocs.io/en/stable/telegram.utils.helpers.html#telegram.utils.helpers.create_deep_linked_url  url = create_deep_linked_url(context.bot.get_me().username, update.message.chat.username) update.message.reply_text(text="Share it with your friends: %s.\n Copy the link and share it with them" % url) pass def start(update: Update, context: CallbackContext): """ method to run on """ # extracting the payload with /start  _ = re.findall(r"(?:/start )(.+)", update.message.text) # checking if it exists and sending message accordingly  if len(_) > 0: update.message.reply_text(text="You have been refered by: %s" % _[0]) pass else: update.message.reply_text(text="Hello, It seems you are new to this bot") pass update.message.reply_text(text="Use /generate to create your own referal") pass updater.dispatcher.add_handler(CommandHandler("generate", generate)) updater.dispatcher.add_handler(CommandHandler("start", start)) updater.start_polling() 
Enter fullscreen mode Exit fullscreen mode

Course Outro

Thanks for reading this course, I would encourage you to explore this wonderful telegram client yourself. I would be happy to see your creations after pursuing this tutorial. In future, if I planned to add more snippets and projects, you know where to find it. Yes, here

If you need any help regarding this, you can join their telegram group https://t.me/pythontelegrambotgroup, or you can contact me on any of the following

Email: tbhaxor@gmail.com

Twitter: @tbhaxor

LinkedIn: @tbhaxor

Top comments (22)

Collapse
 
rio_albert profile image
Albert Rio

Your curiosity about making Telegram bots with Python takes me back to a whimsical weekend last year. I had invited a few buddies over for a board game night at my place. But as tech enthusiasts, we often get sidetracked. Instead of playing the actual board games, we ended up conceptualizing a fun project: to build a Telegram bot for our gaming group that would notify everyone about the upcoming gaming events and keep scores. Now, we weren’t prepared for this on-the-spot coding marathon, and none of us was a real Python guru. So, we decided to hire a python expert from Digitalogy.co to help us out.

The expert was named Tim, a friendly guy with a penchant for coffee and humorous Python anecdotes. Together, with his help, we spent the night integrating functionalities into our bot using Python's 'python-telegram-bot' library. It's a fantastic tool that abstracts away the intricacies and lets you focus on building features. For instance, we added a function where users could "challenge" others in the group for a game, and the bot would automatically set up a poll for members to vote on the date and time. By dawn, not only had we built a functional bot, but we had also had heaps of fun (and quite a few coffee cups piled up!). It's incredible how much you can achieve with Python and a dash of creativity.

Keep exploring, and if you ever want to deep dive into making your own Telegram bot, remember that there's always a vast Python community out there willing to help, whether you're just starting or looking to refine your expertise!

Collapse
 
xiaoyao_1992 profile image
xiaoyao

good

Collapse
 
zahidcodes profile image
Iftikhar Zahid

superb bro. so hardworking......

Collapse
 
sectasy0 profile image
sectasy

Hi, Great Tutorial!! If you want to internationalize your bots then you can check out my library to internationalize python projects, Thanks!

github.com/sectasy0/pyi18n

Collapse
 
pallavi38408805 profile image
Pallavi

This is a very nice one and gives in-depth information. I am really happy with the quality and presentation of the article. I’d really like to appreciate the efforts you get with writing this post. Thanks for sharing.
Python classes in pune

Collapse
 
tbhaxor profile image
Gurkirat Singh

Thank you, pallavi

Collapse
 
kedarkhedkar profile image
Kedar Khedkar

Hey, can you help me with the storing users input in conversation

Collapse
 
tbhaxor profile image
Gurkirat Singh

Sure. Where are you stuck?

Collapse
 
kedarkhedkar profile image
Kedar Khedkar

Can we have a discussion on mail, I want to learn some more things about the Python Telegram Bot

Thread Thread
 
tbhaxor profile image
Gurkirat Singh • Edited

Sure. Send me your question on [READACTED]

Thread Thread
 
kedarkhedkar profile image
Kedar Khedkar

I solved the problem, but surely ask you in future if I am stuck. Your article really helped me to understand the library. Thanks for that!

Collapse
 
sod profile image
Sodiq Ojulari

Great tutorial, thanks for taking the time to work on this. The project links in your post is broken.

"All the snippets and projects related to this course will be found in projects directory"

I appreciate your content.

Collapse
 
alvindera97 profile image
Nwokolo Godwin Chidera

I honestly don't know why the devs at Telegram don't have something as amazing as this!!! This is one of my best reads in 2021! A masterpiece!!!!

Thanks!

Collapse
 
tbhaxor profile image
Gurkirat Singh

Glad to know you liked it

Collapse
 
bizzyel profile image
BizzyEL

How can I add an emoji to the replyKeyboardMarkup text string?

Collapse
 
tbhaxor profile image
Gurkirat Singh

You can straightaway use emojis in the strings. It does support unicode characters.
For more details, please read this wiki → github.com/python-telegram-bot/pyt...

Collapse
 
jdanvz profile image
Daniel Vázquez

The Github repo are down

Some comments may only be visible to logged-in visitors. Sign in to view all comments.