django-telegram-bot-api 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.3
2
+ Name: django-telegram-bot-api
3
+ Version: 0.1.0
4
+ Summary: A typed Telegram Bot API client for Django
5
+ Author: reza
6
+ Author-email: reza <rza.karampour@gmail.com>
7
+ Requires-Dist: django>=6.1.2
8
+ Requires-Dist: djangorestframework>=3.18.3
9
+ Requires-Dist: pydantic
10
+ Requires-Dist: requests>=2.34.2
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Django Telegram Bot
15
+
16
+ The fastest way to build a production-ready Telegram bot on Django.
17
+
18
+ ---
19
+ ## 🚀 Overview
20
+
21
+ # django-telegram
22
+ A typed Telegram Bot API client for Django.
23
+
24
+ `django-telegram` gives you every Telegram Bot API method as a plain Python method, and every Telegram type as a [Pydantic](https://docs.pydantic.dev/) model. You call a method, and you get a typed object back instead of a raw dictionary.
25
+
26
+ ## What it does
27
+
28
+ - **All Bot API methods in one place.** Sending a message, answering a callback query, setting a webhook, and the rest of the API are available as methods on a single `Telegram` client.
29
+ - **All Telegram types included.** Updates, messages, users, chats, keyboards, and every other object defined by Telegram are Pydantic models, so you get validation and editor autocompletion.
30
+ - **Responses are objects.** Access `message.chat.id` or `update.callback_query.data` directly instead of digging through nested dictionaries.
31
+ - **Built for Django.** Configuration is read from your Django settings, and the library fits into your own views and URLs.
32
+ - **Synchronous.** No event loop or async runtime required.
33
+
34
+ ---
35
+
36
+ ## ⚙️ Installation
37
+
38
+ ```bash
39
+ pip install django_telegram
40
+ ```
41
+
42
+ or with `uv`:
43
+
44
+ ```bash
45
+ uv add django_telegram
46
+ ```
47
+
48
+ ---
49
+
50
+ ## ⚡ Quick Start
51
+
52
+ Here's the minimal setup to get a bot responding to `/start`.
53
+
54
+ ### 1. Add the app and configure settings
55
+ ### 1. Configure settings
56
+
57
+ Add a `TELEGRAM` dictionary to your Django settings. You do **not** need to add `django_telegram` to `INSTALLED_APPS`.
58
+
59
+ ```python
60
+ # settings.py
61
+ TELEGRAM = {
62
+ "BOT_TOKEN": "your-telegram-bot-token",
63
+ "TM_WEBHOOK_URL": "https://api.telegram.org",
64
+ }
65
+ ```
66
+
67
+ | Key | Required | Default | Description |
68
+ |---|---|---|---|
69
+ | `BOT_TOKEN` | Yes | n/a | Your bot token from [@BotFather](https://t.me/BotFather). |
70
+ | `TM_WEBHOOK_URL` | Yes | n/a | Base URL of the Telegram Bot API. Use `https://api.telegram.org`, or the address of your own local Bot API server. |
71
+ | `PROXY_SOCKS` | No | `""` | SOCKS proxy for outgoing requests, e.g. `127.0.0.1:1080`. |
72
+
73
+ If a required key is missing, `ImproperlyConfigured` is raised and the message tells you which one.
74
+
75
+ > [!TIP]
76
+ > Keep secrets out of your repository by reading them from environment variables:
77
+ >
78
+ > ```python
79
+ > import os
80
+ >
81
+ > TELEGRAM = {
82
+ > "BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"],
83
+ > "TM_WEBHOOK_URL": os.getenv("TELEGRAM_API_URL", "https://api.telegram.org"),
84
+ > }
85
+ > ```
86
+
87
+ Once the settings are in place, the client needs no arguments:
88
+
89
+ ```python
90
+ from django_telegram import Telegram
91
+
92
+ bot = Telegram() # reads BOT_TOKEN and TM_WEBHOOK_URL from settings
93
+ ```
94
+
95
+ You can still pass a token explicitly, for example in tests or when running several bots:
96
+
97
+ ```python
98
+ bot = Telegram(token="123456:ABC-DEF")
99
+ ```
100
+
101
+ ### 2. Add the webhook URL
102
+
103
+ Create a Django app for your bot (here it is called `bot`):
104
+
105
+ ```bash
106
+ python manage.py startapp bot
107
+ ```
108
+
109
+ Add a view that receives Telegram's updates and passes them to the dispatcher:
110
+
111
+ ```python
112
+ # bot/views.py
113
+ import logging
114
+
115
+ from django_telegram import Dispatcher, Update
116
+ from rest_framework import status
117
+ from rest_framework.request import Request
118
+ from rest_framework.response import Response
119
+ from rest_framework.views import APIView
120
+
121
+ from .handlers import command_handlers # noqa: F401 (registers your handler classes)
122
+
123
+ logger = logging.getLogger(__name__)
124
+
125
+
126
+ class TelegramWebhookView(APIView):
127
+ authentication_classes = []
128
+ permission_classes = []
129
+
130
+ def post(self, request: Request) -> Response:
131
+ try:
132
+ update = Update(**request.data)
133
+ Dispatcher(update).dispatch()
134
+ except Exception:
135
+ logger.exception("Error while processing Telegram update")
136
+
137
+ # Always answer 200, otherwise Telegram keeps retrying the same update.
138
+ return Response({"ok": True}, status=status.HTTP_200_OK)
139
+ ```
140
+
141
+ Add the route to the app:
142
+
143
+ ```python
144
+ # bot/urls.py
145
+ from django.urls import path
146
+
147
+ from .views import TelegramWebhookView
148
+
149
+ app_name = "bot"
150
+
151
+ urlpatterns = [
152
+ path("webhook/", TelegramWebhookView.as_view(), name="webhook"),
153
+ ]
154
+ ```
155
+
156
+ Include it in your project URLs:
157
+
158
+ ```python
159
+ # config/urls.py
160
+ from django.urls import include, path
161
+
162
+ urlpatterns = [
163
+ path("bot/", include("bot.urls", namespace="bot")),
164
+ ]
165
+ ```
166
+
167
+ Your webhook is now served at `https://your-domain.com/bot/webhook/`.
168
+
169
+ > [!IMPORTANT]
170
+ > **Every app that defines handlers must import them in its view module.**
171
+ >
172
+ > The dispatcher finds your handlers by looking at the subclasses of `CommandHandler`, `MessageHandler`, and the other base classes. A class only exists once the file that defines it has been imported, so if the file is never imported, the dispatcher sees nothing and the update is silently ignored.
173
+ >
174
+ > Importing your handlers at the top of the webhook view (`from .handlers import command_handlers`) guarantees they are loaded as soon as Django loads your URLs, with no extra registration step.
175
+
176
+ ### 3. Write your handlers
177
+
178
+ Create a `handlers` package inside your app and add one file per handler type:
179
+
180
+ ```text
181
+ bot/
182
+ ├── __init__.py
183
+ ├── urls.py
184
+ ├── views.py
185
+ └── handlers/
186
+ ├── __init__.py
187
+ └── command_handlers.py
188
+ ```
189
+
190
+ Handlers inherit from one of the base classes and implement `handle(self, update)`. Commands (any message that starts with `/`) go to `CommandHandler`:
191
+
192
+ ```python
193
+ # bot/handlers/command_handlers.py
194
+ from django.conf import settings
195
+ from django_telegram import CommandHandler, Telegram, Update
196
+ from django_telegram.telegram_models import KeyboardButton, ReplyKeyboardMarkup
197
+
198
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
199
+
200
+
201
+ class Commands(CommandHandler):
202
+
203
+ def handle(self, update: Update):
204
+ chat_id = update.message.chat.id
205
+ text = update.message.text
206
+
207
+ if text.startswith("/start"):
208
+ bot.send_message(chat_id=chat_id, text="hello 3>")
209
+
210
+ elif text.startswith("/products"):
211
+ bot.send_message(
212
+ chat_id=chat_id,
213
+ text="choices one !",
214
+ reply_markup=ReplyKeyboardMarkup(
215
+ keyboard=[
216
+ [
217
+ KeyboardButton(text="product 1"),
218
+ KeyboardButton(text="product 2"),
219
+ ],
220
+ [
221
+ KeyboardButton(text="product 3"),
222
+ KeyboardButton(text="product 4"),
223
+ ],
224
+ ],
225
+ resize_keyboard=True,
226
+ ),
227
+ )
228
+ ```
229
+
230
+ The dispatcher does not look at the command name. Every command is delivered to your `CommandHandler` subclass, and you decide what to do for `/start`, `/products`, and so on inside `handle()`.
231
+
232
+ When your app grows to several handler files, import them all from the package so the view only needs one import:
233
+
234
+ ```python
235
+ # bot/__init__.py
236
+ from . import command_handlers # noqa: F401
237
+ # from . import message_handlers # noqa: F401
238
+ # from . import callback_query_handlers # noqa: F401
239
+ ```
240
+
241
+ ### 4. Handle plain messages
242
+
243
+ Any message that is not a command (no leading `/`) and has no media goes to `MessageHandler`. Create a new file next to your command handlers:
244
+
245
+ ```text
246
+ bot/handlers/
247
+ ├── __init__.py
248
+ ├── command_handlers.py
249
+ └── message_handlers.py ← new
250
+ ```
251
+
252
+ In this example, when the user sends `product 1` (the text of the reply keyboard button from the previous step), the bot answers with a message that has an inline button attached:
253
+
254
+ ```python
255
+ # bot/handlers/message_handlers.py
256
+ from django.conf import settings
257
+ from django_telegram import MessageHandler, Telegram, Update
258
+ from django_telegram.telegram_models import InlineKeyboardButton, InlineKeyboardMarkup
259
+
260
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
261
+
262
+
263
+ class MyMessageHandler(MessageHandler):
264
+
265
+ def handle(self, update: Update):
266
+ chat_id = update.message.chat.id
267
+ text = update.message.text
268
+
269
+ if text == "product 1":
270
+ bot.send_message(
271
+ chat_id=chat_id,
272
+ text="product 1",
273
+ reply_markup=InlineKeyboardMarkup(
274
+ inline_keyboard=[
275
+ [
276
+ InlineKeyboardButton(text="product 1", callback_data="pro1"),
277
+ ],
278
+ ],
279
+ ),
280
+ )
281
+ ```
282
+
283
+ The `callback_data` value (`"pro1"`) is what Telegram sends back to your bot when the user presses the button. You will handle it in the next step.
284
+
285
+ Now register the new file. A handler class only exists once its file has been imported, so add it to `handlers/__init__.py`:
286
+
287
+ ```python
288
+ # bot/handlers/__init__.py
289
+ from . import (
290
+ command_handlers, # noqa: F401
291
+ message_handlers, # noqa: F401
292
+ )
293
+ ```
294
+
295
+ Send `product 1` to your bot: it replies with a message and an inline button.
296
+
297
+ ### 5. Handle inline button presses (callback queries)
298
+
299
+ When the user presses an inline button, Telegram sends a callback query instead of a message. These go to `CallbackQueryHandler`. Create another file:
300
+
301
+ ```text
302
+ bot/handlers/
303
+ ├── __init__.py
304
+ ├── callback_query_handlers.py ← new
305
+ ├── command_handlers.py
306
+ └── message_handlers.py
307
+ ```
308
+
309
+ ```python
310
+ # bot/handlers/callback_query_handlers.py
311
+ from django.conf import settings
312
+ from django_telegram import CallbackQueryHandler, Telegram, Update
313
+
314
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
315
+
316
+
317
+ class MyCallbackQueryHandler(CallbackQueryHandler):
318
+
319
+ def handle(self, update: Update):
320
+ callback_data = update.callback_query.data
321
+
322
+ if callback_data == "pro1":
323
+ bot.answer_callback_query(
324
+ callback_query_id=update.callback_query.id,
325
+ text="hi this is product 1",
326
+ show_alert=True,
327
+ )
328
+ ```
329
+
330
+ `answer_callback_query` is required for every callback query: it stops the loading spinner on the button. With `show_alert=True` the text is shown as a pop-up alert instead of a small toast.
331
+
332
+ Register the file the same way:
333
+
334
+ ```python
335
+ # bot/handlers/__init__.py
336
+ from . import (
337
+ callback_query_handlers, # noqa: F401
338
+ command_handlers, # noqa: F401
339
+ message_handlers, # noqa: F401
340
+ )
341
+ ```
342
+
343
+ ### Summary
344
+
345
+ | User action | Update type | Base class | Example file |
346
+ |---|---|---|---|
347
+ | Sends `/start` | command | `CommandHandler` | `command_handlers.py` |
348
+ | Sends `product 1` | text message | `MessageHandler` | `message_handlers.py` |
349
+ | Presses an inline button | callback query | `CallbackQueryHandler` | `callback_query_handlers.py` |
350
+
351
+ Each new handler file needs one line in `handlers/__init__.py`. Your webhook view only needs `from . import handlers`.
@@ -0,0 +1,338 @@
1
+ # Django Telegram Bot
2
+
3
+ The fastest way to build a production-ready Telegram bot on Django.
4
+
5
+ ---
6
+ ## 🚀 Overview
7
+
8
+ # django-telegram
9
+ A typed Telegram Bot API client for Django.
10
+
11
+ `django-telegram` gives you every Telegram Bot API method as a plain Python method, and every Telegram type as a [Pydantic](https://docs.pydantic.dev/) model. You call a method, and you get a typed object back instead of a raw dictionary.
12
+
13
+ ## What it does
14
+
15
+ - **All Bot API methods in one place.** Sending a message, answering a callback query, setting a webhook, and the rest of the API are available as methods on a single `Telegram` client.
16
+ - **All Telegram types included.** Updates, messages, users, chats, keyboards, and every other object defined by Telegram are Pydantic models, so you get validation and editor autocompletion.
17
+ - **Responses are objects.** Access `message.chat.id` or `update.callback_query.data` directly instead of digging through nested dictionaries.
18
+ - **Built for Django.** Configuration is read from your Django settings, and the library fits into your own views and URLs.
19
+ - **Synchronous.** No event loop or async runtime required.
20
+
21
+ ---
22
+
23
+ ## ⚙️ Installation
24
+
25
+ ```bash
26
+ pip install django_telegram
27
+ ```
28
+
29
+ or with `uv`:
30
+
31
+ ```bash
32
+ uv add django_telegram
33
+ ```
34
+
35
+ ---
36
+
37
+ ## ⚡ Quick Start
38
+
39
+ Here's the minimal setup to get a bot responding to `/start`.
40
+
41
+ ### 1. Add the app and configure settings
42
+ ### 1. Configure settings
43
+
44
+ Add a `TELEGRAM` dictionary to your Django settings. You do **not** need to add `django_telegram` to `INSTALLED_APPS`.
45
+
46
+ ```python
47
+ # settings.py
48
+ TELEGRAM = {
49
+ "BOT_TOKEN": "your-telegram-bot-token",
50
+ "TM_WEBHOOK_URL": "https://api.telegram.org",
51
+ }
52
+ ```
53
+
54
+ | Key | Required | Default | Description |
55
+ |---|---|---|---|
56
+ | `BOT_TOKEN` | Yes | n/a | Your bot token from [@BotFather](https://t.me/BotFather). |
57
+ | `TM_WEBHOOK_URL` | Yes | n/a | Base URL of the Telegram Bot API. Use `https://api.telegram.org`, or the address of your own local Bot API server. |
58
+ | `PROXY_SOCKS` | No | `""` | SOCKS proxy for outgoing requests, e.g. `127.0.0.1:1080`. |
59
+
60
+ If a required key is missing, `ImproperlyConfigured` is raised and the message tells you which one.
61
+
62
+ > [!TIP]
63
+ > Keep secrets out of your repository by reading them from environment variables:
64
+ >
65
+ > ```python
66
+ > import os
67
+ >
68
+ > TELEGRAM = {
69
+ > "BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"],
70
+ > "TM_WEBHOOK_URL": os.getenv("TELEGRAM_API_URL", "https://api.telegram.org"),
71
+ > }
72
+ > ```
73
+
74
+ Once the settings are in place, the client needs no arguments:
75
+
76
+ ```python
77
+ from django_telegram import Telegram
78
+
79
+ bot = Telegram() # reads BOT_TOKEN and TM_WEBHOOK_URL from settings
80
+ ```
81
+
82
+ You can still pass a token explicitly, for example in tests or when running several bots:
83
+
84
+ ```python
85
+ bot = Telegram(token="123456:ABC-DEF")
86
+ ```
87
+
88
+ ### 2. Add the webhook URL
89
+
90
+ Create a Django app for your bot (here it is called `bot`):
91
+
92
+ ```bash
93
+ python manage.py startapp bot
94
+ ```
95
+
96
+ Add a view that receives Telegram's updates and passes them to the dispatcher:
97
+
98
+ ```python
99
+ # bot/views.py
100
+ import logging
101
+
102
+ from django_telegram import Dispatcher, Update
103
+ from rest_framework import status
104
+ from rest_framework.request import Request
105
+ from rest_framework.response import Response
106
+ from rest_framework.views import APIView
107
+
108
+ from .handlers import command_handlers # noqa: F401 (registers your handler classes)
109
+
110
+ logger = logging.getLogger(__name__)
111
+
112
+
113
+ class TelegramWebhookView(APIView):
114
+ authentication_classes = []
115
+ permission_classes = []
116
+
117
+ def post(self, request: Request) -> Response:
118
+ try:
119
+ update = Update(**request.data)
120
+ Dispatcher(update).dispatch()
121
+ except Exception:
122
+ logger.exception("Error while processing Telegram update")
123
+
124
+ # Always answer 200, otherwise Telegram keeps retrying the same update.
125
+ return Response({"ok": True}, status=status.HTTP_200_OK)
126
+ ```
127
+
128
+ Add the route to the app:
129
+
130
+ ```python
131
+ # bot/urls.py
132
+ from django.urls import path
133
+
134
+ from .views import TelegramWebhookView
135
+
136
+ app_name = "bot"
137
+
138
+ urlpatterns = [
139
+ path("webhook/", TelegramWebhookView.as_view(), name="webhook"),
140
+ ]
141
+ ```
142
+
143
+ Include it in your project URLs:
144
+
145
+ ```python
146
+ # config/urls.py
147
+ from django.urls import include, path
148
+
149
+ urlpatterns = [
150
+ path("bot/", include("bot.urls", namespace="bot")),
151
+ ]
152
+ ```
153
+
154
+ Your webhook is now served at `https://your-domain.com/bot/webhook/`.
155
+
156
+ > [!IMPORTANT]
157
+ > **Every app that defines handlers must import them in its view module.**
158
+ >
159
+ > The dispatcher finds your handlers by looking at the subclasses of `CommandHandler`, `MessageHandler`, and the other base classes. A class only exists once the file that defines it has been imported, so if the file is never imported, the dispatcher sees nothing and the update is silently ignored.
160
+ >
161
+ > Importing your handlers at the top of the webhook view (`from .handlers import command_handlers`) guarantees they are loaded as soon as Django loads your URLs, with no extra registration step.
162
+
163
+ ### 3. Write your handlers
164
+
165
+ Create a `handlers` package inside your app and add one file per handler type:
166
+
167
+ ```text
168
+ bot/
169
+ ├── __init__.py
170
+ ├── urls.py
171
+ ├── views.py
172
+ └── handlers/
173
+ ├── __init__.py
174
+ └── command_handlers.py
175
+ ```
176
+
177
+ Handlers inherit from one of the base classes and implement `handle(self, update)`. Commands (any message that starts with `/`) go to `CommandHandler`:
178
+
179
+ ```python
180
+ # bot/handlers/command_handlers.py
181
+ from django.conf import settings
182
+ from django_telegram import CommandHandler, Telegram, Update
183
+ from django_telegram.telegram_models import KeyboardButton, ReplyKeyboardMarkup
184
+
185
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
186
+
187
+
188
+ class Commands(CommandHandler):
189
+
190
+ def handle(self, update: Update):
191
+ chat_id = update.message.chat.id
192
+ text = update.message.text
193
+
194
+ if text.startswith("/start"):
195
+ bot.send_message(chat_id=chat_id, text="hello 3>")
196
+
197
+ elif text.startswith("/products"):
198
+ bot.send_message(
199
+ chat_id=chat_id,
200
+ text="choices one !",
201
+ reply_markup=ReplyKeyboardMarkup(
202
+ keyboard=[
203
+ [
204
+ KeyboardButton(text="product 1"),
205
+ KeyboardButton(text="product 2"),
206
+ ],
207
+ [
208
+ KeyboardButton(text="product 3"),
209
+ KeyboardButton(text="product 4"),
210
+ ],
211
+ ],
212
+ resize_keyboard=True,
213
+ ),
214
+ )
215
+ ```
216
+
217
+ The dispatcher does not look at the command name. Every command is delivered to your `CommandHandler` subclass, and you decide what to do for `/start`, `/products`, and so on inside `handle()`.
218
+
219
+ When your app grows to several handler files, import them all from the package so the view only needs one import:
220
+
221
+ ```python
222
+ # bot/__init__.py
223
+ from . import command_handlers # noqa: F401
224
+ # from . import message_handlers # noqa: F401
225
+ # from . import callback_query_handlers # noqa: F401
226
+ ```
227
+
228
+ ### 4. Handle plain messages
229
+
230
+ Any message that is not a command (no leading `/`) and has no media goes to `MessageHandler`. Create a new file next to your command handlers:
231
+
232
+ ```text
233
+ bot/handlers/
234
+ ├── __init__.py
235
+ ├── command_handlers.py
236
+ └── message_handlers.py ← new
237
+ ```
238
+
239
+ In this example, when the user sends `product 1` (the text of the reply keyboard button from the previous step), the bot answers with a message that has an inline button attached:
240
+
241
+ ```python
242
+ # bot/handlers/message_handlers.py
243
+ from django.conf import settings
244
+ from django_telegram import MessageHandler, Telegram, Update
245
+ from django_telegram.telegram_models import InlineKeyboardButton, InlineKeyboardMarkup
246
+
247
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
248
+
249
+
250
+ class MyMessageHandler(MessageHandler):
251
+
252
+ def handle(self, update: Update):
253
+ chat_id = update.message.chat.id
254
+ text = update.message.text
255
+
256
+ if text == "product 1":
257
+ bot.send_message(
258
+ chat_id=chat_id,
259
+ text="product 1",
260
+ reply_markup=InlineKeyboardMarkup(
261
+ inline_keyboard=[
262
+ [
263
+ InlineKeyboardButton(text="product 1", callback_data="pro1"),
264
+ ],
265
+ ],
266
+ ),
267
+ )
268
+ ```
269
+
270
+ The `callback_data` value (`"pro1"`) is what Telegram sends back to your bot when the user presses the button. You will handle it in the next step.
271
+
272
+ Now register the new file. A handler class only exists once its file has been imported, so add it to `handlers/__init__.py`:
273
+
274
+ ```python
275
+ # bot/handlers/__init__.py
276
+ from . import (
277
+ command_handlers, # noqa: F401
278
+ message_handlers, # noqa: F401
279
+ )
280
+ ```
281
+
282
+ Send `product 1` to your bot: it replies with a message and an inline button.
283
+
284
+ ### 5. Handle inline button presses (callback queries)
285
+
286
+ When the user presses an inline button, Telegram sends a callback query instead of a message. These go to `CallbackQueryHandler`. Create another file:
287
+
288
+ ```text
289
+ bot/handlers/
290
+ ├── __init__.py
291
+ ├── callback_query_handlers.py ← new
292
+ ├── command_handlers.py
293
+ └── message_handlers.py
294
+ ```
295
+
296
+ ```python
297
+ # bot/handlers/callback_query_handlers.py
298
+ from django.conf import settings
299
+ from django_telegram import CallbackQueryHandler, Telegram, Update
300
+
301
+ bot = Telegram(token=settings.TELEGRAM["BOT_TOKEN"])
302
+
303
+
304
+ class MyCallbackQueryHandler(CallbackQueryHandler):
305
+
306
+ def handle(self, update: Update):
307
+ callback_data = update.callback_query.data
308
+
309
+ if callback_data == "pro1":
310
+ bot.answer_callback_query(
311
+ callback_query_id=update.callback_query.id,
312
+ text="hi this is product 1",
313
+ show_alert=True,
314
+ )
315
+ ```
316
+
317
+ `answer_callback_query` is required for every callback query: it stops the loading spinner on the button. With `show_alert=True` the text is shown as a pop-up alert instead of a small toast.
318
+
319
+ Register the file the same way:
320
+
321
+ ```python
322
+ # bot/handlers/__init__.py
323
+ from . import (
324
+ callback_query_handlers, # noqa: F401
325
+ command_handlers, # noqa: F401
326
+ message_handlers, # noqa: F401
327
+ )
328
+ ```
329
+
330
+ ### Summary
331
+
332
+ | User action | Update type | Base class | Example file |
333
+ |---|---|---|---|
334
+ | Sends `/start` | command | `CommandHandler` | `command_handlers.py` |
335
+ | Sends `product 1` | text message | `MessageHandler` | `message_handlers.py` |
336
+ | Presses an inline button | callback query | `CallbackQueryHandler` | `callback_query_handlers.py` |
337
+
338
+ Each new handler file needs one line in `handlers/__init__.py`. Your webhook view only needs `from . import handlers`.
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "django-telegram-bot-api"
3
+ version = "0.1.0"
4
+ description = "A typed Telegram Bot API client for Django"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "reza", email = "rza.karampour@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "django>=6.1.2",
12
+ "djangorestframework>=3.18.3",
13
+ "pydantic",
14
+ "requests>=2.34.2",
15
+ ]
16
+
17
+ [build-system]
18
+ requires = ["uv_build>=0.9.18,<0.10.0"]
19
+ build-backend = "uv_build"
20
+
21
+ [tool.uv.build-backend]
22
+ module-name = "django_telegram"