├── .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 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------