1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Compare commits

...

340 Commits
3.7.8 ... 4.2.1

Author SHA1 Message Date
fbf34f5953 Bump version to 4.2.1 - AsyncTeleBot alpha 2021-12-04 20:25:39 +03:00
4347dd3dd9 Merge pull request #1380 from coder2020official/master
2 new examples and behaviour change
2021-12-04 20:08:26 +03:00
d830ae0b15 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-12-04 22:03:56 +05:00
4f198bc6f5 Forgot to update file 2021-12-04 22:03:14 +05:00
66615a41c4 Merge branch 'eternnoir:master' into master 2021-12-04 21:57:32 +05:00
a5ee5f816c Update README.md 2021-12-04 21:57:16 +05:00
fb52137bff 2 new examples 2021-12-04 21:54:26 +05:00
7ee07f4dc7 Merge pull request #1379 from Badiboy/master
Readme
2021-12-04 19:44:00 +03:00
f224069a34 Update README.md 2021-12-04 19:43:33 +03:00
6cca77f755 Update README.md 2021-12-04 19:43:01 +03:00
084289baa4 Merge pull request #1378 from Badiboy/master
Readme minor fixed
2021-12-04 19:42:08 +03:00
e2dbb88459 Readme minor fixed 2021-12-04 19:41:25 +03:00
a2822c74ed Update README.md 2021-12-04 21:34:15 +05:00
4cd30c75ac Merge pull request #1377 from coder2020official/master
Exception handler, Boolean Fix and more
2021-12-04 19:33:53 +03:00
f4b9480588 Update README.md 2021-12-04 21:25:47 +05:00
482589af49 Update README.md 2021-12-04 21:25:14 +05:00
bbe4a96984 Update README.md 2021-12-04 21:23:23 +05:00
60294d0c41 Update README.md 2021-12-04 21:22:44 +05:00
3035763277 Update send_file_example.py 2021-12-04 21:22:00 +05:00
51eabde320 Update 2021-12-04 21:11:51 +05:00
a5305f551c Update README.md 2021-12-03 21:13:02 +05:00
1f918dece5 Merge pull request #1375 from coder2020official/master
Forgot to make polling sync
2021-11-27 23:12:15 +03:00
fc152f37ad Merge branch 'eternnoir:master' into master 2021-11-28 01:05:11 +05:00
411c7e915a No asyncio.run() 2021-11-28 01:04:49 +05:00
5bf2415653 Merge pull request #1374 from Badiboy/master
Python 3.10 added
2021-11-27 22:30:25 +03:00
7d9856dae3 Python 3.10 added 2021-11-27 22:29:57 +03:00
a308ab12fa Asynchronous TeleBot version Alpha release 2021-11-27 22:28:46 +03:00
d58336adcb Fix 2021-11-28 00:25:56 +05:00
bfc0b8ecd5 Update async_telebot.py 2021-11-28 00:21:09 +05:00
a9b422783f Middlewares, new file, and examples 2021-11-27 23:41:39 +05:00
e015b4c010 Merge pull request #1373 from Carlosma7/master
Add GrandQuiz Bot by Carlosma7
2021-11-27 18:49:05 +03:00
f666c15a1f Add GrandQuiz Bot by Carlosma7 2021-11-27 16:32:58 +01:00
6770011dd7 Middleware support 2021-11-27 19:04:03 +05:00
d7b0513fb1 Merge branch 'eternnoir:master' into master 2021-11-26 21:05:05 +05:00
9932ade00e Merge pull request #1369 from abdullaev388/master
CallbackData class added
2021-11-24 18:32:42 +03:00
8b6eba8203 Docstrings added 2021-11-24 20:26:58 +05:00
9c8ea29fc6 token removed :) 2021-11-23 18:15:52 +05:00
714ae7d67f CallbackData class added 2021-11-23 18:01:51 +05:00
1e4477c148 Logging fix 2021-11-20 16:01:38 +05:00
53f9232f36 Update requirements.txt 2021-11-20 15:54:43 +05:00
1f05b47ad6 Asynchronous Telebot 2021-11-20 15:47:55 +05:00
98d63d235f Merge pull request #1360 from JoachimStanislaus/master
added Google Sheet bot to list of bot examples.
2021-11-11 12:40:36 +03:00
7925bdc6c9 added Google Sheet bot to list of bot examples. 2021-11-11 17:21:56 +08:00
4ba23562ef Merge pull request #1358 from Badiboy/master
Update readme and typo
2021-11-08 19:11:11 +03:00
e22a7fecea One more readme update... 2021-11-08 18:53:10 +03:00
9b99bb5f21 Update readme and typo 2021-11-08 18:51:42 +03:00
c39b3aaaf3 Merge pull request #1357 from Badiboy/master
Bump to version 4.2.0
2021-11-08 18:37:03 +03:00
d14ac2fe85 Bump to version 4.2.0 2021-11-08 18:35:55 +03:00
04f3e518da Merge remote-tracking branch 'upstream/master' 2021-11-08 18:33:10 +03:00
5f5298bcd1 Merge pull request #1356 from Badiboy/master
RETRY_ENGINE
2021-11-08 18:33:00 +03:00
7f1497c5e9 Merge remote-tracking branch 'upstream/master' 2021-11-08 18:32:38 +03:00
5ac71baafe RETRY_ENGINE
Added RETRY_ENGINE var to api_helper.

