├── .gitignore ├── README.md └── docs ├── README.md ├── _config.yml ├── answers.png ├── deleting.png ├── photo.png ├── question.png └── sapito.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | tutorial.ipynb 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Functions Tutorial 2 | - InlineKeyboardButton 3 | - InlineKeyboardMarkup 4 | - ConversationHandler 5 | - CallbackQueryHandler 6 | - MessageHandler 7 | 8 |     [Access the Tutorial](https://misosora.github.io/telegram-functions-tutorial/) 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # _**A Little Tutorial on Some Useful Functions from the Telegram API**_ 2 | ##### **by Sora Satie** 3 | 4 |
5 | 6 | ##   **1. Importing the Functions** 7 | 8 |     Starting with the basics, you want to make sure you import the functions we'll be using from now on. The beginning of your main file should look like this: 9 | 10 | 11 | ```python 12 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 13 | from telegram.ext import Updater, CommandHandler, ConversationHandler, CallbackQueryHandler, MessageHandler, Filters 14 | import logging 15 | import os 16 | ``` 17 | 18 |     But what does all of that mean? 19 | - _**InlineKeyboardButton**_: defines a button (you can create more than one within the 'keyboard') 20 | - _**InlineKeyboardMarkup**_: puts all of the buttons you chose to define in a 'markup' so you can send them attached to a message 21 | - _**Handlers**_: 22 | - used to define the behavior of the bot's commands (*CommandHandler*) 23 | - a chain of commands (*ConversationHandler*) 24 | - the queries sent back by the buttons (*CallbackQueryHandler*) 25 | - the messages sent by the user (*MessageHandler*) 26 | - _**Filters**_: used by the *MessageHandler* to specify which type of message or media we're dealing with (such as photos or audios) 27 | - _**logging**_: it basically sends you information about the program running, in this case your bot 28 | - _**os**_: we'll use this later on to delete a file from our computer or server 29 | 30 |     Don't worry if you don't understand everything right away, it'll get much clearer when we (finally) start coding! 31 | 32 |
33 | 34 | ##   **2. Adding Buttons** 35 | 36 |     First, let's create a simple 'start' function: 37 | 38 | 39 | ```python 40 | def start(update, context): 41 | message = "Hey, @{}!".format(update.effective_user.username) 42 | # 'update.effective_user.username' returns the user's @ 43 | context.bot.send_message(chat_id=update.effective_chat.id, text=message) 44 | 45 | return 46 | ``` 47 | 48 |     To run this command, simply add the following line to your main function: 49 | 50 | 51 | ```python 52 | # dp = updater.dispatcher 53 | dp.add_handler(CommandHandler("start", start)) 54 | ``` 55 | 56 |     That looks nice, but it's not exactly very interactive. What about a function that asks the user a question? Well, let's have fun with buttons! 57 | 58 | 59 | ```python 60 | # You can choose whatever question you want! 61 | def question(update, context): 62 | prompt = "Tell me, human, what sound does a frog make?" 63 | 64 | # Now, let's define our buttons (I chose to put four options) 65 | keyboard = [ 66 | [InlineKeyboardButton(text="Meow", callback_data='wrong'), 67 | InlineKeyboardButton(text="Moo", callback_data='wrong')], 68 | [InlineKeyboardButton(text="Ribbit", callback_data='right'), 69 | InlineKeyboardButton(text="Oink", callback_data='wrong')] 70 | ] 71 | ``` 72 | 73 |     Notice that I split the four options into two groups of two. They will be displayed like this: 74 | 75 |     ![this image cannot be displayed](question.png "Buttons") 76 | 77 |     To change this, simply put whichever buttons you want to be together within the same square brackets. 78 | 79 |     You might also be wondering what the 'callback_data' means. I'm going to explain how to use it in the next section, but it's basically the data you receive when the user clicks on a button. You can also set it to a number or multiple words using underscores. Examples: 80 | - callback_data='4' 81 | - callback_data='this_is_a_callback' 82 | 83 |     Just use whatever's more convenient for your type of question or prompt! 84 | 85 | 86 | ```python 87 | # Ok, we have a keyboard (aka our buttons)! We just need to declare our markup and send our message 88 | markup = InlineKeyboardMarkup(inline_keyboard=keyboard) 89 | context.bot.send_message(chat_id=update.effective_chat.id, text=prompt, reply_markup=markup) 90 | 91 | return 92 | ``` 93 | 94 |     To run this command, simply add the following line to your main function: 95 | 96 | 97 | ```python 98 | dp.add_handler(CommandHandler("question", question)) 99 | ``` 100 | 101 |     _Voilà!_ As you can see, using and sending buttons is quite simple. The real fun part, however, is how we use 'callback_data' to process or send further information to the user. 102 | 103 |
104 | 105 | ##   **3. Making Your Buttons Interactive** 106 | 107 |     Let's finally make this command interactive, shall we? 108 | 109 | 110 | ```python 111 | # We need a new function that will process the 'callback_data' 112 | def query_handler(update, context): 113 | 114 | # Here, we'll have access to the user's answer 115 | query = update.callback_query 116 | query.answer() 117 | 118 | # Change your comparisons depending on what you chose as 'callback_data' 119 | if query.data == 'wrong': 120 | wrong = "Sorry, that's not the right answer. Try again." 121 | context.bot.send_message(chat_id=update.effective_chat.id, text=wrong) 122 | if query.data == 'right': 123 | right = "Congratulations! You have chosen the right answer." 124 | context.bot.send_message(chat_id=update.effective_chat.id, text=right) 125 | 126 | return 127 | ``` 128 | 129 |     To run this function alongside your question command, simply add the following line to your main function: 130 | 131 | 132 | ```python 133 | dp.add_handler(CallbackQueryHandler(query_handler)) 134 | ``` 135 | 136 |     If you click on the buttons from top to bottom, it should look like this: 137 | 138 |     ![this image cannot be displayed](answers.png "Button answers") 139 | 140 |     If you want it to be a little cleaner after the user has answered, you can either delete the whole message sent by the bot (with the buttons), or just the buttons. 141 | - Deleting the whole message: 142 | 143 | 144 | ```python 145 | # First, we need to save the message ID so we can properly delete it 146 | # On your question function, replace this line: 147 | context.bot.send_message(chat_id=update.effective_chat.id, text=prompt, reply_markup=markup) 148 | # With this one: 149 | text = context.bot.send_message(chat_id=update.effective_chat.id, text=prompt, reply_markup=markup) 150 | # Then save its ID 151 | context.bot_data['text_id'] = text.message_id 152 | # You can use context.bot_data['name'] to save bot-related data and access it outside of your function 153 | # There's also context.user_data['name'] to save user-related data 154 | ``` 155 | 156 | 157 | ```python 158 | # After you've saved the id, add the following lines to all of your if statements on your 'query_handler': 159 | text_id = context.bot_data['text_id'] 160 | context.bot.delete_message(chat_id=update.effective_chat.id, message_id=text_id) 161 | # It instantly deletes the question along with the buttons attached to it 162 | ``` 163 | 164 | - Deleting only the buttons: 165 | 166 | 167 | ```python 168 | # You just have to add this line to all of your if statements on your 'query_handler': 169 | context.bot.edit_message_reply_markup(chat_id=query.message.chat_id, message_id=query.message.message_id) 170 | ``` 171 | 172 |     Deleting the whole message and deleting only the buttons, respectively, should look like this: 173 | 174 |     ![this image cannot be displayed](deleting.png "Cleaning up the answers") 175 | 176 |     You can make small modifications, such as only deleting the message or the buttons when the user gets the answer right. To do this, just add the 'query_handler' modifications above to the if statement regarding the right 'callback_data'. 177 | 178 |     Congratulations! You just wrote your first interactive function with buttons! :) As you can see, the hardest part is just figuring out how everything works, but the actual code is quite straightforward. In the next section, we'll talk about something a bit more complicated, but also not at all impossible (and really fun!). 179 | 180 |
181 | 182 | ##   **4. Using the 'ConversationHandler'** 183 | 184 |     Let's suppose we want to write a command that waits for the user's answer. To do that, we need to set up a chain of events that will happen before and after the user's message. But how? 185 | 186 |     First, we need to define a variable to return so we can define the 'ConversationHandler' behavior: 187 | 188 | 189 | ```python 190 | # Right below your import statements, add the following line: 191 | MEDIA = range(1) 192 | # This variable will be returned by the functions you call before the last one. 193 | ``` 194 | 195 |     To make things more interesting, we're going to ask the user for an image. The first step is to build the main command. 196 | 197 | 198 | ```python 199 | def photo(update, context): 200 | prompt = "Please, send me a photo!" 201 | context.bot.send_message(chat_id=update.effective_chat.id, text=prompt) 202 | 203 | return MEDIA 204 | ``` 205 | 206 |     This function then calls the next one, which will save important information we need to process the data. 207 | 208 | 209 | ```python 210 | def save_info(update, context): 211 | 212 | # Getting the information of the image sent by the user 213 | media = context.bot.get_file(update.message.photo[-1]) 214 | 215 | if media == None: return 216 | 217 | prompt = "Do you want me to send you this photo as a document?" 218 | keyboard = [ 219 | [InlineKeyboardButton(text="Yes", callback_data='yes')], 220 | [InlineKeyboardButton(text="No", callback_data='no')] 221 | ] 222 | markup = InlineKeyboardMarkup(inline_keyboard=keyboard) 223 | 224 | context.bot.send_message(chat_id=update.effective_chat.id, text=prompt, reply_markup=markup) 225 | 226 | # The media information (so we can save the image if the user presses "Yes") 227 | context.user_data['media_id'] = media.file_id 228 | 229 | return MEDIA 230 | ``` 231 | 232 |     Now we just need to handle the 'callback_data'. 233 | 234 | 235 | ```python 236 | def query_handler(update, context): 237 | query = update.callback_query 238 | query.answer() 239 | 240 | if query.data == 'no': 241 | goodbye = "Understandable, have a good day!" 242 | context.bot.send_message(chat_id=update.effective_chat.id, text=goodbye) 243 | 244 | if query.data == 'yes': 245 | 246 | # What we're doing here is: 247 | # - Downloading the image sent by the user 248 | # - Sending it as a document 249 | # - Deleting the image from our computer or server 250 | # - Ending the 'ConversationHandler' 251 | media_id = context.user_data['media_id'] 252 | photo = context.bot.get_file(media_id) 253 | photo.download(f"{media_id}.jpg") 254 | 255 | context.bot.send_document(chat_id=update.effective_chat.id, document=open(f"{media_id}.jpg", 'rb')) 256 | os.remove(f"{media_id}.jpg") 257 | 258 | return ConversationHandler.END 259 | ``` 260 | 261 |     All of our functions are done! We just need to define the 'ConversationHandler'. On your main function, add the following code: 262 | 263 | 264 | ```python 265 | conv_handler = ConversationHandler( 266 | entry_points=[CommandHandler("photo", photo)], # User sends the /photo command 267 | states={MEDIA: [MessageHandler(Filters.photo, save_info)]}, # User sends an image 268 | fallbacks=[CallbackQueryHandler(query_handler)] # 'callback_data' is processed 269 | ) 270 | dp.add_handler(conv_handler) 271 | ``` 272 | 273 |     In the end, your function should prompt the user for a photo and respond like this: 274 | 275 |     ![this image cannot be displayed](photo.png "Sending the photo as a document") 276 | 277 |     You can manage different types of messages with your 'MessageHandler', such as text, audio, image, anything you want! The functions we built are quite simple, but it's just a sneak peek of the many amazing things you can do with the Telegram API. 278 | 279 |     My boyfriend has a project that processes images and removes their backgrounds using a Deep Learning model. Since it's a Telegram bot, I decided to put all of my knowledge of buttons and handlers into practice, so I'll link the GitHub repository here if you want to see what I did. It's mainly the code written above to get an image from the user, but it's worth checking out the cool Deep Learning aspect of it! :) 280 | 281 |     ["**Background Removal Bot**"](https://github.com/LFRusso/BackgroundRemovalBot) 282 | 283 |     Well, we've finally come to an end! Thank you so much for reading my first little tutorial, I hope from the bottom of my heart that it has helped you even a small bit :) Feel free to give me feedback or suggestions, and have fun with your bot! 284 | 285 |
286 | 287 |     ![this image cannot be displayed](sapito.jpg "Strawberry frog says bye!") 288 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/answers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misosora/telegram-functions-tutorial/87dce1aae997477f01df3bcb5bb2f4ae705f852d/docs/answers.png -------------------------------------------------------------------------------- /docs/deleting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misosora/telegram-functions-tutorial/87dce1aae997477f01df3bcb5bb2f4ae705f852d/docs/deleting.png -------------------------------------------------------------------------------- /docs/photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misosora/telegram-functions-tutorial/87dce1aae997477f01df3bcb5bb2f4ae705f852d/docs/photo.png -------------------------------------------------------------------------------- /docs/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misosora/telegram-functions-tutorial/87dce1aae997477f01df3bcb5bb2f4ae705f852d/docs/question.png -------------------------------------------------------------------------------- /docs/sapito.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misosora/telegram-functions-tutorial/87dce1aae997477f01df3bcb5bb2f4ae705f852d/docs/sapito.jpg --------------------------------------------------------------------------------