- Notifications
You must be signed in to change notification settings - Fork5.7k
Code snippets
This page can be read on its own to find the code snippet you need right now.
It is also a follow-up to the pageIntroduction to the API. If you come from there, you can leave your command line open and just try out a few of these snippets.
- Pure API
- General code snippets
- Advanced snippets
To fetch messages sent to your Bot, you can use thegetUpdates API method.
Note: You don't have to useget_updates
if you are writing your bot with thetelegram.ext
submodule, sincetelegram.ext.Updater
takes care of fetching all updates for you. Read more about thathere.
updates=awaitbot.get_updates()print([u.message.textforuinupdates])
updates=awaitbot.get_updates()print([u.message.photoforuinupdatesifu.message.photo])
You'll always need thechat_id
chat_id= (awaitbot.get_updates())[-1].message.chat_id
These snippets usually apply to both ways of fetching updates. If you're usingtelegram.ext
, you can get thechat_id
in your handler callback withupdate.message.chat_id
.
Note: In general, you can send messages to users by passing their user id as thechat_id
.If the bot has a chat with the user, it will send the message to that chat.
awaitbot.send_message(chat_id=chat_id,text="I'm sorry Dave I'm afraid I can't do that.")
Note:send_message
method (as any ofsend_*
methods ofBot
class) returns the instance ofMessage
class, so it can be used in code later.
This is a shortcut tobot.send_message
with same defaults. Read more about itin the docs.
awaitupdate.message.reply_text("I'm sorry Dave I'm afraid I can't do that.")
Note: There are equivalents of this method for replying with photos, audio etc., and similar shortcuts exist throughout the library.
ᵀᴱᴸᴱᴳᴿᴬᴹUse this to tell the user that something is happening on the bot's side:
awaitbot.send_chat_action(chat_id=chat_id,action=telegram.constants.ChatAction.TYPING)
Alternatively, if you have several commands and don't want to repeat the above code snippet inside all commands seethis wiki section on how to do that.
location_keyboard=telegram.KeyboardButton(text="send_location",request_location=True)contact_keyboard=telegram.KeyboardButton(text="send_contact",request_contact=True)custom_keyboard= [[location_keyboard,contact_keyboard ]]reply_markup=telegram.ReplyKeyboardMarkup(custom_keyboard)awaitbot.send_message(chat_id=chat_id, ...text="Would you mind sharing your location and contact with me?", ...reply_markup=reply_markup)
To catch the incoming message with the location/contact, useMessageHandler
withfilters.LOCATION
andfilters.CONTACT
, respectively.
Telegram supports some formatting options for text. All the details about what is supported can be foundhere. Please keep in mind that you will have to escape the special characters as detailed in the documentation. PTB also offers ahelper function for escaping of Markdown text. For escaping of HTML text, you can usehtml.escape
from the standard library.
You can format text with every API method/type that has aparse_mode
parameter. In addition to editing your text as described in the link above, pass one of the parse modes available throughtelegram.constants.ParseMode
to theparse_mode
parameter. Since the5.0
update of the Bot API (version13.1+
of PTB), you can alternatively pass a list oftelegram.MessageEntities
to theentities
parameter.
Note: In the API 4.5 update, Telegram introduced MarkdownV2, which supports nested entities and needs other escaping than v1. Markdown V1 is referred as legacy mode by the official API docs, and you should prefer MarkdownV2. Make sure to also usereply_markdown_v2
instead ofreply_markdown
etc.
awaitbot.send_message(chat_id=chat_id,text="*bold* _italic_ `fixed width font` [link](http://google.com)\.",parse_mode=telegram.constants.ParseMode.MARKDOWN_V2)
awaitbot.send_message(chat_id=chat_id,text='<b>bold</b> <i>italic</i> <a href="http://google.com">link</a>.',parse_mode=telegram.constants.ParseMode.HTML)
ᵀᴱᴸᴱᴳᴿᴬᴹTo useMessageEntity
, extract the entities and their respective text from aMessage
object usingparse_entities
.
Note: This method should always be used instead of theentities
attribute, since it calculates the correct substring from the message text based on UTF-16 codepoints - that is, it extracts the correct string even on when working with weird characters such as Emojis.
Consider this example which checks for presence of URLs in a message and prints them on screen.
# A dictionary that maps entity to textentities=message.parse_entities()forentinentities:txt=entities[ent]ifent.type==ent.TEXT_LINK:# Text with embedded URLprint(f"{txt} -{ent.url}")elifent.type==ent.URL:# Plain URLprint(txt)
For more narrowed use cases like extracting only Telegram message links, you might be better usingptbcontrib/extract_urls.
This is an example how to use entities to convert Telegram formatting to BBCode. In the current version it doesnot support nested entities.
Define parsing function:
Click to expand
importsysdefparse_bbcode(message_text,entities,urled=False):"""BBCode parsing function"""ifmessage_textisNone:returnNoneifnotsys.maxunicode==0xffff:message_text=message_text.encode('utf-16-le')bbcode_text=''last_offset=0forentity,textinsorted(entities.items(),key=(lambdaitem:item[0].offset)):ifentity.type=='text_link':insert='[url={}]{}[/url]'.format(entity.url,text)elifentity.type=='mention':insert='[url=https://t.me/{0}]{1}[/url]'.format(text.strip('@'),text)elifentity.type=='url'andurled:insert='[url={0}]{0}[/url]'.format(text)elifentity.type=='bold':insert='[b]'+text+'[/b]'elifentity.type=='italic':insert='[i]'+text+'[/i]'elifentity.type=='underline':insert='[u]'+text+'[/u]'elifentity.type=='strikethrough':insert='[s]'+text+'[/s]'elifentity.type=='code':insert='[code]'+text+'[/code]'elifentity.type=='pre':insert='[pre]'+text+'[/pre]'else:insert=textifsys.maxunicode==0xffff:bbcode_text+=message_text[last_offset:entity.offset]+insertelse:bbcode_text+=message_text[last_offset*2:entity.offset*2].decode('utf-16-le')+insertlast_offset=entity.offset+entity.lengthifsys.maxunicode==0xffff:bbcode_text+=message_text[last_offset:]else:bbcode_text+=message_text[last_offset*2:].decode('utf-16-le')returnbbcode_text
Call it with:
entities=update.message.parse_entities()bbcode=parse_bbcode(update.message.text,entities,urled=True)
...or for photo captions:
entities=update.message.parse_caption_entities()bbcode=parse_bbcode(caption,entities,urled=True)
bbcode
will contain message/caption text formatted in BBCode.urled
parameter determines if URLs in text are to be processed as links or left as text.
custom_keyboard= [['top-left','top-right'], ['bottom-left','bottom-right']]reply_markup=telegram.ReplyKeyboardMarkup(custom_keyboard)awaitbot.send_message(chat_id=chat_id,text="Custom Keyboard Test",reply_markup=reply_markup)
See also:Build a menu with Buttons
reply_markup=telegram.ReplyKeyboardRemove()awaitbot.send_message(chat_id=chat_id,text="I'm back.",reply_markup=reply_markup)
The Unicode flag emoji for any country can by definition be calculated from the countries2 letter country code. The following snippet only works in Python 3.
OFFSET=127462-ord('A')defflag(code):code=code.upper()returnchr(ord(code[0])+OFFSET)+chr(ord(code[1])+OFFSET)>>>flag('un')'🇺🇳'>>>flag('eu')'🇪🇺'
The 🎰 dice can take the values 1-64. Here is a dictionary that maps each value to the unique combination of symbols that produce that value:
Click to expand
slot_machine_value= {1: ("bar","bar","bar"),2: ("grape","bar","bar"),3: ("lemon","bar","bar"),4: ("seven","bar","bar"),5: ("bar","grape","bar"),6: ("grape","grape","bar"),7: ("lemon","grape","bar"),8: ("seven","grape","bar"),9: ("bar","lemon","bar"),10: ("grape","lemon","bar"),11: ("lemon","lemon","bar"),12: ("seven","lemon","bar"),13: ("bar","seven","bar"),14: ("grape","seven","bar"),15: ("lemon","seven","bar"),16: ("seven","seven","bar"),17: ("bar","bar","grape"),18: ("grape","bar","grape"),19: ("lemon","bar","grape"),20: ("seven","bar","grape"),21: ("bar","grape","grape"),22: ("grape","grape","grape"),23: ("lemon","grape","grape"),24: ("seven","grape","grape"),25: ("bar","lemon","grape"),26: ("grape","lemon","grape"),27: ("lemon","lemon","grape"),28: ("seven","lemon","grape"),29: ("bar","seven","grape"),30: ("grape","seven","grape"),31: ("lemon","seven","grape"),32: ("seven","seven","grape"),33: ("bar","bar","lemon"),34: ("grape","bar","lemon"),35: ("lemon","bar","lemon"),36: ("seven","bar","lemon"),37: ("bar","grape","lemon"),38: ("grape","grape","lemon"),39: ("lemon","grape","lemon"),40: ("seven","grape","lemon"),41: ("bar","lemon","lemon"),42: ("grape","lemon","lemon"),43: ("lemon","lemon","lemon"),44: ("seven","lemon","lemon"),45: ("bar","seven","lemon"),46: ("grape","seven","lemon"),47: ("lemon","seven","lemon"),48: ("seven","seven","lemon"),49: ("bar","bar","seven"),50: ("grape","bar","seven"),51: ("lemon","bar","seven"),52: ("seven","bar","seven"),53: ("bar","grape","seven"),54: ("grape","grape","seven"),55: ("lemon","grape","seven"),56: ("seven","grape","seven"),57: ("bar","lemon","seven"),58: ("grape","lemon","seven"),59: ("lemon","lemon","seven"),60: ("seven","lemon","seven"),61: ("bar","seven","seven"),62: ("grape","seven","seven"),63: ("lemon","seven","seven"),64: ("seven","seven","seven"),}
importasynciofromtelegramimportUpdatefromtelegram.extimportContextTypesasyncdefadd_group(update:Update,context:ContextTypes.DEFAULT_TYPE):awaitasyncio.gather(update.message.reply_text(f"{member.full_name} just joined the group")formemberinupdate.message.new_chat_members )add_group_handle=MessageHandler(filters.StatusUpdate.NEW_CHAT_MEMBERS,add_group)application.add_handler(add_group_handle)
Note that service messages about non-bot users joining the chat are removed from large groups. You can get the new members message by following thechatmemberbot.py example.
If you're usingMessageHandlers
and do not want them to respond to the channel posts automatically forwarded to the discussion group linked to your channel, you can use this filter in yourMessageHandler
(requires PTB v13.9+):
~filters.IS_AUTOMATIC_FORWARD
If you're usingMessageHandlers
and do not want them to respond to messages from anonymous admins, you can use this filter in yourMessageHandler
:
~filters.SenderChat.SUPER_GROUP
This decorator allows you to register a function as a command handler in aFlask like manner.
defcommand_handler(command):defdecorator(func):handler=CommandHandler(command,func)application.add_handler(handler)returnfuncreturndecorator
Add the@command_handler(command)
decorator on top of your handler function:
@command_handler("hello")asyncdefhello(update,context):awaitcontext.bot.send_message(chat_id=update.effective_chat.id,text="Hello world!")
Note: You can modify this decorator in order to register any type of handler (seeTypes Of Handlers). Please also note that PTB deliberately does not provide such functionality out of the box due to the reasons mentioned in#899.
This decorator allows you to restrict the access of a handler to only theuser_ids
specified inLIST_OF_ADMINS
.
fromfunctoolsimportwrapsLIST_OF_ADMINS= [12345678,87654321]defrestricted(func):@wraps(func)asyncdefwrapped(update,context,*args,**kwargs):user_id=update.effective_user.idifuser_idnotinLIST_OF_ADMINS:print(f"Unauthorized access denied for{user_id}.")returnreturnawaitfunc(update,context,*args,**kwargs)returnwrapped
Add a@restricted
decorator on top of your handler declaration:
@restrictedasyncdefmy_handler(update,context):pass# only accessible if `user_id` is in `LIST_OF_ADMINS`.
This parametrized decorator allows you to signal different actions depending on the type of response of your bot. This way users will have similar feedback from your bot as they would from a real human.
fromfunctoolsimportwrapsdefsend_action(action):"""Sends `action` while processing func command."""defdecorator(func):@wraps(func)asyncdefcommand_func(update,context,*args,**kwargs):awaitcontext.bot.send_chat_action(chat_id=update.effective_message.chat_id,action=action)returnawaitfunc(update,context,*args,**kwargs)returncommand_funcreturndecorator
You can decorate handler callbacks directly with@send_action(ChatAction.<Action>)
or create aliases and decorate with them (more readable) .
send_typing_action=send_action(ChatAction.TYPING)send_upload_video_action=send_action(ChatAction.UPLOAD_VIDEO)send_upload_photo_action=send_action(ChatAction.UPLOAD_PHOTO)
With the above aliases, the following decorators are equivalent
@send_typing_actionasyncdefmy_handler(update,context):pass# user will see 'typing' while your bot is handling the request.@send_action(ChatAction.TYPING)asyncdefmy_handler(update,context):pass# user will see 'typing' while your bot is handling the request.
All possible actions are documentedhere.
Often times you will find yourself in need for a menu with dynamic content. Use the followingbuild_menu
method to create a button layout withn_cols
columns out of a list ofbuttons
.
fromtypingimportUnion,ListfromtelegramimportInlineKeyboardButtondefbuild_menu(buttons:List[InlineKeyboardButton],n_cols:int,header_buttons:Union[InlineKeyboardButton,List[InlineKeyboardButton]]=None,footer_buttons:Union[InlineKeyboardButton,List[InlineKeyboardButton]]=None)->List[List[InlineKeyboardButton]]:menu= [buttons[i:i+n_cols]foriinrange(0,len(buttons),n_cols)]ifheader_buttons:menu.insert(0,header_buttonsifisinstance(header_buttons,list)else [header_buttons])iffooter_buttons:menu.append(footer_buttonsifisinstance(footer_buttons,list)else [footer_buttons])returnmenu
You can use theheader_buttons
andfooter_buttons
lists to put buttons in the first or last row respectively.
Replace the...
in below snippet by an appropriate argument, as indicated in theInlineKeyboardButton documentation. If you want to useKeyboardButtons
, useReplyKeyboardMarkup
instead ofInlineKeyboardMarkup
.
button_list= [InlineKeyboardButton("col1",callback_data=...),InlineKeyboardButton("col2",callback_data=...),InlineKeyboardButton("row 2",callback_data=...)]reply_markup=InlineKeyboardMarkup(util.build_menu(button_list,n_cols=2))awaitbot.send_message(...,"A two-column menu",reply_markup=reply_markup)
Or, if you need a dynamic version, use list comprehension to generate yourbutton_list
dynamically from a list of strings:
some_strings= ["col1","col2","row2"]button_list= [[KeyboardButton(s)]forsinsome_strings]
This is especially useful if put inside a helper method likeget_data_buttons
to work on dynamic data and updating the menu according to user input.
To handle thecallback_data
, you need to set aCallbackQueryHandler
.
Verify data fromTelegram Web Login Widget.
When using aLoginUrl
in anInlineKeyboardButton
to authorize a user on your website via Telegram, you'll have to to check the hash of the received data to verify the data of the integrity as describedhere
The data JSON data will have the following form:
{"id":XXXXXXXXX"first_name":"XXX""last_name":"XXX""username":"XXXXX""photo_url":"https://t.meXXXXXX.jpg""auth_date":XXXXXXXXXX"hash":"XXXXXXXXXXXXXXXXXXXXXX....."}
The following is an example implementation in Python:
Click to expand
importhashlibimporthmacBOT_TOKEN='YOUR BOT TOKEN'defverify(request_data):request_data=request_data.copy()tg_hash=request_data['hash']request_data.pop('hash',None)request_data_alphabetical_order=sorted(request_data.items(),key=lambdax:x[0])data_check_string= []fordata_pairinrequest_data_alphabetical_order:key,value=data_pair[0],data_pair[1]data_check_string.append(f"{key}={value}")data_check_string='\n'.join(data_check_string)secret_key=hashlib.sha256(BOT_TOKEN.encode()).digest()received_hash=hmac.new(secret_key,msg=data_check_string.encode(),digestmod=hashlib.sha256).hexdigest()ifreceived_hash==tg_hash:# The user clicked to the Auth Button and data is verified.print('User Logged in.')returnTrueelse:# The data is not validprint('User data mis-matched.')returnFalse# Optionally use another if-else block to check the auth_date in order to prevent outdated data from being verified.
A sample of Flask app can be foundhere.
The following example allows you to restart the bot from within a handler.It goes without saying that you should protect this method from access by unauthorized users - seehere for some tips on this.The main magic consists of callingApplication.stop_running
from within a handler callback to allow for a graceful shutdown.Actually restarting the python script can then be achieved by different means.
Click to expand
importosimportsysfromtelegramimportUpdatefromtelegram.extimportContextTypes,Application,CommandHandlerasyncdefrestart(update:Update,context:ContextTypes.DEFAULT_TYPE)->None:context.bot_data["restart"]=Truecontext.application.stop_running()defmain()->None:application=Application.builder().token("TOKEN").build()application.bot_data["restart"]=Falseapplication.add_handler(CommandHandler("restart",restart))application.run_polling(allowed_updates=Update.ALL_TYPES)ifapplication.bot_data["restart"]:os.execl(sys.executable,sys.executable,*sys.argv)if__name__=="__main__":main()
Wiki ofpython-telegram-bot
© Copyright 2015-2025 – Licensed byCreative Commons
- Architecture Overview
- Builder Pattern for
Application
- Types of Handlers
- Working with Files and Media
- Exceptions, Warnings and Logging
- Concurrency in PTB
- Advanced Filters
- Storing data
- Making your bot persistent
- Adding Defaults
- Job Queue
- Arbitrary
callback_data
- Avoiding flood limits
- Webhooks
- Bot API Forward Compatiblity
- Frequently requested design patterns
- Code snippets
- Performance Optimizations
- Telegram Passport
- Bots built with PTB
- Automated Bot Tests