Added RETRY_ENGINE = 2 based on native "requests" retry mechanism.
2021-11-07 23:02:23 +03:00
695c699893 Merge pull request #1353 from coder2020official/master
Bot API 5.4 features
2021-11-06 18:02:01 +03:00
bf96f13296 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-11-06 19:59:59 +05:00
62b1ec04ab Update __init__.py 2021-11-06 19:59:44 +05:00
e412d2f084 Update README.md 2021-11-06 19:56:10 +05:00
8003ff5e59 Delete states.pkl 2021-11-06 19:51:29 +05:00
becce1f580 Update apihelper.py 2021-11-06 19:51:05 +05:00
3a6073e3a0 Update custom_states.py 2021-11-06 13:08:49 +05:00
fc347ae166 Update custom_states.py 2021-11-06 13:06:43 +05:00
8dcfa0c282 Little fix for states 2021-11-06 12:52:41 +05:00
6808ab3ebe Update test_types.py 2021-11-06 12:42:48 +05:00
31097c5380 Update test_types.py 2021-11-06 12:34:49 +05:00
d49c57699e Tests 2021-11-06 12:27:19 +05:00
ed6616e4c7 Bot API 5.4 2021-11-06 12:21:02 +05:00
953e2286b8 Bot API 5.4 2021-11-06 12:15:28 +05:00
06c8782127 Little update
Allowed other handlers, checked methods and other things
2021-11-05 23:22:03 +05:00
2623fa362c Merge pull request #1350 from Badiboy/master
Custom request sender
2021-11-03 18:50:25 +03:00
4a274ba440 Custom request sender
Added apihelper.CUSTOM_REQUEST_SENDER option. It allows to substitute requests.request to your own function.
2021-11-03 18:48:46 +03:00
bfef7e1ce2 Merge pull request #1349 from barbax7/antiflood
Antiflood
2021-11-03 17:38:32 +03:00
558b37b1c3 New antiflood function 2021-11-03 15:30:10 +01:00
d1c26f82aa Merge pull request #1346 from DevAdvik/patch-1
Added A New Bot - @ETHGasFeeTrackerBot
2021-10-24 19:09:04 +03:00
1a351bc8c7 Added A New Bot 2021-10-24 20:38:15 +05:30
27546daad9 Merge pull request #1345 from resinprotein2333/patch-1
Update README.md
2021-10-24 11:48:02 +03:00
bb58d3fead Update README.md
Add my bot into the bot list
2021-10-24 16:45:49 +08:00
099d638a7e Merge pull request #1338 from barbax7/exceptions
Added description of the ApiTelegramException as attribute of the class
2021-10-17 11:29:44 +03:00
5fb48e68a0 Added description of the ApiTelegramException as attribute of the class 2021-10-16 17:45:15 +02:00
b979c2fa1f Merge pull request #1336 from coder2020official/master
File support for states
2021-10-14 17:23:43 +03:00
b6625baec6 Update __init__.py 2021-10-13 19:02:17 +05:00
98044d6faa File support for states
File support. Now states can be saved in pickle file
2021-10-13 18:34:36 +05:00
2113846567 Merge pull request #1330 from Badiboy/master
Bump version 4.1.1
2021-10-09 22:31:59 +03:00
5c9d4edca9 Bump version 4.1.1 2021-10-09 22:31:34 +03:00
4dce9340b0 Merge pull request #1326 from TrevorWinstral/master
Added my bots to README
2021-10-08 13:45:24 +03:00
bf79e8341e Moved bots to bottom of list 2021-10-08 10:39:59 +00:00
dadfdc2f13 Updated README with my bots 2021-10-06 14:15:30 +02:00
585f627e1f Updated README with my bots 2021-10-06 14:14:05 +02:00
eead303d47 Updated README with my bots 2021-10-06 14:13:05 +02:00
14cc15c711 Merge pull request #1325 from coder2020official/master
Critical fix
2021-10-01 21:47:50 +03:00
bf8736e17e Critical fix 2021-10-01 23:29:59 +05:00
5014ca2459 Merge pull request #1324 from coder2020official/master
Update of state handlers
2021-10-01 14:55:09 +03:00
f337abe06e Update __init__.py 2021-10-01 16:09:20 +05:00
ff35f25211 Update __init__.py 2021-10-01 16:08:01 +05:00
2e4280a947 Update of state handlers
No need to create state handlers
2021-10-01 15:56:54 +05:00
4a6b5b3d28 Merge pull request #1322 from Badiboy/master
Bugfix with one_time_keyboard = False
2021-09-30 11:57:54 +03:00
a28af3903d Bugfix with one_time_keyboard = False 2021-09-30 11:56:36 +03:00
d1d5b9effb Merge pull request #1320 from Badiboy/master
polling should leave our world. :)
2021-09-28 19:18:32 +03:00
062fababf2 polling should leave our world. :) 2021-09-28 19:17:09 +03:00
946afcc3c1 Merge pull request #1317 from coder2020official/master
Update handler_backends.py
2021-09-25 21:35:25 +03:00
6e502cd1c6 Merge branch 'master' into master 2021-09-25 23:29:50 +05:00
0a7f897f05 Merge pull request #1318 from Badiboy/master
States minor update
2021-09-25 21:15:52 +03:00
b35f17124f States minor update 2021-09-25 21:15:24 +03:00
44b44ac2c5 Optimization 2021-09-25 23:05:36 +05:00
39e875c1ea Update handler_backends.py 2021-09-25 22:49:32 +05:00
be7317cc86 Merge pull request #1315 from coder2020official/master
States, New filter, and more
2021-09-25 20:43:33 +03:00
e1c33a1de6 Merge pull request #1316 from Badiboy/master
Release 4.1.0
2021-09-25 20:34:05 +03:00
8149551a15 Release 4.1.0 2021-09-25 20:33:32 +03:00
ab648ef3db Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-09-25 22:19:09 +05:00
e721910c0c Update __init__.py 2021-09-25 22:19:07 +05:00
9287eced49 Update a typo 2021-09-25 21:10:29 +05:00
967b94b14f Update handler_backends.py 2021-09-25 20:27:03 +05:00
2df6f00ba5 Optimization
Optimized code, added filters support
2021-09-25 18:22:54 +05:00
beb4f8df44 Update register_handler.py 2021-09-25 17:15:33 +05:00
92ac5a4166 States, and some minor improvements 2021-09-25 17:12:32 +05:00
716323e56a Register_XXX_Handler 2021-09-22 22:46:19 +05:00
cd92d95f91 Create admin_filter_example.py 2021-09-22 22:37:57 +05:00
9c86ed623d Update custom_filters.py 2021-09-22 22:37:18 +05:00
c6ff9b07df Merge pull request #1311 from SwissCorePy/master
added property `user` to TeleBot class
2021-09-20 15:40:14 +03:00
38cc96d0f3 added property user to TeleBot class
Added property `user` to TeleBot class. The idea is to have easy access to the user object representing the bot without doing an API call every time.
2021-09-20 14:31:00 +02:00
82518d8664 Merge pull request #1309 from bim-ba/master
Added new example: anonymous chat-bot
2021-09-19 17:50:51 +03:00
aba2a9e179 Improve readabilty of "elif-zone" 2021-09-19 17:41:07 +03:00
c5c4d081ea Added new example: anonymous chat-bot 2021-09-18 19:46:53 +03:00
f854163626 Merge pull request #1308 from coder2020official/master
Added example and editd description.
2021-09-14 13:08:45 +03:00
fc31a2d466 Update custom_filters.py 2021-09-14 15:02:54 +05:00
86a0a8cd68 Little fixes and example
Fixed is_forwarded custom filter & created example
2021-09-14 15:00:27 +05:00
31c3b3b28e Merge pull request #1307 from coder2020official/master
Update custom_filters.py
2021-09-13 23:26:40 +03:00
b95ab104e3 Update custom_filters.py 2021-09-13 23:09:06 +05:00
a54e4c22a8 Merge pull request #1305 from Badiboy/master
4.0.1 beta release
2021-09-12 21:13:38 +03:00
7913e25be2 4.0.1 beta release 2021-09-12 21:12:19 +03:00
63cbda8890 Merge pull request #1304 from Badiboy/master
README contents update
2021-09-12 20:53:04 +03:00
38851bce22 README contents update 2021-09-12 20:02:49 +03:00
74835c40ea Merge pull request #1303 from Badiboy/master
Update custom filters readme and examples
2021-09-12 19:42:36 +03:00
97e99b4910 Fix 2021-09-12 19:39:26 +03:00
4ced4d29f5 Update custom filters readme and examples 2021-09-12 19:36:23 +03:00
239a90de14 Merge pull request #1302 from coder2020official/master
Some useful filters
2021-09-12 19:14:46 +03:00
c86af0496b Update README.md 2021-09-12 20:43:09 +05:00
4071ab9124 Update README.md 2021-09-12 20:41:26 +05:00
5c715dabc3 Update README.md 2021-09-12 20:40:31 +05:00
43d2d8583e Update README.md 2021-09-12 20:32:16 +05:00
cf78234e3a Update README.md 2021-09-12 20:30:32 +05:00
5f4cd09490 Update README.md 2021-09-12 20:28:46 +05:00
8534804c0c Update README.md 2021-09-12 20:28:01 +05:00
cf75e76e5c Update README.md 2021-09-12 20:27:01 +05:00
7d5e9e5111 Added file custom_filters
Added file with custom filters. Updated the examples
2021-09-12 20:22:26 +05:00
1ceec3cb54 Update custom_filters.py 2021-09-12 19:38:54 +05:00
5f8c75816e Some useful filters
Created useful filters that can be used in message handlers.
Created some examples on using them.
2021-09-12 19:34:43 +05:00
4e37662ab3 upd 2021-09-12 00:30:56 +05:00
a97a917522 Merge pull request #1301 from coder2020official/master
Custom filters example
2021-09-11 22:30:50 +03:00
88f91518c7 Update __init__.py 2021-09-12 00:27:51 +05:00
e89acc8ba6 Update custom_filters.py 2021-09-12 00:27:04 +05:00
5d611ea7f3 upd 2021-09-12 00:24:52 +05:00
5c80f11261 Updated __init__.py 2021-09-12 00:21:35 +05:00
f2202b44fe Merge pull request #1300 from Badiboy/master
Custom filters upd
2021-09-11 21:51:09 +03:00
2da48c0adc Custom filters upd 2021-09-11 21:49:51 +03:00
389407e3ee Merge pull request #1299 from Badiboy/master
Custom filters upd
2021-09-11 21:14:43 +03:00
14be2b8c18 Custom filters upd 2021-09-11 21:10:21 +03:00
df7808264f Merge pull request #1298 from coder2020official/master
Custom filters
2021-09-11 21:03:28 +03:00
9d37503442 reupdated 2021-09-11 23:02:56 +05:00
dd6f39c3cd Merge pull request #1296 from Badiboy/master
Filter clearance
2021-09-11 20:57:55 +03:00
8e4d70b9c6 Update __init__.py 2021-09-11 22:30:53 +05:00
87fb30d57b Update __init__.py 2021-09-11 22:03:37 +05:00
8f3371dcd5 Update __init__.py 2021-09-11 21:59:28 +05:00
ec8975c9e3 Custom filters
Added new feature - from now you can create your own custom filters
2021-09-11 21:47:59 +05:00
16edfbb9dc Warning if commands or content_types filters are strings 2021-09-11 19:26:55 +03:00
f70b135359 Filter clearance
1. Filter optimization: should not store empty filters
2. Filter order: chat_type, content, others
3. Default session timeout set to 600 instead of "forever".
4. Type
2021-09-11 17:02:40 +03:00
78fb69ded1 Merge pull request #1295 from coder2020official/master
is_private filter
2021-09-10 22:17:05 +03:00
0f7eb1571e Update README.md 2021-09-10 20:42:48 +05:00
ac54b7abd4 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-09-10 20:42:51 +05:00
0f3a6393fc Update __init__.py 2021-09-10 20:42:43 +05:00
de6f339cdf Update README.md 2021-09-10 17:57:19 +05:00
d0969bd5f3 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-09-10 17:57:13 +05:00
4035a38507 Update __init__.py 2021-09-10 17:56:44 +05:00
944b077c65 Update README.md 2021-09-10 17:30:58 +05:00
644c6b9082 is_private 2021-09-10 17:30:17 +05:00
dc9f8db556 Merge pull request #1288 from SwissCorePy/master
Added missing content_type "animation"
2021-09-02 22:34:51 +03:00
07ebdeab25 Added missing content_type "animation" 2021-09-02 19:46:01 +02:00
e8738cce7d Merge pull request #1284 from Badiboy/master
Bump version to 4.0 release
2021-08-30 13:50:32 +03:00
d9e638a7df Bump version to 4.0 release 2021-08-30 13:49:28 +03:00
08b1dd31c8 Merge pull request #1277 from fgallaire/master
A Google Cloud Functions webhook
2021-08-25 15:19:51 +03:00
b4f0a6d546 add Google in the name 2021-08-25 14:17:25 +02:00
4eb28df1ab A Google Cloud Functions webhook 2021-08-24 13:01:10 +02:00
50e5e96bb1 Merge pull request #1275 from Badiboy/master
chat_invite_link bugfix
2021-08-22 22:17:30 +03:00
bd3a9bc350 chat_invite_link bugfix 2021-08-22 22:16:03 +03:00
06923c8274 Merge pull request #1272 from Badiboy/master
Simplify and speedup _test_filter
2021-08-20 15:55:10 +03:00
3efc2cf869 Typo 2021-08-19 23:36:37 +03:00
f5de0eeacf Simplify and speedup _test_filter 2021-08-19 22:46:12 +03:00
6e871b8eb1 Merge pull request #1270 from Badiboy/master
Check and update for full compatibility to Bot API up to 5.3
2021-08-18 23:31:15 +03:00
f6359bc32c Readme fix 2021-08-18 23:29:40 +03:00
2bc052ad5a Check and update for full compatibility to Bot API up to 5.3
Pre-release of 4.0.0
2021-08-18 23:27:28 +03:00
022ef6a64c Dependecies clearing 2021-08-18 22:16:30 +03:00
3232811543 Merge pull request #1269 from Badiboy/master
Check and update for full compatibility to Bot API up to 5.0
2021-08-18 22:01:12 +03:00
fabcd93dd7 API update fix 03 2021-08-18 21:57:56 +03:00
8053183cb5 API update fix 02 2021-08-18 19:36:48 +03:00
b2b7d90888 API update fix 01 2021-08-18 19:32:43 +03:00
3e9d73c25d Merge remote-tracking branch 'upstream/master' 2021-08-18 18:52:09 +03:00
d6501ddc0e Check and update for full compatibility to Bot API up to 5.0 2021-08-18 18:47:38 +03:00
e818e3875d Merge pull request #1266 from coder2020official/master
New set of register_xxx_handler functions for dynamic handlers registering.
2021-08-17 16:50:00 +03:00
56cd3971dc Update __init__.py 2021-08-16 22:41:27 +04:00
958ca34e9c Merge pull request #1264 from coder2020official/master
Added skip_pending parameter to polling and infinity_polling in addition to skip_pending in Telebot.Init. Allows skip pending updates for already created Bot instances.
2021-08-16 21:07:41 +03:00
f4ef2366b6 Update skip_updates_example.py 2021-08-16 22:03:17 +04:00
f553960096 Update __init__.py 2021-08-16 22:00:08 +04:00
24ef64456b Update __init__.py 2021-08-16 14:53:00 +04:00
3e7da0fd18 Update skip_updates_example.py 2021-08-16 14:49:45 +04:00
2c0f42b363 Update __init__.py 2021-08-16 14:48:21 +04:00
1e4a6e2125 Update __init__.py 2021-08-15 13:32:11 +04:00
beeb60aab8 skip_updates 2021-08-15 11:40:13 +04:00
5b942a5b31 Merge pull request #1263 from coder2020official/master
Create chat_member_example.py
2021-08-14 17:55:15 +03:00
3e4a6cd702 Create chat_member_example.py 2021-08-14 18:46:45 +04:00
0e369953cb Merge pull request #1261 from Badiboy/master
BotCommandScopeChatMember fix
2021-08-12 15:17:11 +03:00
911e356930 BotCommandScopeChatMember fix 2021-08-12 15:16:04 +03:00
554b39a49a Merge pull request #1257 from AmolDerickSoans/master
Add IPO bot
2021-08-06 14:48:51 +03:00
ea16f35432 Add IPO bot
Listed oneIPO bot created using pyTelegramBotAPI in  section : Bpts using this API
2021-08-06 12:29:00 +05:30
81d94687be Merge pull request #1254 from snikidev/bug/InputInvoiceMessageContent-return-statement
🐛 Bugfix: Add return statement to to_dict() method inside InputInvoiceMessageContent type
2021-08-03 21:03:38 +03:00
4ba4bc18cf add extra space 2021-08-03 17:35:59 +01:00
c117ff2d50 Add return statement to to_dict() method inside InputInvoiceMessageContent 2021-08-03 17:34:29 +01:00
735c224444 Merge pull request #1248 from coder2020official/master
caption_entities in edit_message_caption
2021-07-30 19:11:05 +03:00
81adfd335e UPD 2021-07-30 19:15:37 +05:00
7ebe589b46 Update __init__.py 2021-07-28 23:10:15 +05:00
9c1b19a9e4 upd 2021-07-28 23:06:31 +05:00
02b886465e new filters 2021-07-25 15:46:53 +05:00
2d89ceb745 Merge pull request #1241 from Badiboy/master
Release version 3.8.2
2021-07-21 21:54:29 +03:00
ae8c3252df Release version 3.8.2 2021-07-21 21:53:56 +03:00
7914f71938 Merge pull request #1237 from monosans/comprehension
Replace for loops with comprehensions
2021-07-19 23:40:55 +03:00
097ba9fec2 Replace for loops with comprehensions 2021-07-19 20:03:03 +03:00
d09d9f0c09 Merge pull request #1232 from Badiboy/master
Invoice tips typo fix
2021-07-15 09:27:49 +03:00
29c98b0230 Invoice tips typo fix 2021-07-15 09:27:07 +03:00
2b1db1f1b3 Merge pull request #1231 from vnagornyy/master
Added tip for invoice
2021-07-15 09:23:10 +03:00
fa80b1dba0 Added tip for invoice 2021-07-15 08:56:04 +03:00
b45db584df Merge pull request #1230 from Badiboy/master
Fix worker_pool issue
2021-07-13 22:16:48 +03:00
f52ea635e5 Fix worker_pool issue 2021-07-13 22:09:56 +03:00
9b56afd569 Merge pull request #1229 from Badiboy/master
Fix CallbackQuery issue for games
2021-07-13 20:13:36 +03:00
6fb10e92e4 Fix CallbackQuery issue for games 2021-07-13 20:11:47 +03:00
fcf4d91564 Merge remote-tracking branch 'upstream/master' 2021-07-13 20:00:17 +03:00
38319871e6 Merge pull request #1225 from dannkunt/patch-1
Fix wrong type hint
2021-07-13 11:32:06 +03:00
2d0b092ea4 Fix wrong type hint
call.id gives int
2021-07-10 22:03:31 +03:00
060b8c61bb Merge remote-tracking branch 'upstream/master' 2021-07-09 10:50:53 +03:00
db2accc2f8 Merge pull request #1223 from Badiboy/master
Timeouts in making requests are rethought
2021-07-09 10:50:46 +03:00
798fda4c8a Merge remote-tracking branch 'upstream/master' 2021-07-09 10:50:03 +03:00
2578e48134 Timeouts in making requests are rethought 2021-07-09 10:42:56 +03:00
ac20216a7a Merge pull request #1222 from Badiboy/master
Preserve dict change in de_json routines
2021-07-08 13:43:09 +03:00
beb5a456eb Preserve dict change in Update 2021-07-08 09:35:48 +03:00
41faadd572 Merge pull request #1221 from AndydeCleyre/feature/mentioncolorcodebot
mention colorcodebot as a project using this library
2021-07-08 08:36:22 +03:00
a15016d7d9 mention colorcodebot as a project using this library 2021-07-07 13:00:32 -04:00
47dd84c441 Merge pull request #1216 from SwissCorePy/master
fixed bug
2021-07-01 20:04:37 +03:00
c7b360e982 fixed bug 2021-07-01 18:54:39 +02:00
09041b018f Merge pull request #1215 from SwissCorePy/master
Added the property `difference` to the class ChatMemberUpdated
2021-06-30 17:33:13 +03:00
3a4cf47def Merge branch 'master' of https://github.com/SwissCorePy/pyTelegramBotAPI 2021-06-30 14:16:54 +02:00
56e4f68a83 added the property difference to ChatMemberUpdated 2021-06-30 14:16:38 +02:00
484e7fccbd Merge pull request #1214 from SwissCorePy/master
new deprecated decorator
2021-06-30 15:01:46 +03:00
791d65e95a replaced old deprecated decorator 2021-06-30 13:47:39 +02:00
073d7fb6a7 Update util.py
whoops warn is not optional
2021-06-30 13:11:48 +02:00
a6668397e1 new deprecated decorator
added a new deprecated decorator to util
2021-06-30 13:08:05 +02:00
983d626d87 Merge pull request #1212 from Badiboy/master
Update file_name to visible_file_name in send_document
2021-06-29 13:31:25 +03:00
a4e73a05c6 Update file_name to visible_file_name in send_document 2021-06-29 13:30:01 +03:00
30e304ffb5 Merge pull request #1204 from floydya/file-name-patch
Allows to set visible document file_name on send.
2021-06-29 13:27:44 +03:00
430b34c7a2 Merge pull request #1210 from SwissCorePy/master
README update
2021-06-28 17:15:45 +03:00
b222416fd8 Update README.md 2021-06-28 15:44:49 +02:00
f8110cd046 Update README.md
* Added the new message_handlers
* Added some information about local Bot API Server
* Replaced the split_string with the smart_split function
2021-06-28 15:17:53 +02:00
6bc60f4aa9 Merge pull request #1208 from SwissCorePy/master
get_chat_member_count and ban_chat_member added.
get_chat_members_count and kick_chat_member are marked as deprecated.
2021-06-28 13:09:19 +03:00
b48a445e9f Update __init__.py
updated docstrings
2021-06-28 12:02:40 +02:00
0b383498eb addded logger info for deprecated funcs 2021-06-28 11:59:21 +02:00
2e3b4223a5 Merge pull request #1209 from Badiboy/master
Release 3.8.1 - bugfix
2021-06-28 12:41:38 +03:00
60bb63ab2b Release 3.8.1 - bugfix 2021-06-28 12:41:15 +03:00
0aa7a8a8f6 new 5.3 function names
added the new function names (the previous names are still working) from 5.3 and some other small changes
2021-06-28 09:31:06 +02:00
72ed7c1dde Merge pull request #1207 from Badiboy/master
Post-release fix for infinity_polling
2021-06-27 20:43:34 +03:00
a29c4af2ee Post-release fix for infinity_polling 2021-06-27 20:40:16 +03:00
8d8f234138 Merge pull request #1206 from MAIKS1900/master
2 of 3 Bot API 5.3 changes
2021-06-27 17:33:04 +03:00
491cc05a95 - Set BotCommandScope as abstract class.
- Docstrings from telegram API Scope types
2021-06-27 17:28:11 +03:00
b2c6077f4d Merge branch 'master' of https://github.com/MAIKS1900/pyTelegramBotAPI into master 2021-06-27 15:08:37 +03:00
fb290dc12d Merge pull request #1205 from Badiboy/master
Release version 3.8.0
2021-06-27 13:24:55 +03:00
c088fabe6c Release version 3.8.0 2021-06-27 13:09:08 +03:00
a791ff4e46 Add tests for file sending with name 2021-06-27 11:58:33 +03:00
e56f134a7c Add file_name support to send_document method 2021-06-27 11:38:45 +03:00
38c4c21030 Add file_name argument to send_data method 2021-06-27 11:37:27 +03:00
3e33b7f1cb Bot API 5.3 changes
- Personalized Commands for different chats
- Custom Placeholders of input field for ReplyKeyboardMarkup and ForceReply.
2021-06-26 14:36:14 +03:00
e381671645 Merge pull request #1201 from SwissCorePy/master
Added handlers for `my_chat_member` and `chat_member`.
Added aalowed_updates to polling functions.
2021-06-24 09:06:33 +03:00
ce991e9ac3 Update types.py
added the missing attributes `can_manage_chat` and `can_manage_voice_chats` to ChatMember class
2021-06-23 22:52:24 +02:00
3d5415433e Update __init__.py
Updated TeleBot doc string and added the missing functions to AsyncTeleBot
2021-06-23 22:51:17 +02:00
0bfefdf15d changed allowed_updates in util to update_types
i think its more clear name
2021-06-23 19:57:44 +02:00
506464e637 Update __init__.py
Added the parameter `allowed_updates` to polling and infinity_polling functions
2021-06-23 19:29:36 +02:00
4554cb969f Update __init__.py
added handlers for `my_chat_member` and `chat_member`
2021-06-23 16:10:48 +02:00
65cf841015 Update util.py
added `allowed_updates` list (used by `_init_._retrieve_all_updates` because `chat_member` is not requested by default)
2021-06-23 16:09:40 +02:00
0f0ce934dc Merge pull request #1199 from SwissCorePy/master
added InputInvoiceMessageContent and tgs_sticker support
2021-06-22 17:34:05 +03:00
bffbe764e5 Update tgs_sticker support
* Updated `create_new_sticker_set` and `add_sticker_to_set` functions
* Removed `create_new_animated_sticker_set` and `add_sticker_to_animated_sticker_set` functions
2021-06-22 15:57:34 +02:00
c00595e212 Update types.py
* Added Parameter `caption_entities` to `InputMedia` class
* Added Parameter `disable_content_type_detection` to `InputMediaDocument` class
2021-06-22 15:55:14 +02:00
b20f5b359b Merge pull request #1200 from pablodz/patch-1
Fix long string blocking version of python on github actions setup
2021-06-22 08:29:50 +03:00
558eef78b4 Fix long string blocking version of python on github actions setup 2021-06-21 17:27:35 -05:00
3f46ce3b7b added InputInvoiceMessageContent and tgs_sticker support
and some small changes
2021-06-21 19:59:39 +02:00
69e8edef19 Merge pull request #1198 from SwissCorePy/master
Added some missing features
2021-06-21 20:48:04 +03:00
d3369245c4 fixed wrong type hint 2021-06-21 17:49:03 +02:00
55e9f2095e Merge branch 'master' of https://github.com/eternnoir/pyTelegramBotAPI 2021-06-21 17:39:57 +02:00
7118613ef7 Added missing features
* added some missing features of TelegramBotAPI 4.6-5.2 to pyTelegramBotAPI
* added type hints to (almost) all public TeleBot functions
2021-06-21 17:39:13 +02:00
105d65d5ce Merge pull request #1197 from vishal2376/master
Add developer bot
2021-06-21 14:16:37 +03:00
f11bf08ba1 Update README.md 2021-06-21 16:30:17 +05:30
66598e39fe Change description of developer bot 2021-06-21 16:27:32 +05:30
4146b50384 Add developer bot 2021-06-21 13:16:22 +05:30
f62d642572 Merge pull request #1196 from leonheess/patch-1
Add Anti-Tracking Bot
2021-06-20 19:33:50 +03:00
18f1fd42b0 Add Anti-Tracking Bot 2021-06-20 13:14:55 +02:00
07d198aebe Merge pull request #1195 from SwissCorePy/master
Added class ChatMemberUpdated
2021-06-19 22:18:50 +03:00
0370a9f277 Added class ChatMemberUpdated
* Added class `ChatMemberUpdated` to types
* Simplified `de_json` functions in `WebhookInfo` and `Update` classes (for overall more consistent code)
* changed `options_ids` to ´option_id` in class `PollAnswer`
* Added test for `ChatMemberUpdated` class in `test_types.py` and added the fields `my_chat_member` and `chat_member` to the `Update` class and its tests
2021-06-19 20:13:53 +02:00
22d3ac027a Merge pull request #1194 from SwissCorePy/master
Minor updates to the https://github.com/eternnoir/pyTelegramBotAPI/pull/1191
2021-06-19 19:25:04 +03:00
795f7fff7f Some small changes
* Fixed type warnings in some editors by changing `var: Type = None` to `var: Union[Type, None] = None`
* changed some args from `obj['arg']` to `obj.get('arg')` if arg is optional
* better PEP-8 compliance for less weak warnings
* added tests for the new type `ChatInviteLink`
2021-06-19 17:59:55 +02:00
ab6d40a072 Merge pull request #1193 from Badiboy/master
Raise exception if no token passed
2021-06-19 15:10:32 +03:00
d26923e167 Raise exception if no token passed 2021-06-19 15:09:52 +03:00
05aff236c1 Merge pull request #1191 from SwissCorePy/master
Huge update with type checking and some new properties.

Note: should be presisely tested before publishing.
2021-06-19 15:00:12 +03:00
a9ae070256 Update types.py 2021-06-18 22:37:31 +02:00
63fe6e01d1 Fixed the errors from my last PRs
I testet all using pytest and python versions 3.6-3.9 on macOS
2021-06-18 22:35:49 +02:00
bbafdd1c1d Some Updates
> Added lot of type hints to types.py
> Added some new fields from TelegramBotAPI to pyTelegramBotAPI
> fixed `circular import error in util.py
> Added functions `log_out` and `close` to __init__.py and apihelper.py
> And some more small changes
2021-06-17 20:28:53 +02:00
fe9df2df8c Merge pull request #1186 from AREEG94FAHAD/master
Add to translator bot
2021-06-12 21:13:41 +03:00
b0b8623dce Merge pull request #1 from AREEG94FAHAD/Cryptocurrency
Update README.md
2021-06-12 19:02:06 +03:00
a01e59951a Update README.md 2021-06-12 19:01:42 +03:00
d5c202abbd Update README.md 2021-06-12 18:19:18 +03:00
81299ff613 Update README.md 2021-06-12 18:18:51 +03:00
25bac68309 Update README.md 2021-06-12 18:18:16 +03:00
a05324bdad Merge pull request #1181 from pablodz/master
Github Actions: setup for 3.6+ pypy3.6+
2021-06-09 17:29:57 +03:00
74c4ab2f04 Merge pull request #1183 from SwissCorePy/master
Fixed a bug in `user_link`
2021-06-09 17:28:13 +03:00
ab05cb0045 Fixed a bug in user_link
`user_link` returned an empty string if `include_id` was set to False
2021-06-09 16:20:42 +02:00
709eb8cf45 Github Actions: setup for 3.6+ pypy3.6+ 2021-06-06 14:30:55 -05:00
643cdeceee Merge pull request #1179 from Badiboy/master
Fix special case when content_type is None
2021-06-04 12:29:05 +03:00
2add34c702 Fix special case when content_type is None 2021-06-04 12:28:33 +03:00
877397a46b Merge pull request #1178 from Badiboy/master
Partial rollback for previous update
2021-06-04 12:12:26 +03:00
afbc67795a Partial rollback for previous update 2021-06-04 12:11:37 +03:00
da5dc20b3a Merge pull request #1176 from SwissCorePy/master
Added some functions and type hints
2021-06-03 23:45:21 +03:00
ed5e5e5077 Update util.py
- Removed function `unix_time`
- Added function `escape`
- Added function `user_link`
- Added function `quick_markup`
- Added some type hints
2021-06-03 19:51:33 +02:00
9a6ddce8df Added the function unix_time 2021-06-03 19:06:53 +02:00
db8478d0a4 Merge pull request #1174 from SwissCorePy/master
Add `smart_split` function to utils.py
2021-06-03 19:55:44 +03:00
20030f47af Update util.py
Added the function `smart_split` to split text into meaningful parts.
2021-06-03 18:51:32 +02:00
f7cf1965cb Merge pull request #1168 from anvarjamgirov/patch-1
Bug fixed on set_game_score
2021-06-01 08:30:42 +03:00
aea067f789 Bug fixed on set_game_score
fixed wrong ordered argument error on calling apihelper.set_game_score method in set_game_score
2021-06-01 08:39:09 +05:00
e2c20c1e55 Merge pull request #1167 from zora89/master
typo corrected.
2021-05-25 23:56:01 +03:00
1209281787 type corrected. 2021-05-26 02:17:49 +05:30
742f67c85b Merge pull request #1163 from yehwankim23/patch-1
Fix typo
2021-05-20 12:07:34 +03:00
e22fcbe3c0 Fix typo 2021-05-20 18:01:10 +09:00
d3998dfadb Merge pull request #1161 from yarreg/feature/invite-link-api
Create_chat_invite_link, edit_chat_invite_link, revoke_chat_invite_link methods
2021-05-19 11:30:06 +03:00
ff54f194ad Added: create_chat_invite_link, edit_chat_invite_link, revoke_chat_invite_link methods 2021-05-19 11:22:40 +03:00
f6b967421e Merge pull request #1160 from Badiboy/master
Update version.py
2021-05-15 20:30:27 +03:00
59559199d5 Update version.py 2021-05-15 20:29:58 +03:00
98784c811e Merge pull request #1159 from Badiboy/master
Fix release 3.7.8u1 (update was inconsistent, sorry)
2021-05-15 20:28:51 +03:00
26e5f3d3a8 Fix release 3.7.8u1 2021-05-15 20:27:52 +03:00
fe1f99abdf Merge pull request #1158 from Badiboy/master
send_poll fix of fix
2021-05-15 20:09:49 +03:00
7540a26fb9 send_poll fix of fix
Previous update was inconsistent, sorry.
2021-05-15 20:08:51 +03:00
61 changed files with 10179 additions and 1961 deletions

35
.github/workflows/setup_python.yml vendored Normal file
View File

@ -0,0 +1,35 @@
# This is a basic workflow to help you get started with Actions
name: Setup
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
#workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
all-setups:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.6', 'pypy-3.7' ] #'pypy-3.8', 'pypy-3.9' NOT SUPPORTED NOW
name: ${{ matrix.python-version }} and tests
steps:
- uses: actions/checkout@v2
- name: Setup python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- run: |
pip3 install -r requirements.txt
python setup.py install
cd tests && py.test

2
.gitignore vendored
View File

@ -25,6 +25,7 @@ var/
.idea/
venv/
.venv/
# PyInstaller
# Usually these files are written by a python script from a template
@ -62,3 +63,4 @@ testMain.py
#VS Code
.vscode/
.DS_Store

View File

@ -4,6 +4,7 @@ python:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "pypy3"
install: "pip install -r requirements.txt"
script:

448
README.md
View File

@ -1,13 +1,19 @@
# <p align="center">pyTelegramBotAPI
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
[![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
[![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/)
* [Getting started.](#getting-started)
# <p align="center">pyTelegramBotAPI
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Supports both sync and async ways.</p>
## <p align="center">Supporting Bot API version: <a href="https://core.telegram.org/bots/api#november-5-2021">5.4</a>!
## Contents
* [Getting started](#getting-started)
* [Writing your first bot](#writing-your-first-bot)
* [Prerequisites](#prerequisites)
* [A simple echo bot](#a-simple-echo-bot)
@ -16,32 +22,48 @@
* [Methods](#methods)
* [General use of the API](#general-use-of-the-api)
* [Message handlers](#message-handlers)
* [Edited Message handler](#edited-message-handler)
* [Channel Post handler](#channel-post-handler)
* [Edited Channel Post handler](#edited-channel-post-handler)
* [Callback Query handlers](#callback-query-handler)
* [Middleware handlers](#middleware-handler)
* [Shipping Query Handler](#shipping-query-handler)
* [Pre Checkout Query Handler](#pre-checkout-query-handler)
* [Poll Handler](#poll-handler)
* [Poll Answer Handler](#poll-answer-handler)
* [My Chat Member Handler](#my-chat-member-handler)
* [Chat Member Handler](#chat-member-handler)
* [Chat Join request handler](#chat-join-request-handler)
* [Inline Mode](#inline-mode)
* [Inline handler](#inline-handler)
* [Chosen Inline handler](#chosen-inline-handler)
* [Answer Inline Query](#answer-inline-query)
* [Additional API features](#additional-api-features)
* [Middleware handlers](#middleware-handlers)
* [Custom filters](#custom-filters)
* [TeleBot](#telebot)
* [Reply markup](#reply-markup)
* [Inline Mode](#inline-mode)
* [Advanced use of the API](#advanced-use-of-the-api)
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
* [Using local Bot API Server](#using-local-bot-api-sever)
* [Asynchronous TeleBot](#asynchronous-telebot)
* [Sending large text messages](#sending-large-text-messages)
* [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot)
* [The listener mechanism](#the-listener-mechanism)
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [Proxy](#proxy)
* [Testing](#testing)
* [API conformance](#api-conformance)
* [Change log](#change-log)
* [AsyncTeleBot](#asynctelebot)
* [F.A.Q.](#faq)
* [Bot 2.0](#bot-20)
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
* [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors)
* [The Telegram Chat Group](#the-telegram-chat-group)
* [More examples](#more-examples)
* [Bots using this API](#bots-using-this-api)
## Getting started.
## Getting started
This API is tested with Python Python 3.6-3.9 and Pypy 3.
This API is tested with Python 3.6-3.10 and Pypy 3.
There are two ways to install the library:
* Installation using pip (a Python package manager)*:
@ -103,13 +125,13 @@ This one echoes all incoming text messages back to the sender. It uses a lambda
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
```python
bot.polling()
bot.infinity_polling()
```
Alright, that's it! Our source file now looks like this:
```python
import telebot
bot = telebot.TeleBot("TOKEN")
bot = telebot.TeleBot("YOUR_BOT_TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
@ -119,7 +141,7 @@ def send_welcome(message):
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
bot.infinity_polling()
```
To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages.
@ -146,7 +168,7 @@ Outlined below are some general use cases of the API.
#### Message handlers
A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters.
Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
Each filter must return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
```python
@bot.message_handler(filters)
def function_name(message):
@ -162,8 +184,9 @@ TeleBot supports the following filters:
|content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.|
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))|
|commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.|
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
|chat_types|list of chat types|`True` if `message.chat.type` in your filter|
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`|
Here are some examples of using the filters and message handlers:
```python
@ -207,28 +230,100 @@ def send_something(message):
```
**Important: all handlers are tested in the order in which they were declared**
#### Edited Message handlers
#### Edited Message handler
Handle edited messages
`@bot.edited_message_handler(filters) # <- passes a Message type object to your function`
@bot.edited_message_handler(filters)
#### Channel Post handler
Handle channel post messages
`@bot.channel_post_handler(filters) # <- passes a Message type object to your function`
#### channel_post_handler
@bot.channel_post_handler(filters)
#### edited_channel_post_handler
@bot.edited_channel_post_handler(filters)
#### Edited Channel Post handler
Handle edited channel post messages
`@bot.edited_channel_post_handler(filters) # <- passes a Message type object to your function`
#### Callback Query Handler
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries.
Handle callback queries
```python
@bot.callback_query_handler(func=lambda call: True)
def test_callback(call):
def test_callback(call): # <- passes a CallbackQuery type object to your function
logger.info(call)
```
#### Middleware Handler
#### Shipping Query Handler
Handle shipping queries
`@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function`
#### Pre Checkout Query Handler
Handle pre checkoupt queries
`@bot.pre_checkout_query_handler() # <- passes a PreCheckoutQuery type object to your function`
#### Poll Handler
Handle poll updates
`@bot.poll_handler() # <- passes a Poll type object to your function`
#### Poll Answer Handler
Handle poll answers
`@bot.poll_answer_handler() # <- passes a PollAnswer type object to your function`
#### My Chat Member Handler
Handle updates of a the bot's member status in a chat
`@bot.my_chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
#### Chat Member Handler
Handle updates of a chat member's status in a chat
`@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
*Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`*
#### Chat Join Request Handler
Handle chat join requests using:
`@bot.chat_join_request_handler() # <- passes ChatInviteLink type object to your function`
### Inline Mode
More information about [Inline mode](https://core.telegram.org/bots/inline).
#### Inline handler
Now, you can use inline_handler to get inline queries in telebot.
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
# Query message is text
```
#### Chosen Inline handler
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
command for @Botfather.
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
```python
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
def test_chosen(chosen_inline_result):
# Process all chosen_inline_result.
```
#### Answer Inline Query
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.'))
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
```
### Additional API features
#### Middleware Handlers
A middleware handler is a function that allows you to modify requests or the bot context as they pass through the
Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are
@ -248,6 +343,43 @@ def start(message):
assert message.another_text == message.text + ':changed'
```
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
#### Custom filters
Also, you can use built-in custom filters. Or, you can create your own filter.
[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/general_custom_filters.py)
Also, we have examples on them. Check this links:
You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py)
Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/id_filter_example.py)
Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/text_filter_example.py)
If you want to add some built-in filter, you are welcome to add it in custom_filters.py file.
Here is example of creating filter-class:
```python
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
# Class will check whether the user is admin or creator in group or not
key='is_admin'
@staticmethod
def check(message: telebot.types.Message):
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
# To register filter, you need to use method add_custom_filter.
bot.add_custom_filter(IsAdmin())
# Now, you can use it in handler.
@bot.message_handler(is_admin=True)
def admin_of_group(message):
bot.send_message(message.chat.id, 'You are admin of this group!')
```
#### TeleBot
```python
@ -257,11 +389,10 @@ TOKEN = '<token_string>'
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
# - interval: True/False (default False) - The interval between polling requests
# Note: Editing this parameter harms the bot's response time
# - interval: int (default 0) - The interval between polling requests
# - timeout: integer (default 20) - Timeout in seconds for long polling.
tb.polling(none_stop=False, interval=0, timeout=20)
# - allowed_updates: List of Strings (default None) - List of update types to request
tb.infinity_polling(interval=0, timeout=20)
# getMe
user = tb.get_me()
@ -273,6 +404,7 @@ tb.remove_webhook()
# getUpdates
updates = tb.get_updates()
# or
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
@ -394,49 +526,8 @@ ForceReply:
![ForceReply](https://farm4.staticflickr.com/3809/32418726814_d1baec0fc2_o_d.jpg "ForceReply")
### Inline Mode
More information about [Inline mode](https://core.telegram.org/bots/inline).
#### inline_handler
Now, you can use inline_handler to get inline queries in telebot.
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
# Query message is text
```
#### chosen_inline_handler
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
command for @Botfather.
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
```python
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
def test_chosen(chosen_inline_result):
# Process all chosen_inline_result.
```
#### answer_inline_query
```python
@bot.inline_handler(lambda query: query.query == 'text')
def query_text(inline_query):
try:
r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.'))
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
bot.answer_inline_query(inline_query.id, [r, r2])
except Exception as e:
print(e)
```
### Working with entities:
### Working with entities
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
Attributes:
* `type`
@ -454,26 +545,38 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta
## Advanced use of the API
### Asynchronous delivery of messages
There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up you bot __significantly__, but it has unwanted side effects if used without caution.
### Using local Bot API Sever
Since version 5.0 of the Bot API, you have the possibility to run your own [Local Bot API Server](https://core.telegram.org/bots/api#using-a-local-bot-api-server).
pyTelegramBotAPI also supports this feature.
```python
from telebot import apihelper
apihelper.API_URL = "http://localhost:4200/bot{0}/{1}"
```
**Important: Like described [here](https://core.telegram.org/bots/api#logout), you have to log out your bot from the Telegram server before switching to your local API server. in pyTelegramBotAPI use `bot.log_out()`**
*Note: 4200 is an example port*
### Asynchronous TeleBot
New: There is an asynchronous implementation of telebot.
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
```python
tb = telebot.AsyncTeleBot("TOKEN")
```
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
Now, every function that calls the Telegram API is executed in a separate asynchronous task.
Using AsyncTeleBot allows you to do the following:
```python
import telebot
tb = telebot.AsyncTeleBot("TOKEN")
task = tb.get_me() # Execute an API call
# Do some other operations...
a = 0
for a in range(100):
a += 10
result = task.wait() # Get the result of the execution
@tb.message_handler(commands=['start'])
async def start_message(message):
await bot.send_message(message.chat.id, 'Hello!')
```
*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.*
See more in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot)
### Sending large text messages
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
@ -484,6 +587,19 @@ large_text = open("large_text.txt", "rb").read()
# Split the text each 3000 characters.
# split_string returns a list with the splitted text.
splitted_text = util.split_string(large_text, 3000)
for text in splitted_text:
tb.send_message(chat_id, text)
```
Or you can use the new `smart_split` function to get more meaningful substrings:
```python
from telebot import util
large_text = open("large_text.txt", "rb").read()
# Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096)
# Splits by last '\n', '. ' or ' ' in exactly this priority.
# smart_split returns a list with the splitted text.
splitted_text = util.smart_split(large_text, chars_per_string=3000)
for text in splitted_text:
tb.send_message(chat_id, text)
```
@ -505,7 +621,7 @@ def handle_messages(messages):
bot.reply_to(message, 'Hi')
bot.set_update_listener(handle_messages)
bot.polling()
bot.infinity_polling()
```
### Using web hooks
@ -514,7 +630,6 @@ When using webhooks telegram sends one Update per call, for processing it you sh
There are some examples using webhooks in the [examples/webhook_examples](examples/webhook_examples) directory.
### Logging
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
@ -526,13 +641,12 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
```
### Proxy
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
```python
from telebot import apihelper
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
apihelper.proxy = {'http':'http://127.0.0.1:3128'}
```
If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`.
@ -541,18 +655,51 @@ If you want to use socket5 proxy you need install dependency `pip install reques
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
### Testing
You can disable or change the interaction with real Telegram server by using
```python
apihelper.CUSTOM_REQUEST_SENDER = your_handler
```
parameter. You can pass there your own function that will be called instead of _requests.request_.
For example:
```python
def custom_sender(method, url, **kwargs):
print("custom_sender. method: {}, url: {}, params: {}".format(method, url, kwargs.get("params")))
result = util.CustomRequestResponse('{"ok":true,"result":{"message_id": 1, "date": 1, "chat": {"id": 1, "type": "private"}}}')
return result
```
Then you can use API and proceed requests in your handler code.
```python
apihelper.CUSTOM_REQUEST_SENDER = custom_sender
tb = TeleBot("test")
res = tb.send_message(123, "Test")
```
Result will be:
`custom_sender. method: post, url: https://api.telegram.org/botololo/sendMessage, params: {'chat_id': '123', 'text': 'Test'}`
## API conformance
_Checking is in progress..._
[Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._
* ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021)
* [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember
* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021)
* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021)
* ✔ [Bot API 5.0](https://core.telegram.org/bots/api-changelog#november-4-2020)
* ✔ [Bot API 4.9](https://core.telegram.org/bots/api-changelog#june-4-2020)
* ✔ [Bot API 4.8](https://core.telegram.org/bots/api-changelog#april-24-2020)
* ✔ [Bot API 4.7](https://core.telegram.org/bots/api-changelog#march-30-2020)
* ✔ [Bot API 4.6](https://core.telegram.org/bots/api-changelog#january-23-2020)
* [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) - No nested MessageEntities and Markdown2 support
* ✔ [Bot API 4.4](https://core.telegram.org/bots/api-changelog#july-29-2019)
* ✔ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019)
* ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019)
* [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support.
* [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support.
* [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support
* [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support
* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018)
* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017)
* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017)
@ -567,23 +714,54 @@ _Checking is in progress..._
* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016)
## Change log
## AsyncTeleBot
### Asynchronous version of telebot
We have a fully asynchronous version of TeleBot.
This class is not controlled by threads. Asyncio tasks are created to execute all the stuff.
27.04.2020 - Poll and Dice are up to date.
Python2 conformance is not checked any more due to EOL.
### EchoBot
Echo Bot example on AsyncTeleBot:
```python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking.
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
async def send_welcome(message):
await bot.reply_to(message, """\
Hi there, I am EchoBot.
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
""")
# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
@bot.message_handler(func=lambda message: True)
async def echo_message(message):
await bot.reply_to(message, message.text)
bot.polling()
```
As you can see here, keywords are await and async.
### Why should I use async?
Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other.
### Differences in AsyncTeleBot
AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot/middleware)
### Examples
See more examples in our [examples](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder
## F.A.Q.
### Bot 2.0
April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface.
[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130)
### How can I distinguish a User and a GroupChat in message.chat?
Telegram Bot API support new type Chat for message.chat.
@ -591,7 +769,7 @@ Telegram Bot API support new type Chat for message.chat.
-
```python
if message.chat.type == "private":
# private chat message
# private chat message
if message.chat.type == "group":
# group chat message
@ -613,7 +791,6 @@ Bot instances that were idle for a long time might be rejected by the server whe
Get help. Discuss. Chat.
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
## More examples
@ -624,66 +801,49 @@ Get help. Discuss. Chat.
## Bots using this API
* [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes
* [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger*
* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files.
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you.
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander.
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`.
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you.
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte*
* [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie*
* [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger)
* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi*
* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images.
* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall.
* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin.
* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025
* [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot) - DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev.
* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number.
* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00
* [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram
* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz)
* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful.
* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic
* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video.
* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service.
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages vocabulary.
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples and try to name the performer of the song.
* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon)
* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm).
* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies.
* [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain)
* [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students.
* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given IDs
* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe.
* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian).
* [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers.
* [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov.
* [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram.
* [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...)
* [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering
* [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates.
* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network.
* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications.
* [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read.
* [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake.
* [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites.
* [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram.
* [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot.
* [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want.
* [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast.
* [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour)
* [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani)
* [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user.
* [COVID-19 Galicia Bot](https://t.me/covid19_galicia_bot) by [@dgarcoe](https://github.com/dgarcoe) This bot provides daily data related to the COVID19 crisis in Galicia (Spain) obtained from official government sources.
* [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server.
* [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls.
* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon)
* [Price Tracker](https://t.me/trackokbot) by [@barbax7](https://github.com/barbax7). This bot tracks amazon.it product's prices the user is interested to and notify him when one price go down.
* [Torrent Hunt](https://t.me/torrenthuntbot) by [@Hemantapkh](https://github.com/hemantapkh/torrenthunt). Torrent Hunt bot is a multilingual bot with inline mode support to search and explore torrents from 1337x.to.
**Want to have your bot listed here? Just make a pull request.**
* [Translator bot](https://github.com/AREEG94FAHAD/translate_text_bot) by Areeg Fahad. This bot can be used to translate texts.
* [Digital Cryptocurrency bot](https://github.com/AREEG94FAHAD/currencies_bot) by Areeg Fahad. With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency.
* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent.
* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems)
* [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings)
* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user
* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded
* [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities.
* [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI
* [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet.
* [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development.
**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.**

View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
This Example will show you how to use CallbackData
"""
from telebot.callback_data import CallbackData, CallbackDataFilter
from telebot import types, TeleBot
from telebot.custom_filters import AdvancedCustomFilter
API_TOKEN = ''
PRODUCTS = [
{'id': '0', 'name': 'xiaomi mi 10', 'price': 400},
{'id': '1', 'name': 'samsung s20', 'price': 800},
{'id': '2', 'name': 'iphone 13', 'price': 1300}
]
bot = TeleBot(API_TOKEN)
products_factory = CallbackData('product_id', prefix='products')
def products_keyboard():
return types.InlineKeyboardMarkup(
keyboard=[
[
types.InlineKeyboardButton(
text=product['name'],
callback_data=products_factory.new(product_id=product["id"])
)
]
for product in PRODUCTS
]
)
def back_keyboard():
return types.InlineKeyboardMarkup(
keyboard=[
[
types.InlineKeyboardButton(
text='',
callback_data='back'
)
]
]
)
class ProductsCallbackFilter(AdvancedCustomFilter):
key = 'config'
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
@bot.message_handler(commands=['products'])
def products_command_handler(message: types.Message):
bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard())
# Only product with field - product_id = 2
@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2'))
def product_one_callback(call: types.CallbackQuery):
bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True)
# Any other products
@bot.callback_query_handler(func=None, config=products_factory.filter())
def products_callback(call: types.CallbackQuery):
callback_data: dict = products_factory.parse(callback_data=call.data)
product_id = int(callback_data['product_id'])
product = PRODUCTS[product_id]
text = f"Product name: {product['name']}\n" \
f"Product price: {product['price']}"
bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
text=text, reply_markup=back_keyboard())
@bot.callback_query_handler(func=lambda c: c.data == 'back')
def back_callback(call: types.CallbackQuery):
bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
text='Products:', reply_markup=products_keyboard())
bot.add_custom_filter(ProductsCallbackFilter())
bot.infinity_polling()

117
examples/anonymous_bot.py Normal file
View File

@ -0,0 +1,117 @@
# This bot is needed to connect two people and their subsequent anonymous communication
#
# Avaiable commands:
# `/start` - Just send you a messsage how to start
# `/find` - Find a person you can contact
# `/stop` - Stop active conversation
import telebot
from telebot import types
# Initialize bot with your token
bot = telebot.TeleBot(TOKEN)
# The `users` variable is needed to contain chat ids that are either in the search or in the active dialog, like {chat_id, chat_id}
users = {}
# The `freeid` variable is needed to contain chat id, that want to start conversation
# Or, in other words: chat id of user in the search
freeid = None
# `/start` command handler
#
# That command only sends you 'Just use /find command!'
@bot.message_handler(commands=['start'])
def start(message: types.Message):
bot.send_message(message.chat.id, 'Just use /find command!')
# `/find` command handler
#
# That command finds opponent for you
#
# That command according to the following principle:
# 1. You have written `/find` command
# 2. If you are already in the search or have an active dialog, bot sends you 'Shut up!'
# 3. If not:
# 3.1. Bot sends you 'Finding...'
# 3.2. If there is no user in the search:
# 3.2.2. `freeid` updated with `your_chat_id`
# 3.3. If there is user in the search:
# 3.3.1. Both you and the user in the search recieve the message 'Founded!'
# 3.3.2. `users` updated with a {user_in_the_search_chat_id, your_chat_id}
# 3.3.3. `users` updated with a {your_chat_id, user_in_the_search_id}
# 3.3.4. `freeid` updated with `None`
@bot.message_handler(commands=['find'])
def find(message: types.Message):
global freeid
if message.chat.id not in users:
bot.send_message(message.chat.id, 'Finding...')
if freeid == None:
freeid = message.chat.id
else:
# Question:
# Is there any way to simplify this like `bot.send_message([message.chat.id, freeid], 'Founded!')`?
bot.send_message(message.chat.id, 'Founded!')
bot.send_message(freeid, 'Founded!')
users[freeid] = message.chat.id
users[message.chat.id] = freeid
freeid = None
print(users, freeid) # Debug purpose, you can remove that line
else:
bot.send_message(message.chat.id, 'Shut up!')
# `/stop` command handler
#
# That command stops your current conversation (if it exist)
#
# That command according to the following principle:
# 1. You have written `/stop` command
# 2. If you are not have active dialog or you are not in search, bot sends you 'You are not in search!'
# 3. If you are in active dialog:
# 3.1. Bot sends you 'Stopping...'
# 3.2. Bot sends 'Your opponent is leavin`...' to your opponent
# 3.3. {your_opponent_chat_id, your_chat_id} removes from `users`
# 3.4. {your_chat_id, your_opponent_chat_id} removes from `users`
# 4. If you are only in search:
# 4.1. Bot sends you 'Stopping...'
# 4.2. `freeid` updated with `None`
@bot.message_handler(commands=['stop'])
def stop(message: types.Message):
global freeid
if message.chat.id in users:
bot.send_message(message.chat.id, 'Stopping...')
bot.send_message(users[message.chat.id], 'Your opponent is leavin`...')
del users[users[message.chat.id]]
del users[message.chat.id]
print(users, freeid) # Debug purpose, you can remove that line
elif message.chat.id == freeid:
bot.send_message(message.chat.id, 'Stopping...')
freeid = None
print(users, freeid) # Debug purpose, you can remove that line
else:
bot.send_message(message.chat.id, 'You are not in search!')
# message handler for conversation
#
# That handler needed to send message from one opponent to another
# If you are not in `users`, you will recieve a message 'No one can hear you...'
# Otherwise all your messages are sent to your opponent
#
# Questions:
# 1. Is there any way to improve readability like `content_types=['all']`?
# 2. Is there any way to register this message handler only when i found the opponent?
@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'poll', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice'])
def chatting(message: types.Message):
if message.chat.id in users:
bot.copy_message(users[message.chat.id], users[users[message.chat.id]], message.id)
else:
bot.send_message(message.chat.id, 'No one can hear you...')
bot.infinity_polling(skip_pending=True)

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
"""
This Example will show you how to use CallbackData
"""
from telebot.callback_data import CallbackData, CallbackDataFilter
from telebot import types
from telebot.async_telebot import AsyncTeleBot
from telebot.asyncio_filters import AdvancedCustomFilter
API_TOKEN = 'TOKEN'
PRODUCTS = [
{'id': '0', 'name': 'xiaomi mi 10', 'price': 400},
{'id': '1', 'name': 'samsung s20', 'price': 800},
{'id': '2', 'name': 'iphone 13', 'price': 1300}
]
bot = AsyncTeleBot(API_TOKEN)
products_factory = CallbackData('product_id', prefix='products')
def products_keyboard():
return types.InlineKeyboardMarkup(
keyboard=[
[
types.InlineKeyboardButton(
text=product['name'],
callback_data=products_factory.new(product_id=product["id"])
)
]
for product in PRODUCTS
]
)
def back_keyboard():
return types.InlineKeyboardMarkup(
keyboard=[
[
types.InlineKeyboardButton(
text='',
callback_data='back'
)
]
]
)
class ProductsCallbackFilter(AdvancedCustomFilter):
key = 'config'
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
@bot.message_handler(commands=['products'])
async def products_command_handler(message: types.Message):
await bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard())
# Only product with field - product_id = 2
@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2'))
async def product_one_callback(call: types.CallbackQuery):
await bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True)
# Any other products
@bot.callback_query_handler(func=None, config=products_factory.filter())
async def products_callback(call: types.CallbackQuery):
callback_data: dict = products_factory.parse(callback_data=call.data)
product_id = int(callback_data['product_id'])
product = PRODUCTS[product_id]
text = f"Product name: {product['name']}\n" \
f"Product price: {product['price']}"
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
text=text, reply_markup=back_keyboard())
@bot.callback_query_handler(func=lambda c: c.data == 'back')
async def back_callback(call: types.CallbackQuery):
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
text='Products:', reply_markup=products_keyboard())
bot.add_custom_filter(ProductsCallbackFilter())
bot.polling()

View File

@ -0,0 +1,11 @@
from telebot.async_telebot import AsyncTeleBot
import telebot
bot = AsyncTeleBot('TOKEN')
@bot.chat_join_request_handler()
async def make_some(message: telebot.types.ChatJoinRequest):
await bot.send_message(message.chat.id, 'I accepted a new user!')
await bot.approve_chat_join_request(message.chat.id, message.from_user.id)
bot.polling(skip_pending=True)

View File

@ -0,0 +1,33 @@
from telebot import types,util
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member.
@bot.chat_member_handler()
async def chat_m(message: types.ChatMemberUpdated):
old = message.old_chat_member
new = message.new_chat_member
if new.status == "member":
await bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message
#if bot is added to group, this handler will work
@bot.my_chat_member_handler()
async def my_chat_m(message: types.ChatMemberUpdated):
old = message.old_chat_member
new = message.new_chat_member
if new.status == "member":
await bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group
await bot.leave_chat(message.chat.id)
#content_Type_service is:
#'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
#'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended',
#'voice_chat_participants_invited', 'message_auto_delete_timer_changed'
# this handler deletes service messages
@bot.message_handler(content_types=util.content_type_service)
async def delall(message: types.Message):
await bot.delete_message(message.chat.id,message.message_id)
bot.polling()

View File

@ -0,0 +1,12 @@
from telebot.async_telebot import AsyncTeleBot
from telebot import asyncio_filters
bot = AsyncTeleBot('TOKEN')
# Handler
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
async def answer_for_admin(message):
await bot.send_message(message.chat.id,"hello my admin")
# Register filter
bot.add_custom_filter(asyncio_filters.IsAdminFilter(bot))
bot.polling()

View File

@ -0,0 +1,43 @@
from telebot.async_telebot import AsyncTeleBot
import telebot
bot = AsyncTeleBot('TOKEN')
# AdvancedCustomFilter is for list, string filter values
class MainFilter(telebot.asyncio_filters.AdvancedCustomFilter):
key='text'
@staticmethod
async def check(message, text):
return message.text in text
# SimpleCustomFilter is for boolean values, such as is_admin=True
class IsAdmin(telebot.asyncio_filters.SimpleCustomFilter):
key='is_admin'
@staticmethod
async def check(message: telebot.types.Message):
result = await bot.get_chat_member(message.chat.id,message.from_user.id)
return result.status in ['administrator','creator']
@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin
async def admin_rep(message):
await bot.send_message(message.chat.id, "Hi admin")
@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin
async def not_admin(message):
await bot.send_message(message.chat.id, "You are not admin")
@bot.message_handler(text=['hi']) # Response to hi message
async def welcome_hi(message):
await bot.send_message(message.chat.id, 'You said hi')
@bot.message_handler(text=['bye']) # Response to bye message
async def bye_user(message):
await bot.send_message(message.chat.id, 'You said bye')
# Do not forget to register filters
bot.add_custom_filter(MainFilter())
bot.add_custom_filter(IsAdmin())
bot.polling()

View File

@ -0,0 +1,17 @@
from telebot.async_telebot import AsyncTeleBot
import telebot
bot = AsyncTeleBot('TOKEN')
# Chat id can be private or supergroups.
@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not.
async def admin_rep(message):
await bot.send_message(message.chat.id, "You are allowed to use this command.")
@bot.message_handler(commands=['admin'])
async def not_admin(message):
await bot.send_message(message.chat.id, "You are not allowed to use this command")
# Do not forget to register
bot.add_custom_filter(telebot.asyncio_filters.ChatFilter())
bot.polling()

View File

@ -0,0 +1,22 @@
from telebot.async_telebot import AsyncTeleBot
import telebot
bot = AsyncTeleBot('TOKEN')
# Check if message is a reply
@bot.message_handler(is_reply=True)
async def start_filter(message):
await bot.send_message(message.chat.id, "Looks like you replied to my message.")
# Check if message was forwarded
@bot.message_handler(is_forwarded=True)
async def text_filter(message):
await bot.send_message(message.chat.id, "I do not accept forwarded messages!")
# Do not forget to register filters
bot.add_custom_filter(telebot.asyncio_filters.IsReplyFilter())
bot.add_custom_filter(telebot.asyncio_filters.ForwardFilter())
bot.polling()

View File

@ -0,0 +1,20 @@
from telebot.async_telebot import AsyncTeleBot
import telebot
bot = AsyncTeleBot('TOKEN')
# Check if message starts with @admin tag
@bot.message_handler(text_startswith="@admin")
async def start_filter(message):
await bot.send_message(message.chat.id, "Looks like you are calling admin, wait...")
# Check if text is hi or hello
@bot.message_handler(text=['hi','hello'])
async def text_filter(message):
await bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name))
# Do not forget to register filters
bot.add_custom_filter(telebot.asyncio_filters.TextMatchFilter())
bot.add_custom_filter(telebot.asyncio_filters.TextStartsFilter())
bot.polling()

View File

@ -0,0 +1,74 @@
import telebot
from telebot import asyncio_filters
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
class MyStates:
name = 1
surname = 2
age = 3
@bot.message_handler(commands=['start'])
async def start_ex(message):
"""
Start command. Here we are starting state
"""
await bot.set_state(message.from_user.id, MyStates.name)
await bot.send_message(message.chat.id, 'Hi, write me a name')
@bot.message_handler(state="*", commands='cancel')
async def any_state(message):
"""
Cancel state
"""
await bot.send_message(message.chat.id, "Your state was cancelled.")
await bot.delete_state(message.from_user.id)
@bot.message_handler(state=MyStates.name)
async def name_get(message):
"""
State 1. Will process when user's state is 1.
"""
await bot.send_message(message.chat.id, f'Now write me a surname')
await bot.set_state(message.from_user.id, MyStates.surname)
async with bot.retrieve_data(message.from_user.id) as data:
data['name'] = message.text
@bot.message_handler(state=MyStates.surname)
async def ask_age(message):
"""
State 2. Will process when user's state is 2.
"""
await bot.send_message(message.chat.id, "What is your age?")
await bot.set_state(message.from_user.id, MyStates.age)
async with bot.retrieve_data(message.from_user.id) as data:
data['surname'] = message.text
# result
@bot.message_handler(state=MyStates.age, is_digit=True)
async def ready_for_answer(message):
async with bot.retrieve_data(message.from_user.id) as data:
await bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
await bot.delete_state(message.from_user.id)
#incorrect number
@bot.message_handler(state=MyStates.age, is_digit=False)
async def age_incorrect(message):
await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
# register filters
bot.add_custom_filter(asyncio_filters.StateFilter(bot))
bot.add_custom_filter(asyncio_filters.IsDigitFilter())
# set saving states into file.
bot.enable_saving_states() # you can delete this if you do not need to save states
bot.polling()

View File

@ -0,0 +1,20 @@
import telebot
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
@bot.message_handler(content_types=['photo'])
async def new_message(message: telebot.types.Message):
result_message = await bot.send_message(message.chat.id, '<i>Downloading your photo...</i>', parse_mode='HTML', disable_web_page_preview=True)
file_path = await bot.get_file(message.photo[-1].file_id)
downloaded_file = await bot.download_file(file_path.file_path)
with open('file.jpg', 'wb') as new_file:
new_file.write(downloaded_file)
await bot.edit_message_text(chat_id=message.chat.id, message_id=result_message.id, text='<i>Done!</i>', parse_mode='HTML')
bot.polling(skip_pending=True)

View File

@ -0,0 +1,26 @@
#!/usr/bin/python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
async def send_welcome(message):
await bot.reply_to(message, """\
Hi there, I am EchoBot.
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
""")
# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
@bot.message_handler(func=lambda message: True)
async def echo_message(message):
await bot.reply_to(message, message.text)
bot.polling()

View File

@ -0,0 +1,27 @@
import telebot
from telebot.async_telebot import AsyncTeleBot
import logging
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
class ExceptionHandler(telebot.ExceptionHandler):
def handle(self, exception):
logger.error(exception)
bot = AsyncTeleBot('TOKEN',exception_handler=ExceptionHandler())
@bot.message_handler(commands=['photo'])
async def photo_send(message: telebot.types.Message):
await bot.send_message(message.chat.id, 'Hi, this is an example of exception handlers.')
raise Exception('test') # Exception goes to ExceptionHandler if it is set
bot.polling(skip_pending=True)

View File

@ -0,0 +1,39 @@
# Just a little example of middleware handlers
import telebot
from telebot.asyncio_handler_backends import BaseMiddleware
from telebot.async_telebot import AsyncTeleBot
from telebot.async_telebot import CancelUpdate
bot = AsyncTeleBot('TOKEN')
class SimpleMiddleware(BaseMiddleware):
def __init__(self, limit) -> None:
self.last_time = {}
self.limit = limit
self.update_types = ['message']
# Always specify update types, otherwise middlewares won't work
async def pre_process(self, message, data):
if not message.from_user.id in self.last_time:
# User is not in a dict, so lets add and cancel this function
self.last_time[message.from_user.id] = message.date
return
if message.date - self.last_time[message.from_user.id] < self.limit:
# User is flooding
await bot.send_message(message.chat.id, 'You are making request too often')
return CancelUpdate()
self.last_time[message.from_user.id] = message.date
async def post_process(self, message, data, exception):
pass
bot.setup_middleware(SimpleMiddleware(2))
@bot.message_handler(commands=['start'])
async def start(message):
await bot.send_message(message.chat.id, 'Hello!')
bot.polling()

View File

@ -0,0 +1,48 @@
#!/usr/bin/python
# This example shows how to implement i18n (internationalization) l10n (localization) to create
# multi-language bots with middleware handler.
#
# Also, you could check language code in handler itself too.
# But this example just to show the work of middlewares.
import telebot
from telebot.async_telebot import AsyncTeleBot
from telebot import asyncio_handler_backends
import logging
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
TRANSLATIONS = {
'hello': {
'en': 'hello',
'ru': 'привет',
'uz': 'salom'
}
}
bot = AsyncTeleBot('TOKEN')
class LanguageMiddleware(asyncio_handler_backends.BaseMiddleware):
def __init__(self):
self.update_types = ['message'] # Update types that will be handled by this middleware.
async def pre_process(self, message, data):
data['response'] = TRANSLATIONS['hello'][message.from_user.language_code]
async def post_process(self, message, data, exception):
if exception: # You can get exception occured in handler.
logger.exception(str(exception))
bot.setup_middleware(LanguageMiddleware()) # do not forget to setup
@bot.message_handler(commands=['start'])
async def start(message, data: dict):
# you can get the data in handler too.
# Not necessary to create data parameter in handler function.
await bot.send_message(message.chat.id, data['response'])
bot.polling()

View File

@ -0,0 +1,18 @@
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
async def start_executor(message):
await bot.send_message(message.chat.id, 'Hello!')
bot.register_message_handler(start_executor, commands=['start']) # Start command executor
# See also
# bot.register_callback_query_handler(*args, **kwargs)
# bot.register_channel_post_handler(*args, **kwargs)
# bot.register_chat_member_handler(*args, **kwargs)
# bot.register_inline_handler(*args, **kwargs)
# bot.register_my_chat_member_handler(*args, **kwargs)
# bot.register_edited_message_handler(*args, **kwargs)
# And other functions..
bot.polling(skip_pending=True)

View File

@ -0,0 +1,27 @@
import telebot
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
@bot.message_handler(commands=['photo'])
async def photo_send(message: telebot.types.Message):
with open('test.png', 'rb') as new_file:
await bot.send_photo(message.chat.id, new_file)
@bot.message_handler(commands=['document'])
async def document_send(message: telebot.types.Message):
with open('test.docx', 'rb') as new_file:
await bot.send_document(message.chat.id, new_file)
@bot.message_handler(commands=['photos'])
async def photos_send(message: telebot.types.Message):
with open('test.png', 'rb') as new_file, open('test2.png', 'rb') as new_file2:
await bot.send_media_group(message.chat.id, [telebot.types.InputMediaPhoto(new_file), telebot.types.InputMediaPhoto(new_file2)])
bot.polling(skip_pending=True)

View File

@ -0,0 +1,13 @@
from telebot.async_telebot import AsyncTeleBot
bot = AsyncTeleBot('TOKEN')
@bot.message_handler(commands=['start', 'help'])
async def send_welcome(message):
await bot.reply_to(message, "Howdy, how are you doing?")
@bot.message_handler(func=lambda message: True)
async def echo_all(message):
await bot.reply_to(message, message.text)
bot.polling(skip_pending=True)# Skip pending skips old updates

View File

@ -0,0 +1,14 @@
from telebot.async_telebot import AsyncTeleBot
# Update listeners are functions that are called when any update is received.
bot = AsyncTeleBot(token='TOKEN')
async def update_listener(messages):
for message in messages:
if message.text == '/start':
await bot.send_message(message.chat.id, 'Hello!')
bot.set_update_listener(update_listener)
bot.polling()

View File

@ -0,0 +1,11 @@
import telebot
bot = telebot.TeleBot('TOKEN')
@bot.chat_join_request_handler()
def make_some(message: telebot.types.ChatJoinRequest):
bot.send_message(message.chat.id, 'I accepted a new user!')
bot.approve_chat_join_request(message.chat.id, message.from_user.id)
bot.infinity_polling(allowed_updates=telebot.util.update_types)

View File

@ -0,0 +1,33 @@
import telebot
from telebot import types,util
bot = telebot.TeleBot("token")
#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member.
@bot.chat_member_handler()
def chat_m(message: types.ChatMemberUpdated):
old = message.old_chat_member
new = message.new_chat_member
if new.status == "member":
bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message
#if bot is added to group, this handler will work
@bot.my_chat_member_handler()
def my_chat_m(message: types.ChatMemberUpdated):
old = message.old_chat_member
new = message.new_chat_member
if new.status == "member":
bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group
bot.leave_chat(message.chat.id)
#content_Type_service is:
#'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
#'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
#'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended',
#'voice_chat_participants_invited', 'message_auto_delete_timer_changed'
# this handler deletes service messages
@bot.message_handler(content_types=util.content_type_service)
def delall(message: types.Message):
bot.delete_message(message.chat.id,message.message_id)
bot.infinity_polling(allowed_updates=util.update_types)

View File

@ -0,0 +1,12 @@
import telebot
from telebot import custom_filters
bot = telebot.TeleBot('TOKEN')
# Handler
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
def answer_for_admin(message):
bot.send_message(message.chat.id,"hello my admin")
# Register filter
bot.add_custom_filter(custom_filters.IsAdminFilter(bot))
bot.infinity_polling()

View File

@ -0,0 +1,42 @@
import telebot
bot = telebot.TeleBot('TOKEN')
# AdvancedCustomFilter is for list, string filter values
class MainFilter(telebot.custom_filters.AdvancedCustomFilter):
key='text'
@staticmethod
def check(message, text):
return message.text in text
# SimpleCustomFilter is for boolean values, such as is_admin=True
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
key='is_admin'
@staticmethod
def check(message: telebot.types.Message):
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin
def admin_rep(message):
bot.send_message(message.chat.id, "Hi admin")
@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin
def not_admin(message):
bot.send_message(message.chat.id, "You are not admin")
@bot.message_handler(text=['hi']) # Response to hi message
def welcome_hi(message):
bot.send_message(message.chat.id, 'You said hi')
@bot.message_handler(text=['bye']) # Response to bye message
def bye_user(message):
bot.send_message(message.chat.id, 'You said bye')
# Do not forget to register filters
bot.add_custom_filter(MainFilter())
bot.add_custom_filter(IsAdmin())
bot.infinity_polling(skip_pending=True) # Skip old updates

View File

@ -0,0 +1,19 @@
import telebot
from telebot import custom_filters
bot = telebot.TeleBot('token')
# Chat id can be private or supergroups.
@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not.
def admin_rep(message):
bot.send_message(message.chat.id, "You are allowed to use this command.")
@bot.message_handler(commands=['admin'])
def not_admin(message):
bot.send_message(message.chat.id, "You are not allowed to use this command")
# Do not forget to register
bot.add_custom_filter(custom_filters.ChatFilter())
bot.infinity_polling()

View File

@ -0,0 +1,21 @@
import telebot
from telebot import custom_filters
bot = telebot.TeleBot('TOKEN')
# Check if message is a reply
@bot.message_handler(is_reply=True)
def start_filter(message):
bot.send_message(message.chat.id, "Looks like you replied to my message.")
# Check if message was forwarded
@bot.message_handler(is_forwarded=True)
def text_filter(message):
bot.send_message(message.chat.id, "I do not accept forwarded messages!")
# Do not forget to register filters
bot.add_custom_filter(custom_filters.IsReplyFilter())
bot.add_custom_filter(custom_filters.ForwardFilter())
bot.infinity_polling()

View File

@ -0,0 +1,21 @@
import telebot
from telebot import custom_filters
bot = telebot.TeleBot('TOKEN')
# Check if message starts with @admin tag
@bot.message_handler(text_startswith="@admin")
def start_filter(message):
bot.send_message(message.chat.id, "Looks like you are calling admin, wait...")
# Check if text is hi or hello
@bot.message_handler(text=['hi','hello'])
def text_filter(message):
bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name))
# Do not forget to register filters
bot.add_custom_filter(custom_filters.TextMatchFilter())
bot.add_custom_filter(custom_filters.TextStartsFilter())
bot.infinity_polling()

74
examples/custom_states.py Normal file
View File

@ -0,0 +1,74 @@
import telebot
from telebot import custom_filters
bot = telebot.TeleBot("")
class MyStates:
name = 1
surname = 2
age = 3
@bot.message_handler(commands=['start'])
def start_ex(message):
"""
Start command. Here we are starting state
"""
bot.set_state(message.from_user.id, MyStates.name)
bot.send_message(message.chat.id, 'Hi, write me a name')
@bot.message_handler(state="*", commands='cancel')
def any_state(message):
"""
Cancel state
"""
bot.send_message(message.chat.id, "Your state was cancelled.")
bot.delete_state(message.from_user.id)
@bot.message_handler(state=MyStates.name)
def name_get(message):
"""
State 1. Will process when user's state is 1.
"""
bot.send_message(message.chat.id, f'Now write me a surname')
bot.set_state(message.from_user.id, MyStates.surname)
with bot.retrieve_data(message.from_user.id) as data:
data['name'] = message.text
@bot.message_handler(state=MyStates.surname)
def ask_age(message):
"""
State 2. Will process when user's state is 2.
"""
bot.send_message(message.chat.id, "What is your age?")
bot.set_state(message.from_user.id, MyStates.age)
with bot.retrieve_data(message.from_user.id) as data:
data['surname'] = message.text
# result
@bot.message_handler(state=MyStates.age, is_digit=True)
def ready_for_answer(message):
with bot.retrieve_data(message.from_user.id) as data:
bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
bot.delete_state(message.from_user.id)
#incorrect number
@bot.message_handler(state=MyStates.age, is_digit=False)
def age_incorrect(message):
bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
# register filters
bot.add_custom_filter(custom_filters.StateFilter(bot))
bot.add_custom_filter(custom_filters.IsDigitFilter())
# set saving states into file.
bot.enable_saving_states() # you can delete this if you do not need to save states
bot.infinity_polling(skip_pending=True)

View File

@ -74,4 +74,4 @@ def send_welcome(message):
bot.reply_to(message, reply)
bot.polling()
bot.infinity_polling()

View File

@ -130,4 +130,4 @@ def command_default(m):
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
bot.polling()
bot.infinity_polling()

View File

@ -25,4 +25,4 @@ def echo_message(message):
bot.reply_to(message, message.text)
bot.polling()
bot.infinity_polling()

View File

@ -61,7 +61,7 @@ def default_query(inline_query):
def main_loop():
bot.polling(True)
bot.infinity_polling()
while 1:
time.sleep(3)

View File

@ -24,4 +24,4 @@ def callback_query(call):
def message_handler(message):
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
bot.polling(none_stop=True)
bot.infinity_polling()

View File

@ -50,4 +50,4 @@ def start(message):
bot.send_message(message.chat.id, _('hello'))
bot.polling()
bot.infinity_polling()

View File

@ -58,4 +58,4 @@ def start(message):
bot.send_message(message.chat.id, bot.session['state'])
bot.polling()
bot.infinity_polling()

View File

@ -78,5 +78,4 @@ def got_payment(message):
parse_mode='Markdown')
bot.skip_pending = True
bot.polling(none_stop=True, interval=0)
bot.infinity_polling(skip_pending = True)

View File

@ -0,0 +1,21 @@
import telebot
api_token = 'token'
bot = telebot.TeleBot(api_token)
def start_executor(message):
bot.send_message(message.chat.id, 'Hello!')
bot.register_message_handler(start_executor, commands=['start']) # Start command executor
# See also
# bot.register_callback_query_handler(*args, **kwargs)
# bot.register_channel_post_handler(*args, **kwargs)
# bot.register_chat_member_handler(*args, **kwargs)
# bot.register_inline_handler(*args, **kwargs)
# bot.register_my_chat_member_handler(*args, **kwargs)
# bot.register_edited_message_handler(*args, **kwargs)
# And other functions..
bot.infinity_polling()

View File

@ -0,0 +1,13 @@
import telebot
bot = telebot.TeleBot("TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
@bot.message_handler(func=lambda message: True)
def echo_all(message):
bot.reply_to(message, message.text)
bot.infinity_polling(skip_pending=True)# Skip pending skips old updates

View File

@ -83,4 +83,4 @@ bot.enable_save_next_step_handlers(delay=2)
# WARNING It will work only if enable_save_next_step_handlers was called!
bot.load_next_step_handlers()
bot.polling()
bot.infinity_polling()

View File

@ -81,4 +81,4 @@ def listener(messages):
bot.set_update_listener(listener)
bot.polling()
bot.infinity_polling()

View File

@ -1,4 +1,4 @@
py==1.10.0
pytest==3.0.2
requests==2.20.0
wheel==0.24.0
aiohttp>=3.8.0,<3.9.0

View File

@ -25,6 +25,7 @@ setup(name='pyTelegramBotAPI',
install_requires=['requests'],
extras_require={
'json': 'ujson',
'PIL': 'Pillow',
'redis': 'redis>=3.4.1'
},
classifiers=[

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,10 @@ except ImportError:
import requests
from requests.exceptions import HTTPError, ConnectionError, Timeout
from requests.adapters import HTTPAdapter
try:
# noinspection PyUnresolvedReferences
from requests.packages.urllib3 import fields
format_header_param = fields.format_header_param
except ImportError:
@ -27,23 +29,30 @@ session = None
API_URL = None
FILE_URL = None
CONNECT_TIMEOUT = 3.5
READ_TIMEOUT = 9999
SESSION_TIME_TO_LIVE = None # In seconds. None - live forever, 0 - one-time
CONNECT_TIMEOUT = 15
READ_TIMEOUT = 30
LONG_POLLING_TIMEOUT = 10 # Should be positive, short polling should be used for testing purposes only (https://core.telegram.org/bots/api#getupdates)
SESSION_TIME_TO_LIVE = 600 # In seconds. None - live forever, 0 - one-time
RETRY_ON_ERROR = False
RETRY_TIMEOUT = 2
MAX_RETRIES = 15
RETRY_ENGINE = 1
CUSTOM_SERIALIZER = None
CUSTOM_REQUEST_SENDER = None
ENABLE_MIDDLEWARE = False
def _get_req_session(reset=False):
if SESSION_TIME_TO_LIVE:
# If session TTL is set - check time passed
creation_date = util.per_thread('req_session_time', lambda: datetime.now(), reset)
# noinspection PyTypeChecker
if (datetime.now() - creation_date).total_seconds() > SESSION_TIME_TO_LIVE:
# Force session reset
reset = True
@ -68,7 +77,10 @@ def _make_request(token, method_name, method='get', params=None, files=None):
:param files: Optional files.
:return: The result parsed to a JSON dictionary.
"""
if not token:
raise Exception('Bot token is not defined')
if API_URL:
# noinspection PyUnresolvedReferences
request_url = API_URL.format(token, method_name)
else:
request_url = "https://api.telegram.org/bot{0}/{1}".format(token, method_name)
@ -80,48 +92,61 @@ def _make_request(token, method_name, method='get', params=None, files=None):
fields.format_header_param = _no_encode(format_header_param)
if params:
if 'timeout' in params:
read_timeout = params.pop('timeout') + 10
if 'connect-timeout' in params:
connect_timeout = params.pop('connect-timeout') + 10
read_timeout = params.pop('timeout')
connect_timeout = read_timeout
# if 'connect-timeout' in params:
# connect_timeout = params.pop('connect-timeout') + 10
if 'long_polling_timeout' in params:
# For getUpdates
# The only function with timeout on the BOT API side
params['timeout'] = params.pop('long_polling_timeout')
# Long polling hangs for given time. Read timeout should be greater that long_polling_timeout
read_timeout = max(params['timeout'] + 10, read_timeout)
# For getUpdates: it's the only function with timeout parameter on the BOT API side
long_polling_timeout = params.pop('long_polling_timeout')
params['timeout'] = long_polling_timeout
# Long polling hangs for a given time. Read timeout should be greater that long_polling_timeout
read_timeout = max(long_polling_timeout + 5, read_timeout)
# Lets stop suppose that user is stupid and assume that he knows what he do...
# read_timeout = read_timeout + 10
# connect_timeout = connect_timeout + 10
params = params or None # Set params to None if empty
result = None
if RETRY_ON_ERROR:
if RETRY_ON_ERROR and RETRY_ENGINE == 1:
got_result = False
current_try = 0
while not got_result and current_try<MAX_RETRIES-1:
current_try+=1
try:
result = _get_req_session().request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
got_result = True
except HTTPError:
logger.debug("HTTP Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
except ConnectionError:
logger.debug("Connection Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
except Timeout:
logger.debug("Timeout Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
if not got_result:
result = _get_req_session().request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
elif RETRY_ON_ERROR and RETRY_ENGINE == 2:
http = _get_req_session()
retry_strategy = requests.packages.urllib3.util.retry.Retry(
total=MAX_RETRIES,
)
adapter = HTTPAdapter(max_retries=retry_strategy)
for prefix in ('http://', 'https://'):
http.mount(prefix, adapter)
result = http.request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
elif CUSTOM_REQUEST_SENDER:
result = CUSTOM_REQUEST_SENDER(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
else:
result = _get_req_session().request(
method, request_url, params=params, files=files,
@ -166,6 +191,16 @@ def get_me(token):
return _make_request(token, method_url)
def log_out(token):
method_url = r'logOut'
return _make_request(token, method_url)
def close(token):
method_url = r'close'
return _make_request(token, method_url)
def get_file(token, file_id):
method_url = r'getFile'
return _make_request(token, method_url, params={'file_id': file_id})
@ -175,13 +210,15 @@ def get_file_url(token, file_id):
if FILE_URL is None:
return "https://api.telegram.org/file/bot{0}/{1}".format(token, get_file(token, file_id)['file_path'])
else:
# noinspection PyUnresolvedReferences
return FILE_URL.format(token, get_file(token, file_id)['file_path'])
def download_file(token, file_path):
if FILE_URL is None:
url = "https://api.telegram.org/file/bot{0}/{1}".format(token, file_path)
else:
# noinspection PyUnresolvedReferences
url = FILE_URL.format(token, file_path)
result = _get_req_session().get(url, proxies=proxy)
@ -194,7 +231,8 @@ def download_file(token, file_path):
def send_message(
token, chat_id, text,
disable_web_page_preview=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None,
entities=None, allow_sending_without_reply=None):
"""
Use this method to send text messages. On success, the sent Message is returned.
:param token:
@ -206,6 +244,8 @@ def send_message(
:param parse_mode:
:param disable_notification:
:param timeout:
:param entities:
:param allow_sending_without_reply:
:return:
"""
method_url = r'sendMessage'
@ -221,7 +261,11 @@ def send_message(
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if entities:
payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, method='post')
@ -243,7 +287,7 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed
if drop_pending_updates is not None: # Any bool value should pass
payload['drop_pending_updates'] = drop_pending_updates
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload, files=files)
@ -253,7 +297,7 @@ def delete_webhook(token, drop_pending_updates=None, timeout=None):
if drop_pending_updates is not None: # Any bool value should pass
payload['drop_pending_updates'] = drop_pending_updates
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -261,7 +305,7 @@ def get_webhook_info(token, timeout=None):
method_url = r'getWebhookInfo'
payload = {}
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -273,9 +317,8 @@ def get_updates(token, offset=None, limit=None, timeout=None, allowed_updates=No
if limit:
payload['limit'] = limit
if timeout:
payload['connect-timeout'] = timeout
if long_polling_timeout:
payload['long_polling_timeout'] = long_polling_timeout
payload['timeout'] = timeout
payload['long_polling_timeout'] = long_polling_timeout if long_polling_timeout else LONG_POLLING_TIMEOUT
if allowed_updates is not None: # Empty lists should pass
payload['allowed_updates'] = json.dumps(allowed_updates)
return _make_request(token, method_url, params=payload)
@ -309,12 +352,24 @@ def get_chat_administrators(token, chat_id):
return _make_request(token, method_url, params=payload)
def get_chat_members_count(token, chat_id):
method_url = r'getChatMembersCount'
def get_chat_member_count(token, chat_id):
method_url = r'getChatMemberCount'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload)
def set_sticker_set_thumb(token, name, user_id, thumb):
method_url = r'setStickerSetThumb'
payload = {'name': name, 'user_id': user_id}
files = {}
if thumb:
if not isinstance(thumb, str):
files['thumb'] = thumb
else:
payload['thumb'] = thumb
return _make_request(token, method_url, params=payload, files=files or None)
def set_chat_sticker_set(token, chat_id, sticker_set_name):
method_url = r'setChatStickerSet'
payload = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name}
@ -341,7 +396,7 @@ def forward_message(
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -365,14 +420,14 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_dice(
token, chat_id,
emoji=None, disable_notification=None, reply_to_message_id=None,
reply_markup=None, timeout=None):
reply_markup=None, timeout=None, allow_sending_without_reply=None):
method_url = r'sendDice'
payload = {'chat_id': chat_id}
if emoji:
@ -384,14 +439,17 @@ def send_dice(
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload)
def send_photo(
token, chat_id, photo,
caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None,
caption_entities=None, allow_sending_without_reply=None):
method_url = r'sendPhoto'
payload = {'chat_id': chat_id}
files = None
@ -412,14 +470,18 @@ def send_photo(
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_media_group(
token, chat_id, media,
disable_notification=None, reply_to_message_id=None,
timeout=None):
timeout=None, allow_sending_without_reply=None):
method_url = r'sendMediaGroup'
media_json, files = convert_input_media_array(media)
payload = {'chat_id': chat_id, 'media': media_json}
@ -428,7 +490,9 @@ def send_media_group(
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(
token, method_url, params=payload,
method='post' if files else 'get',
@ -437,37 +501,55 @@ def send_media_group(
def send_location(
token, chat_id, latitude, longitude,
live_period=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None):
live_period=None, reply_to_message_id=None,
reply_markup=None, disable_notification=None,
timeout=None, horizontal_accuracy=None, heading=None,
proximity_alert_radius=None, allow_sending_without_reply=None):
method_url = r'sendLocation'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude}
if live_period:
payload['live_period'] = live_period
if horizontal_accuracy:
payload['horizontal_accuracy'] = horizontal_accuracy
if heading:
payload['heading'] = heading
if proximity_alert_radius:
payload['proximity_alert_radius'] = proximity_alert_radius
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
def edit_message_live_location(token, latitude, longitude, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None, timeout=None):
def edit_message_live_location(
token, latitude, longitude, chat_id=None, message_id=None,
inline_message_id=None, reply_markup=None, timeout=None,
horizontal_accuracy=None, heading=None, proximity_alert_radius=None):
method_url = r'editMessageLiveLocation'
payload = {'latitude': latitude, 'longitude': longitude}
if chat_id:
payload['chat_id'] = chat_id
if message_id:
payload['message_id'] = message_id
if horizontal_accuracy:
payload['horizontal_accuracy'] = horizontal_accuracy
if heading:
payload['heading'] = heading
if proximity_alert_radius:
payload['proximity_alert_radius'] = proximity_alert_radius
if inline_message_id:
payload['inline_message_id'] = inline_message_id
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
@ -485,14 +567,16 @@ def stop_message_live_location(
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_venue(
token, chat_id, latitude, longitude, title, address,
foursquare_id=None, foursquare_type=None, disable_notification=None,
reply_to_message_id=None, reply_markup=None, timeout=None):
reply_to_message_id=None, reply_markup=None, timeout=None,
allow_sending_without_reply=None, google_place_id=None,
google_place_type=None):
method_url = r'sendVenue'
payload = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude, 'title': title, 'address': address}
if foursquare_id:
@ -506,13 +590,20 @@ def send_venue(
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if google_place_id:
payload['google_place_id'] = google_place_id
if google_place_type:
payload['google_place_type'] = google_place_type
return _make_request(token, method_url, params=payload)
def send_contact(
token, chat_id, phone_number, first_name, last_name=None, vcard=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None):
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None,
allow_sending_without_reply=None):
method_url = r'sendContact'
payload = {'chat_id': chat_id, 'phone_number': phone_number, 'first_name': first_name}
if last_name:
@ -526,7 +617,9 @@ def send_contact(
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload)
@ -534,12 +627,13 @@ def send_chat_action(token, chat_id, action, timeout=None):
method_url = r'sendChatAction'
payload = {'chat_id': chat_id, 'action': action}
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
def send_video(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None, thumb=None, width=None, height=None):
parse_mode=None, supports_streaming=None, disable_notification=None, timeout=None,
thumb=None, width=None, height=None, caption_entities=None, allow_sending_without_reply=None):
method_url = r'sendVideo'
payload = {'chat_id': chat_id}
files = None
@ -562,7 +656,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if thumb:
if not util.is_string(thumb):
if files:
@ -575,11 +669,17 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
payload['width'] = width
if height:
payload['height'] = height
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None, thumb=None):
def send_animation(
token, chat_id, data, duration=None, caption=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None, thumb=None, caption_entities=None,
allow_sending_without_reply=None):
method_url = r'sendAnimation'
payload = {'chat_id': chat_id}
files = None
@ -600,7 +700,7 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if thumb:
if not util.is_string(thumb):
if files:
@ -609,11 +709,16 @@ def send_animation(token, chat_id, data, duration=None, caption=None, reply_to_m
files = {'thumb': thumb}
else:
payload['thumb'] = thumb
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
parse_mode=None, disable_notification=None, timeout=None):
parse_mode=None, disable_notification=None, timeout=None, caption_entities=None,
allow_sending_without_reply=None):
method_url = r'sendVoice'
payload = {'chat_id': chat_id}
files = None
@ -634,12 +739,16 @@ def send_voice(token, chat_id, voice, caption=None, duration=None, reply_to_mess
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_message_id=None, reply_markup=None,
disable_notification=None, timeout=None, thumb=None):
disable_notification=None, timeout=None, thumb=None, allow_sending_without_reply=None):
method_url = r'sendVideoNote'
payload = {'chat_id': chat_id}
files = None
@ -660,7 +769,7 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if thumb:
if not util.is_string(thumb):
if files:
@ -669,11 +778,14 @@ def send_video_note(token, chat_id, data, duration=None, length=None, reply_to_m
files = {'thumb': thumb}
else:
payload['thumb'] = thumb
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_audio(token, chat_id, audio, caption=None, duration=None, performer=None, title=None, reply_to_message_id=None,
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None):
reply_markup=None, parse_mode=None, disable_notification=None, timeout=None, thumb=None,
caption_entities=None, allow_sending_without_reply=None):
method_url = r'sendAudio'
payload = {'chat_id': chat_id}
files = None
@ -698,7 +810,7 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if thumb:
if not util.is_string(thumb):
if files:
@ -707,16 +819,24 @@ def send_audio(token, chat_id, audio, caption=None, duration=None, performer=Non
files = {'thumb': thumb}
else:
payload['thumb'] = thumb
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload, files=files, method='post')
def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_markup=None, parse_mode=None,
disable_notification=None, timeout=None, caption=None, thumb=None):
disable_notification=None, timeout=None, caption=None, thumb=None, caption_entities=None,
allow_sending_without_reply=None, disable_content_type_detection=None, visible_file_name=None):
method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id}
files = None
if not util.is_string(data):
files = {data_type: data}
file_data = data
if visible_file_name:
file_data = (visible_file_name, data)
files = {data_type: file_data}
else:
payload[data_type] = data
if reply_to_message_id:
@ -728,7 +848,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
if disable_notification is not None:
payload['disable_notification'] = disable_notification
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if caption:
payload['caption'] = caption
if thumb:
@ -739,6 +859,12 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
files = {'thumb': thumb}
else:
payload['thumb'] = thumb
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if method_url == 'sendDocument' and disable_content_type_detection is not None:
payload['disable_content_type_detection'] = disable_content_type_detection
return _make_request(token, method_url, params=payload, files=files, method='post')
@ -749,13 +875,15 @@ def get_method_by_type(data_type):
return r'sendSticker'
def kick_chat_member(token, chat_id, user_id, until_date=None):
method_url = 'kickChatMember'
def ban_chat_member(token, chat_id, user_id, until_date=None, revoke_messages=None):
method_url = 'banChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if isinstance(until_date, datetime):
payload['until_date'] = until_date.timestamp()
else:
payload['until_date'] = until_date
if revoke_messages is not None:
payload['revoke_messages'] = revoke_messages
return _make_request(token, method_url, params=payload, method='post')
@ -804,7 +932,8 @@ def restrict_chat_member(
def promote_chat_member(
token, chat_id, user_id, can_change_info=None, can_post_messages=None,
can_edit_messages=None, can_delete_messages=None, can_invite_users=None,
can_restrict_members=None, can_pin_messages=None, can_promote_members=None):
can_restrict_members=None, can_pin_messages=None, can_promote_members=None,
is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None):
method_url = 'promoteChatMember'
payload = {'chat_id': chat_id, 'user_id': user_id}
if can_change_info is not None:
@ -823,6 +952,12 @@ def promote_chat_member(
payload['can_pin_messages'] = can_pin_messages
if can_promote_members is not None:
payload['can_promote_members'] = can_promote_members
if is_anonymous is not None:
payload['is_anonymous'] = is_anonymous
if can_manage_chat is not None:
payload['can_manage_chat'] = can_manage_chat
if can_manage_voice_chats is not None:
payload['can_manage_voice_chats'] = can_manage_voice_chats
return _make_request(token, method_url, params=payload, method='post')
@ -843,12 +978,83 @@ def set_chat_permissions(token, chat_id, permissions):
return _make_request(token, method_url, params=payload, method='post')
def create_chat_invite_link(token, chat_id, name, expire_date, member_limit, creates_join_request):
method_url = 'createChatInviteLink'
payload = {
'chat_id': chat_id
}
if expire_date is not None:
if isinstance(expire_date, datetime):
payload['expire_date'] = expire_date.timestamp()
else:
payload['expire_date'] = expire_date
if member_limit:
payload['member_limit'] = member_limit
if creates_join_request is not None:
payload['creates_join_request'] = creates_join_request
if name:
payload['name'] = name
return _make_request(token, method_url, params=payload, method='post')
def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request):
method_url = 'editChatInviteLink'
payload = {
'chat_id': chat_id,
'invite_link': invite_link
}
if expire_date is not None:
if isinstance(expire_date, datetime):
payload['expire_date'] = expire_date.timestamp()
else:
payload['expire_date'] = expire_date
if member_limit is not None:
payload['member_limit'] = member_limit
if name:
payload['name'] = name
if creates_join_request is not None:
payload['creates_join_request'] = creates_join_request
return _make_request(token, method_url, params=payload, method='post')
def revoke_chat_invite_link(token, chat_id, invite_link):
method_url = 'revokeChatInviteLink'
payload = {
'chat_id': chat_id,
'invite_link': invite_link
}
return _make_request(token, method_url, params=payload, method='post')
def export_chat_invite_link(token, chat_id):
method_url = 'exportChatInviteLink'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')
def approve_chat_join_request(token, chat_id, user_id):
method_url = 'approveChatJoinRequest'
payload = {
'chat_id': chat_id,
'user_id': user_id
}
return _make_request(token, method_url, params=payload, method='post')
def decline_chat_join_request(token, chat_id, user_id):
method_url = 'declineChatJoinRequest'
payload = {
'chat_id': chat_id,
'user_id': user_id
}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_photo(token, chat_id, photo):
method_url = 'setChatPhoto'
payload = {'chat_id': chat_id}
@ -874,9 +1080,33 @@ def set_chat_title(token, chat_id, title):
return _make_request(token, method_url, params=payload, method='post')
def set_my_commands(token, commands):
def get_my_commands(token, scope=None, language_code=None):
method_url = r'getMyCommands'
payload = {}
if scope:
payload['scope'] = scope.to_json()
if language_code:
payload['language_code'] = language_code
return _make_request(token, method_url, params=payload)
def set_my_commands(token, commands, scope=None, language_code=None):
method_url = r'setMyCommands'
payload = {'commands': _convert_list_json_serializable(commands)}
if scope:
payload['scope'] = scope.to_json()
if language_code:
payload['language_code'] = language_code
return _make_request(token, method_url, params=payload, method='post')
def delete_my_commands(token, scope=None, language_code=None):
method_url = r'deleteMyCommands'
payload = {}
if scope:
payload['scope'] = scope.to_json()
if language_code:
payload['language_code'] = language_code
return _make_request(token, method_url, params=payload, method='post')
@ -913,7 +1143,7 @@ def unpin_all_chat_messages(token, chat_id):
# Updating messages
def edit_message_text(token, text, chat_id=None, message_id=None, inline_message_id=None, parse_mode=None,
disable_web_page_preview=None, reply_markup=None):
entities = None, disable_web_page_preview=None, reply_markup=None):
method_url = r'editMessageText'
payload = {'text': text}
if chat_id:
@ -924,6 +1154,8 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if entities:
payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities))
if disable_web_page_preview is not None:
payload['disable_web_page_preview'] = disable_web_page_preview
if reply_markup:
@ -932,7 +1164,7 @@ def edit_message_text(token, text, chat_id=None, message_id=None, inline_message
def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_message_id=None,
parse_mode=None, reply_markup=None):
parse_mode=None, caption_entities=None,reply_markup=None):
method_url = r'editMessageCaption'
payload = {'caption': caption}
if chat_id:
@ -943,6 +1175,8 @@ def edit_message_caption(token, caption, chat_id=None, message_id=None, inline_m
payload['inline_message_id'] = inline_message_id
if parse_mode:
payload['parse_mode'] = parse_mode
if caption_entities:
payload['caption_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(caption_entities))
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
return _make_request(token, method_url, params=payload, method='post')
@ -981,7 +1215,7 @@ def delete_message(token, chat_id, message_id, timeout=None):
method_url = r'deleteMessage'
payload = {'chat_id': chat_id, 'message_id': message_id}
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload, method='post')
@ -989,7 +1223,8 @@ def delete_message(token, chat_id, message_id, timeout=None):
def send_game(
token, chat_id, game_short_name,
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None):
disable_notification=None, reply_to_message_id=None, reply_markup=None, timeout=None,
allow_sending_without_reply=None):
method_url = r'sendGame'
payload = {'chat_id': chat_id, 'game_short_name': game_short_name}
if disable_notification is not None:
@ -999,7 +1234,9 @@ def send_game(
if reply_markup:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
return _make_request(token, method_url, params=payload)
@ -1060,11 +1297,11 @@ def get_game_high_scores(token, user_id, chat_id=None, message_id=None, inline_m
def send_invoice(
token, chat_id, title, description, invoice_payload, provider_token, currency, prices,
start_parameter, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
start_parameter = None, photo_url=None, photo_size=None, photo_width=None, photo_height=None,
need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None,
send_phone_number_to_provider = None, send_email_to_provider = None, is_flexible=None,
disable_notification=None, reply_to_message_id=None, reply_markup=None, provider_data=None,
timeout=None):
timeout=None, allow_sending_without_reply=None, max_tip_amount=None, suggested_tip_amounts=None):
"""
Use this method to send invoices. On success, the sent Message is returned.
:param token: Bot's token (you don't need to fill this)
@ -1092,12 +1329,18 @@ def send_invoice(
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
:param timeout:
:param allow_sending_without_reply:
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units of the currency.
At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.
:return:
"""
method_url = r'sendInvoice'
payload = {'chat_id': chat_id, 'title': title, 'description': description, 'payload': invoice_payload,
'provider_token': provider_token, 'start_parameter': start_parameter, 'currency': currency,
'provider_token': provider_token, 'currency': currency,
'prices': _convert_list_json_serializable(prices)}
if start_parameter:
payload['start_parameter'] = start_parameter
if photo_url:
payload['photo_url'] = photo_url
if photo_size:
@ -1129,7 +1372,13 @@ def send_invoice(
if provider_data:
payload['provider_data'] = provider_data
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if max_tip_amount is not None:
payload['max_tip_amount'] = max_tip_amount
if suggested_tip_amounts is not None:
payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts)
return _make_request(token, method_url, params=payload)
@ -1226,15 +1475,17 @@ def upload_sticker_file(token, user_id, png_sticker):
def create_new_sticker_set(
token, user_id, name, title, png_sticker, emojis,
token, user_id, name, title, emojis, png_sticker, tgs_sticker,
contains_masks=None, mask_position=None):
method_url = 'createNewStickerSet'
payload = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis}
stype = 'png_sticker' if png_sticker else 'tgs_sticker'
sticker = png_sticker or tgs_sticker
files = None
if not util.is_string(png_sticker):
files = {'png_sticker': png_sticker}
if not util.is_string(sticker):
files = {stype: sticker}
else:
payload['png_sticker'] = png_sticker
payload[stype] = sticker
if contains_masks is not None:
payload['contains_masks'] = contains_masks
if mask_position:
@ -1242,14 +1493,16 @@ def create_new_sticker_set(
return _make_request(token, method_url, params=payload, files=files, method='post')
def add_sticker_to_set(token, user_id, name, png_sticker, emojis, mask_position):
def add_sticker_to_set(token, user_id, name, emojis, png_sticker, tgs_sticker, mask_position):
method_url = 'addStickerToSet'
payload = {'user_id': user_id, 'name': name, 'emojis': emojis}
stype = 'png_sticker' if png_sticker else 'tgs_sticker'
sticker = png_sticker or tgs_sticker
files = None
if not util.is_string(png_sticker):
files = {'png_sticker': png_sticker}
if not util.is_string(sticker):
files = {stype: sticker}
else:
payload['png_sticker'] = png_sticker
payload[stype] = sticker
if mask_position:
payload['mask_position'] = mask_position.to_json()
return _make_request(token, method_url, params=payload, files=files, method='post')
@ -1267,12 +1520,14 @@ def delete_sticker_from_set(token, sticker):
return _make_request(token, method_url, params=payload, method='post')
# noinspection PyShadowingBuiltins
def send_poll(
token, chat_id,
question, options,
is_anonymous = None, type = None, allows_multiple_answers = None, correct_option_id = None,
explanation = None, explanation_parse_mode=None, open_period = None, close_date = None, is_closed = None,
disable_notifications=False, reply_to_message_id=None, reply_markup=None, timeout=None):
disable_notification=False, reply_to_message_id=None, allow_sending_without_reply=None,
reply_markup=None, timeout=None, explanation_entities=None):
method_url = r'sendPoll'
payload = {
'chat_id': str(chat_id),
@ -1301,14 +1556,19 @@ def send_poll(
if is_closed is not None:
payload['is_closed'] = is_closed
if disable_notifications:
payload['disable_notification'] = disable_notifications
if disable_notification:
payload['disable_notification'] = disable_notification
if reply_to_message_id is not None:
payload['reply_to_message_id'] = reply_to_message_id
if allow_sending_without_reply is not None:
payload['allow_sending_without_reply'] = allow_sending_without_reply
if reply_markup is not None:
payload['reply_markup'] = _convert_markup(reply_markup)
if timeout:
payload['connect-timeout'] = timeout
payload['timeout'] = timeout
if explanation_entities:
payload['explanation_entities'] = json.dumps(
types.MessageEntity.to_list_of_dicts(explanation_entities))
return _make_request(token, method_url, params=payload)
@ -1355,7 +1615,7 @@ def _convert_poll_options(poll_options):
elif isinstance(poll_options[0], str):
# Compatibility mode with previous bug when only list of string was accepted as poll_options
return poll_options
elif isinstance(poll_options[0], types.JsonSerializable):
elif isinstance(poll_options[0], types.PollOption):
return [option.text for option in poll_options]
else:
return poll_options
@ -1439,4 +1699,5 @@ class ApiTelegramException(ApiException):
result)
self.result_json = result_json
self.error_code = result_json['error_code']
self.description = result_json['description']

2881
telebot/async_telebot.py Normal file

File diff suppressed because it is too large Load Diff

178
telebot/asyncio_filters.py Normal file
View File

@ -0,0 +1,178 @@
from abc import ABC
class SimpleCustomFilter(ABC):
"""
Simple Custom Filter base class.
Create child class with check() method.
Accepts only message, returns bool value, that is compared with given in handler.
"""
async def check(self, message):
"""
Perform a check.
"""
pass
class AdvancedCustomFilter(ABC):
"""
Simple Custom Filter base class.
Create child class with check() method.
Accepts two parameters, returns bool: True - filter passed, False - filter failed.
message: Message class
text: Filter value given in handler
"""
async def check(self, message, text):
"""
Perform a check.
"""
pass
class TextMatchFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
@bot.message_handler(text=['account'])
"""
key = 'text'
async def check(self, message, text):
if type(text) is list:return message.text in text
else: return text == message.text
class TextContainsFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
"""
key = 'text_contains'
async def check(self, message, text):
return text in message.text
class TextStartsFilter(AdvancedCustomFilter):
"""
Filter to check whether message starts with some text.
Example:
# Will work if message.text starts with 'Sir'.
@bot.message_handler(text_startswith='Sir')
"""
key = 'text_startswith'
async def check(self, message, text):
return message.text.startswith(text)
class ChatFilter(AdvancedCustomFilter):
"""
Check whether chat_id corresponds to given chat_id.
Example:
@bot.message_handler(chat_id=[99999])
"""
key = 'chat_id'
async def check(self, message, text):
return message.chat.id in text
class ForwardFilter(SimpleCustomFilter):
"""
Check whether message was forwarded from channel or group.
Example:
@bot.message_handler(is_forwarded=True)
"""
key = 'is_forwarded'
async def check(self, message):
return message.forward_from_chat is not None
class IsReplyFilter(SimpleCustomFilter):
"""
Check whether message is a reply.
Example:
@bot.message_handler(is_reply=True)
"""
key = 'is_reply'
async def check(self, message):
return message.reply_to_message is not None
class LanguageFilter(AdvancedCustomFilter):
"""
Check users language_code.
Example:
@bot.message_handler(language_code=['ru'])
"""
key = 'language_code'
async def check(self, message, text):
if type(text) is list:return message.from_user.language_code in text
else: return message.from_user.language_code == text
class IsAdminFilter(SimpleCustomFilter):
"""
Check whether the user is administrator / owner of the chat.
Example:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
"""
key = 'is_chat_admin'
def __init__(self, bot):
self._bot = bot
async def check(self, message):
result = await self._bot.get_chat_member(message.chat.id, message.from_user.id)
return result.status in ['creator', 'administrator']
class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
"""
def __init__(self, bot):
self.bot = bot
key = 'state'
async def check(self, message, text):
result = await self.bot.current_states.current_state(message.from_user.id)
if result is False: return False
elif text == '*': return True
elif type(text) is list: return result in text
return result == text
class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
"""
key = 'is_digit'
async def check(self, message):
return message.text.isdigit()

View File

@ -0,0 +1,219 @@
import os
import pickle
class StateMemory:
def __init__(self):
self._states = {}
async def add_state(self, chat_id, state):
"""
Add a state.
:param chat_id:
:param state: new state
"""
if chat_id in self._states:
self._states[chat_id]['state'] = state
else:
self._states[chat_id] = {'state': state,'data': {}}
async def current_state(self, chat_id):
"""Current state"""
if chat_id in self._states: return self._states[chat_id]['state']
else: return False
async def delete_state(self, chat_id):
"""Delete a state"""
self._states.pop(chat_id)
def _get_data(self, chat_id):
return self._states[chat_id]['data']
async def set(self, chat_id, new_state):
"""
Set a new state for a user.
:param chat_id:
:param new_state: new_state of a user
"""
await self.add_state(chat_id,new_state)
async def _add_data(self, chat_id, key, value):
result = self._states[chat_id]['data'][key] = value
return result
async def finish(self, chat_id):
"""
Finish(delete) state of a user.
:param chat_id:
"""
await self.delete_state(chat_id)
def retrieve_data(self, chat_id):
"""
Save input text.
Usage:
with bot.retrieve_data(message.chat.id) as data:
data['name'] = message.text
Also, at the end of your 'Form' you can get the name:
data['name']
"""
return StateContext(self, chat_id)
class StateFile:
"""
Class to save states in a file.
"""
def __init__(self, filename):
self.file_path = filename
async def add_state(self, chat_id, state):
"""
Add a state.
:param chat_id:
:param state: new state
"""
states_data = self._read_data()
if chat_id in states_data:
states_data[chat_id]['state'] = state
return await self._save_data(states_data)
else:
new_data = states_data[chat_id] = {'state': state,'data': {}}
return await self._save_data(states_data)
async def current_state(self, chat_id):
"""Current state."""
states_data = self._read_data()
if chat_id in states_data: return states_data[chat_id]['state']
else: return False
async def delete_state(self, chat_id):
"""Delete a state"""
states_data = self._read_data()
states_data.pop(chat_id)
await self._save_data(states_data)
def _read_data(self):
"""
Read the data from file.
"""
file = open(self.file_path, 'rb')
states_data = pickle.load(file)
file.close()
return states_data
def _create_dir(self):
"""
Create directory .save-handlers.
"""
dirs = self.file_path.rsplit('/', maxsplit=1)[0]
os.makedirs(dirs, exist_ok=True)
if not os.path.isfile(self.file_path):
with open(self.file_path,'wb') as file:
pickle.dump({}, file)
async def _save_data(self, new_data):
"""
Save data after editing.
:param new_data:
"""
with open(self.file_path, 'wb+') as state_file:
pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL)
return True
def _get_data(self, chat_id):
return self._read_data()[chat_id]['data']
async def set(self, chat_id, new_state):
"""
Set a new state for a user.
:param chat_id:
:param new_state: new_state of a user
"""
await self.add_state(chat_id,new_state)
async def _add_data(self, chat_id, key, value):
states_data = self._read_data()
result = states_data[chat_id]['data'][key] = value
await self._save_data(result)
return result
async def finish(self, chat_id):
"""
Finish(delete) state of a user.
:param chat_id:
"""
await self.delete_state(chat_id)
def retrieve_data(self, chat_id):
"""
Save input text.
Usage:
with bot.retrieve_data(message.chat.id) as data:
data['name'] = message.text
Also, at the end of your 'Form' you can get the name:
data['name']
"""
return StateFileContext(self, chat_id)
class StateContext:
"""
Class for data.
"""
def __init__(self , obj: StateMemory, chat_id) -> None:
self.obj = obj
self.chat_id = chat_id
self.data = obj._get_data(chat_id)
async def __aenter__(self):
return self.data
async def __aexit__(self, exc_type, exc_val, exc_tb):
return
class StateFileContext:
"""
Class for data.
"""
def __init__(self , obj: StateFile, chat_id) -> None:
self.obj = obj
self.chat_id = chat_id
self.data = None
async def __aenter__(self):
self.data = self.obj._get_data(self.chat_id)
return self.data
async def __aexit__(self, exc_type, exc_val, exc_tb):
old_data = self.obj._read_data()
for i in self.data:
old_data[self.chat_id]['data'][i] = self.data.get(i)
await self.obj._save_data(old_data)
return
class BaseMiddleware:
"""
Base class for middleware.
Your middlewares should be inherited from this class.
"""
def __init__(self):
pass
async def pre_process(self, message, data):
raise NotImplementedError
async def post_process(self, message, data, exception):
raise NotImplementedError

1635
telebot/asyncio_helper.py Normal file

File diff suppressed because it is too large Load Diff

115
telebot/callback_data.py Normal file
View File

@ -0,0 +1,115 @@
import typing
class CallbackDataFilter:
def __init__(self, factory, config: typing.Dict[str, str]):
self.config = config
self.factory = factory
def check(self, query):
"""
Checks if query.data appropriates to specified config
:param query: telebot.types.CallbackQuery
:return: bool
"""
try:
data = self.factory.parse(query.data)
except ValueError:
return False
for key, value in self.config.items():
if isinstance(value, (list, tuple, set, frozenset)):
if data.get(key) not in value:
return False
elif data.get(key) != value:
return False
return True
class CallbackData:
"""
Callback data factory
This class will help you to work with CallbackQuery
"""
def __init__(self, *parts, prefix: str, sep=':'):
if not isinstance(prefix, str):
raise TypeError(f'Prefix must be instance of str not {type(prefix).__name__}')
if not prefix:
raise ValueError("Prefix can't be empty")
if sep in prefix:
raise ValueError(f"Separator {sep!r} can't be used in prefix")
self.prefix = prefix
self.sep = sep
self._part_names = parts
def new(self, *args, **kwargs) -> str:
"""
Generate callback data
:param args: positional parameters of CallbackData instance parts
:param kwargs: named parameters
:return: str
"""
args = list(args)
data = [self.prefix]
for part in self._part_names:
value = kwargs.pop(part, None)
if value is None:
if args:
value = args.pop(0)
else:
raise ValueError(f'Value for {part!r} was not passed!')
if value is not None and not isinstance(value, str):
value = str(value)
if self.sep in value:
raise ValueError(f"Symbol {self.sep!r} is defined as the separator and can't be used in parts' values")
data.append(value)
if args or kwargs:
raise TypeError('Too many arguments were passed!')
callback_data = self.sep.join(data)
if len(callback_data.encode()) > 64:
raise ValueError('Resulted callback data is too long!')
return callback_data
def parse(self, callback_data: str) -> typing.Dict[str, str]:
"""
Parse data from the callback data
:param callback_data: string, use to telebot.types.CallbackQuery to parse it from string to a dict
:return: dict parsed from callback data
"""
prefix, *parts = callback_data.split(self.sep)
if prefix != self.prefix:
raise ValueError("Passed callback data can't be parsed with that prefix.")
elif len(parts) != len(self._part_names):
raise ValueError('Invalid parts count!')
result = {'@': prefix}
result.update(zip(self._part_names, parts))
return result
def filter(self, **config) -> CallbackDataFilter:
"""
Generate filter
:param config: specified named parameters will be checked with CallbackQury.data
:return: CallbackDataFilter class
"""
for key in config.keys():
if key not in self._part_names:
raise ValueError(f'Invalid field name {key!r}')
return CallbackDataFilter(self, config)

176
telebot/custom_filters.py Normal file
View File

@ -0,0 +1,176 @@
from abc import ABC
class SimpleCustomFilter(ABC):
"""
Simple Custom Filter base class.
Create child class with check() method.
Accepts only message, returns bool value, that is compared with given in handler.
"""
def check(self, message):
"""
Perform a check.
"""
pass
class AdvancedCustomFilter(ABC):
"""
Simple Custom Filter base class.
Create child class with check() method.
Accepts two parameters, returns bool: True - filter passed, False - filter failed.
message: Message class
text: Filter value given in handler
"""
def check(self, message, text):
"""
Perform a check.
"""
pass
class TextMatchFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
@bot.message_handler(text=['account'])
"""
key = 'text'
def check(self, message, text):
if type(text) is list:return message.text in text
else: return text == message.text
class TextContainsFilter(AdvancedCustomFilter):
"""
Filter to check Text message.
key: text
Example:
# Will respond if any message.text contains word 'account'
@bot.message_handler(text_contains=['account'])
"""
key = 'text_contains'
def check(self, message, text):
return text in message.text
class TextStartsFilter(AdvancedCustomFilter):
"""
Filter to check whether message starts with some text.
Example:
# Will work if message.text starts with 'Sir'.
@bot.message_handler(text_startswith='Sir')
"""
key = 'text_startswith'
def check(self, message, text):
return message.text.startswith(text)
class ChatFilter(AdvancedCustomFilter):
"""
Check whether chat_id corresponds to given chat_id.
Example:
@bot.message_handler(chat_id=[99999])
"""
key = 'chat_id'
def check(self, message, text):
return message.chat.id in text
class ForwardFilter(SimpleCustomFilter):
"""
Check whether message was forwarded from channel or group.
Example:
@bot.message_handler(is_forwarded=True)
"""
key = 'is_forwarded'
def check(self, message):
return message.forward_from_chat is not None
class IsReplyFilter(SimpleCustomFilter):
"""
Check whether message is a reply.
Example:
@bot.message_handler(is_reply=True)
"""
key = 'is_reply'
def check(self, message):
return message.reply_to_message is not None
class LanguageFilter(AdvancedCustomFilter):
"""
Check users language_code.
Example:
@bot.message_handler(language_code=['ru'])
"""
key = 'language_code'
def check(self, message, text):
if type(text) is list:return message.from_user.language_code in text
else: return message.from_user.language_code == text
class IsAdminFilter(SimpleCustomFilter):
"""
Check whether the user is administrator / owner of the chat.
Example:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
"""
key = 'is_chat_admin'
def __init__(self, bot):
self._bot = bot
def check(self, message):
return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator']
class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
"""
def __init__(self, bot):
self.bot = bot
key = 'state'
def check(self, message, text):
if self.bot.current_states.current_state(message.from_user.id) is False: return False
elif text == '*': return True
elif type(text) is list: return self.bot.current_states.current_state(message.from_user.id) in text
return self.bot.current_states.current_state(message.from_user.id) == text
class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
"""
key = 'is_digit'
def check(self, message):
return message.text.isdigit()

View File

@ -141,3 +141,202 @@ class RedisHandlerBackend(HandlerBackend):
handlers = pickle.loads(value)
self.clear_handlers(handler_group_id)
return handlers
class StateMemory:
def __init__(self):
self._states = {}
def add_state(self, chat_id, state):
"""
Add a state.
:param chat_id:
:param state: new state
"""
if chat_id in self._states:
self._states[chat_id]['state'] = state
else:
self._states[chat_id] = {'state': state,'data': {}}
def current_state(self, chat_id):
"""Current state"""
if chat_id in self._states: return self._states[chat_id]['state']
else: return False
def delete_state(self, chat_id):
"""Delete a state"""
self._states.pop(chat_id)
def _get_data(self, chat_id):
return self._states[chat_id]['data']
def set(self, chat_id, new_state):
"""
Set a new state for a user.
:param chat_id:
:param new_state: new_state of a user
"""
self.add_state(chat_id,new_state)
def _add_data(self, chat_id, key, value):
result = self._states[chat_id]['data'][key] = value
return result
def finish(self, chat_id):
"""
Finish(delete) state of a user.
:param chat_id:
"""
self.delete_state(chat_id)
def retrieve_data(self, chat_id):
"""
Save input text.
Usage:
with bot.retrieve_data(message.chat.id) as data:
data['name'] = message.text
Also, at the end of your 'Form' you can get the name:
data['name']
"""
return StateContext(self, chat_id)
class StateFile:
"""
Class to save states in a file.
"""
def __init__(self, filename):
self.file_path = filename
def add_state(self, chat_id, state):
"""
Add a state.
:param chat_id:
:param state: new state
"""
states_data = self._read_data()
if chat_id in states_data:
states_data[chat_id]['state'] = state
return self._save_data(states_data)
else:
new_data = states_data[chat_id] = {'state': state,'data': {}}
return self._save_data(states_data)
def current_state(self, chat_id):
"""Current state."""
states_data = self._read_data()
if chat_id in states_data: return states_data[chat_id]['state']
else: return False
def delete_state(self, chat_id):
"""Delete a state"""
states_data = self._read_data()
states_data.pop(chat_id)
self._save_data(states_data)
def _read_data(self):
"""
Read the data from file.
"""
file = open(self.file_path, 'rb')
states_data = pickle.load(file)
file.close()
return states_data
def _create_dir(self):
"""
Create directory .save-handlers.
"""
dirs = self.file_path.rsplit('/', maxsplit=1)[0]
os.makedirs(dirs, exist_ok=True)
if not os.path.isfile(self.file_path):
with open(self.file_path,'wb') as file:
pickle.dump({}, file)
def _save_data(self, new_data):
"""
Save data after editing.
:param new_data:
"""
with open(self.file_path, 'wb+') as state_file:
pickle.dump(new_data, state_file, protocol=pickle.HIGHEST_PROTOCOL)
return True
def _get_data(self, chat_id):
return self._read_data()[chat_id]['data']
def set(self, chat_id, new_state):
"""
Set a new state for a user.
:param chat_id:
:param new_state: new_state of a user
"""
self.add_state(chat_id,new_state)
def _add_data(self, chat_id, key, value):
states_data = self._read_data()
result = states_data[chat_id]['data'][key] = value
self._save_data(result)
return result
def finish(self, chat_id):
"""
Finish(delete) state of a user.
:param chat_id:
"""
self.delete_state(chat_id)
def retrieve_data(self, chat_id):
"""
Save input text.
Usage:
with bot.retrieve_data(message.chat.id) as data:
data['name'] = message.text
Also, at the end of your 'Form' you can get the name:
data['name']
"""
return StateFileContext(self, chat_id)
class StateContext:
"""
Class for data.
"""
def __init__(self , obj: StateMemory, chat_id) -> None:
self.obj = obj
self.chat_id = chat_id
self.data = obj._get_data(chat_id)
def __enter__(self):
return self.data
def __exit__(self, exc_type, exc_val, exc_tb):
return
class StateFileContext:
"""
Class for data.
"""
def __init__(self , obj: StateFile, chat_id) -> None:
self.obj = obj
self.chat_id = chat_id
self.data = self.obj._get_data(self.chat_id)
def __enter__(self):
return self.data
def __exit__(self, exc_type, exc_val, exc_tb):
old_data = self.obj._read_data()
for i in self.data:
old_data[self.chat_id]['data'][i] = self.data.get(i)
self.obj._save_data(old_data)
return

File diff suppressed because it is too large Load Diff

View File

@ -4,31 +4,49 @@ import re
import string
import threading
import traceback
import warnings
import functools
from typing import Any, Callable, List, Dict, Optional, Union
# noinspection PyPep8Naming
import queue as Queue
import logging
from telebot import types
try:
import ujson as json
except ImportError:
import json
try:
# noinspection PyPackageRequirements
from PIL import Image
from io import BytesIO
pil_imported = True
except:
pil_imported = False
MAX_MESSAGE_LENGTH = 4096
logger = logging.getLogger('TeleBot')
thread_local = threading.local()
content_type_media = [
'text', 'audio', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll',
'text', 'audio', 'animation', 'document', 'photo', 'sticker', 'video', 'video_note', 'voice', 'contact', 'dice', 'poll',
'venue', 'location'
]
content_type_service = [
'new_chat_members', 'left_chat_member', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message'
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message',
'proximity_alert_triggered', 'voice_chat_scheduled', 'voice_chat_started', 'voice_chat_ended',
'voice_chat_participants_invited', 'message_auto_delete_timer_changed'
]
update_types = [
"update_id", "message", "edited_message", "channel_post", "edited_channel_post", "inline_query",
"chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer",
"my_chat_member", "chat_member", "chat_join_request"
]
class WorkerThread(threading.Thread):
@ -152,6 +170,16 @@ class AsyncTask:
return self.result
class CustomRequestResponse():
def __init__(self, json_text, status_code = 200, reason = ""):
self.status_code = status_code
self.text = json_text
self.reason = reason
def json(self):
return json.loads(self.text)
def async_dec():
def decorator(fn):
def wrapper(*args, **kwargs):
@ -165,15 +193,19 @@ def async_dec():
def is_string(var):
return isinstance(var, str)
def is_dict(var):
return isinstance(var, dict)
def is_bytes(var):
return isinstance(var, bytes)
def is_pil_image(var):
return pil_imported and isinstance(var, Image.Image)
def pil_image_to_file(image, extension='JPEG', quality='web_low'):
if pil_imported:
photoBuffer = BytesIO()
@ -184,17 +216,18 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'):
else:
raise RuntimeError('PIL module is not imported')
def is_command(text):
def is_command(text: str) -> bool:
"""
Checks if `text` is a command. Telegram chat commands start with the '/' character.
:param text: Text to check.
:return: True if `text` is a command, else False.
"""
if (text is None): return None
if text is None: return False
return text.startswith('/')
def extract_command(text):
def extract_command(text: str) -> Union[str, None]:
"""
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
If `text` is not a command, this function returns None.
@ -208,11 +241,28 @@ def extract_command(text):
:param text: String to extract the command from
:return: the command if `text` is a command (according to is_command), else None.
"""
if (text is None): return None
if text is None: return None
return text.split()[0].split('@')[0][1:] if is_command(text) else None
def split_string(text, chars_per_string):
def extract_arguments(text: str) -> str:
"""
Returns the argument after the command.
Examples:
extract_arguments("/get name"): 'name'
extract_arguments("/get"): ''
extract_arguments("/get@botName name"): 'name'
:param text: String to extract the arguments from a command
:return: the arguments if `text` is a command (according to is_command), else None.
"""
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE)
result = regexp.match(text)
return result.group(2) if is_command(text) else None
def split_string(text: str, chars_per_string: int) -> List[str]:
"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
@ -223,6 +273,107 @@ def split_string(text, chars_per_string):
"""
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]:
"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
If `chars_per_string` > 4096: `chars_per_string` = 4096.
Splits by '\n', '. ' or ' ' in exactly this priority.
:param text: The text to split
:param chars_per_string: The number of maximum characters per part the text is split to.
:return: The splitted text as a list of strings.
"""
def _text_before_last(substr: str) -> str:
return substr.join(part.split(substr)[:-1]) + substr
if chars_per_string > MAX_MESSAGE_LENGTH: chars_per_string = MAX_MESSAGE_LENGTH
parts = []
while True:
if len(text) < chars_per_string:
parts.append(text)
return parts
part = text[:chars_per_string]
if "\n" in part: part = _text_before_last("\n")
elif ". " in part: part = _text_before_last(". ")
elif " " in part: part = _text_before_last(" ")
parts.append(part)
text = text[len(part):]
def escape(text: str) -> str:
"""
Replaces the following chars in `text` ('&' with '&amp;', '<' with '&lt;' and '>' with '&gt;').
:param text: the text to escape
:return: the escaped text
"""
chars = {"&": "&amp;", "<": "&lt;", ">": "&gt"}
for old, new in chars.items(): text = text.replace(old, new)
return text
def user_link(user: types.User, include_id: bool=False) -> str:
"""
Returns an HTML user link. This is useful for reports.
Attention: Don't forget to set parse_mode to 'HTML'!
Example:
bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML')
:param user: the user (not the user_id)
:param include_id: include the user_id
:return: HTML user link
"""
name = escape(user.first_name)
return (f"<a href='tg://user?id={user.id}'>{name}</a>"
+ (f" (<pre>{user.id}</pre>)" if include_id else ""))
def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.InlineKeyboardMarkup:
"""
Returns a reply markup from a dict in this format: {'text': kwargs}
This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)'
Example:
quick_markup({
'Twitter': {'url': 'https://twitter.com'},
'Facebook': {'url': 'https://facebook.com'},
'Back': {'callback_data': 'whatever'}
}, row_width=2):
returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
and a back button below
kwargs can be:
{
'url': None,
'callback_data': None,
'switch_inline_query': None,
'switch_inline_query_current_chat': None,
'callback_game': None,
'pay': None,
'login_url': None
}
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
:param row_width: int row width
:return: InlineKeyboardMarkup
"""
markup = types.InlineKeyboardMarkup(row_width=row_width)
buttons = [
types.InlineKeyboardButton(text=text, **kwargs)
for text, kwargs in values.items()
]
markup.add(*buttons)
return markup
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
def or_set(self):
self._set()
@ -243,8 +394,10 @@ def orify(e, changed_callback):
e.set = lambda: or_set(e)
e.clear = lambda: or_clear(e)
def OrEvent(*events):
or_event = threading.Event()
def changed():
bools = [ev.is_set() for ev in events]
if any(bools):
@ -263,22 +416,6 @@ def OrEvent(*events):
changed()
return or_event
def extract_arguments(text):
"""
Returns the argument after the command.
Examples:
extract_arguments("/get name"): 'name'
extract_arguments("/get"): ''
extract_arguments("/get@botName name"): 'name'
:param text: String to extract the arguments from a command
:return: the arguments if `text` is a command (according to is_command), else None.
"""
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
result = regexp.match(text)
return result.group(2) if is_command(text) else None
def per_thread(key, construct_value, reset=False):
if reset or not hasattr(thread_local, key):
@ -287,26 +424,71 @@ def per_thread(key, construct_value, reset=False):
return getattr(thread_local, key)
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
# https://stackoverflow.com/a/312464/9935473
for i in range(0, len(lst), n):
yield lst[i:i + n]
def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16))
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used."""
# https://stackoverflow.com/a/30253848/441814
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # turn off filter
warnings.warn("Call to deprecated function {}.".format(func.__name__),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning) # reset filter
return func(*args, **kwargs)
return new_func
def deprecated(warn: bool=False, alternative: Optional[Callable]=None):
"""
Use this decorator to mark functions as deprecated.
When the function is used, an info (or warning if `warn` is True) is logged.
:param warn: If True a warning is logged else an info
:param alternative: The new function to use instead
"""
def decorator(function):
def wrapper(*args, **kwargs):
if not warn:
logger.info(f"`{function.__name__}` is deprecated."
+ (f" Use `{alternative.__name__}` instead" if alternative else ""))
else:
logger.warn(f"`{function.__name__}` is deprecated."
+ (f" Use `{alternative.__name__}` instead" if alternative else ""))
return function(*args, **kwargs)
return wrapper
return decorator
# Cloud helpers
def webhook_google_functions(bot, request):
"""A webhook endpoint for Google Cloud Functions FaaS."""
if request.is_json:
try:
request_json = request.get_json()
update = types.Update.de_json(request_json)
bot.process_new_updates([update])
return ''
except Exception as e:
print(e)
return 'Bot FAIL', 400
else:
return 'Bot ON'
def antiflood(function, *args, **kwargs):
"""
Use this function inside loops in order to avoid getting TooManyRequests error.
Example:
from telebot.util import antiflood
for chat_id in chat_id_list:
msg = antiflood(bot.send_message, chat_id, text)
You want get the
"""
from telebot.apihelper import ApiTelegramException
from time import sleep
try:
msg = function(*args, **kwargs)
except ApiTelegramException as ex:
if ex.error_code == 429:
sleep(ex.result_json['parameters']['retry_after'])
msg = function(*args, **kwargs)
finally:
return msg

View File

@ -1,3 +1,3 @@
# Versions should comply with PEP440.
# This line is parsed in setup.py:
__version__ = '3.7.8'
__version__ = '4.2.1'

View File

@ -62,8 +62,12 @@ def update_type(message):
pre_checkout_query = None
poll = None
poll_answer = None
my_chat_member = None
chat_member = None
chat_join_request = None
return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
my_chat_member, chat_member, chat_join_request)
@pytest.fixture()
@ -78,9 +82,12 @@ def reply_to_message_update_type(reply_to_message):
pre_checkout_query = None
poll = None
poll_answer = None
my_chat_member = None
chat_member = None
chat_join_request = None
return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post,
inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
inline_query, chosen_inline_result, callback_query, shipping_query, pre_checkout_query,
poll, poll_answer, my_chat_member, chat_member, chat_join_request)
def next_handler(message):

View File

@ -6,6 +6,7 @@ sys.path.append('../')
import time
import pytest
import os
from datetime import datetime, timedelta
import telebot
from telebot import types
@ -18,6 +19,14 @@ if not should_skip:
CHAT_ID = os.environ['CHAT_ID']
GROUP_ID = os.environ['GROUP_ID']
def _new_test():
pass
@util.deprecated(alternative=_new_test)
def _test():
pass
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
class TestTeleBot:
@ -125,6 +134,16 @@ class TestTeleBot:
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
assert ret_msg.message_id
def test_send_file_with_filename(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data)
assert ret_msg.message_id
ret_msg = tb.send_document(CHAT_ID, file_data, visible_file_name="test.jpg")
assert ret_msg.message_id
def test_send_file_dis_noti(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN)
@ -407,6 +426,23 @@ class TestTeleBot:
cn = tb.get_chat_members_count(GROUP_ID)
assert cn > 1
def test_export_chat_invite_link(self):
tb = telebot.TeleBot(TOKEN)
il = tb.export_chat_invite_link(GROUP_ID)
assert isinstance(il, str)
def test_create_revoke_detailed_chat_invite_link(self):
tb = telebot.TeleBot(TOKEN)
cil = tb.create_chat_invite_link(GROUP_ID,
(datetime.now() + timedelta(minutes=1)).timestamp(), member_limit=5)
assert isinstance(cil.invite_link, str)
assert cil.creator.id == tb.get_me().id
assert isinstance(cil.expire_date, (float, int))
assert cil.member_limit == 5
assert not cil.is_revoked
rcil = tb.revoke_chat_invite_link(GROUP_ID, cil.invite_link)
assert rcil.is_revoked
def test_edit_markup(self):
text = 'CI Test Message'
tb = telebot.TeleBot(TOKEN)
@ -419,6 +455,13 @@ class TestTeleBot:
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
assert new_msg.message_id
def test_antiflood(self):
text = "Flooding"
tb = telebot.TeleBot(TOKEN)
for _ in range(0,100):
util.antiflood(tb.send_message, CHAT_ID, text)
assert _
@staticmethod
def create_text_message(text):
params = {'text': text}
@ -440,8 +483,12 @@ class TestTeleBot:
pre_checkout_query = None
poll = None
poll_answer = None
my_chat_member = None
chat_member = None
chat_join_request = None
return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
my_chat_member, chat_member, chat_join_request)
def test_is_string_unicode(self):
s1 = u'string'
@ -525,6 +572,24 @@ class TestTeleBot:
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
assert ret_msg.caption_entities[0].type == 'italic'
def test_chat_commands(self):
tb = telebot.TeleBot(TOKEN)
command, description, lang = 'command_1', 'description of command 1', 'en'
scope = telebot.types.BotCommandScopeChat(CHAT_ID)
ret_msg = tb.set_my_commands([telebot.types.BotCommand(command, description)], scope, lang)
assert ret_msg is True
ret_msg = tb.get_my_commands(scope, lang)
assert ret_msg[0].command == command
assert ret_msg[0].description == description
ret_msg = tb.delete_my_commands(scope, lang)
assert ret_msg is True
ret_msg = tb.get_my_commands(scope, lang)
assert ret_msg == []
def test_typed_middleware_handler(self):
from telebot import apihelper
@ -566,6 +631,9 @@ class TestTeleBot:
tb.process_new_updates([update])
time.sleep(1)
assert update.message.text == 'got' * 2
def test_deprecated_dec(self):
_test()
def test_chat_permissions(self):
return # CHAT_ID is private chat, no permissions can be set

View File

@ -210,7 +210,7 @@ def test_json_poll_answer():
poll_answer = types.PollAnswer.de_json(jsonstring)
assert poll_answer.poll_id == '5895675970559410186'
assert isinstance(poll_answer.user, types.User)
assert poll_answer.options_ids == [1]
assert poll_answer.option_ids == [1]
def test_KeyboardButtonPollType():
@ -219,3 +219,29 @@ def test_KeyboardButtonPollType():
json_str = markup.to_json()
assert 'request_poll' in json_str
assert 'quiz' in json_str
def test_json_chat_invite_link():
json_string = r'{"invite_link":"https://t.me/joinchat/MeASP-Wi...","creator":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"ru"},"pending_join_request_count":1,"creates_join_request":true,"is_primary":false,"is_revoked":false}'
invite_link = types.ChatInviteLink.de_json(json_string)
assert invite_link.invite_link == 'https://t.me/joinchat/MeASP-Wi...'
assert isinstance(invite_link.creator, types.User)
assert not invite_link.is_primary
assert not invite_link.is_revoked
assert invite_link.expire_date is None
assert invite_link.member_limit is None
assert invite_link.name is None
assert invite_link.creator.id == 927266710
assert invite_link.pending_join_request_count == 1
assert invite_link.creates_join_request
def test_chat_member_updated():
json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}'
cm_updated = types.ChatMemberUpdated.de_json(json_string)
assert cm_updated.chat.id == -1234567890123
assert cm_updated.from_user.id == 133869498
assert cm_updated.date == 1624119999
assert cm_updated.old_chat_member.status == "member"
assert cm_updated.new_chat_member.status == "administrator"