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

Compare commits

...

468 Commits
4.2.0 ... 4.6.1

Author SHA1 Message Date
914c5fdf0c Merge pull request #1649 from Badiboy/master
Bump version to 4.6.1
2022-07-27 17:19:53 +03:00
8aa3d052cc Bump version to 4.6.1 2022-07-27 17:18:54 +03:00
c145b7ef8f Merge pull request #1644 from coder2020official/test_branch
Updated all docstrings(visual)
2022-07-26 21:31:27 +03:00
6303ecc7c6 Little fix 2022-07-26 20:45:32 +05:00
fc01ec50fc Added .ext file to documentation 2022-07-26 16:30:51 +05:00
51b2ec701d Removed unecessary methods from documentation(to_json, de_json, to_dict) 2022-07-26 16:27:15 +05:00
3d7f334d79 Updated all docstrings for types(visual) 2022-07-26 16:16:35 +05:00
a61508ca0c Merge pull request #1643 from robz-tirtlib/patch-1
small fix
2022-07-25 21:42:22 +03:00
9d9e76e724 small fix
Fixed "ERROR - TeleBot: "message_handler: Commands filter should be list of strings (commands), unknown type supplied to the 'commands' filter list. Not able to use the supplied type."
2022-07-25 15:49:33 +03:00
b0e06253ff Completed docstrings for all files except types.py 2022-07-24 23:14:09 +05:00
7c12162576 Merge pull request #1640 from coder2020official/master
Updated all docstrings / typehints in __init__.py(visual)
2022-07-20 11:35:40 +03:00
f0feb45e87 Completed all docstrings / typehints up to 100%(visual) 2022-07-19 23:51:25 +05:00
c2cfe24426 Typehints & Docstrings completed for sync version up to 100%(visual) 2022-07-19 23:49:05 +05:00
f6ec3493ad Fixed 45% of typehints/docstrings for sync telebot 2022-07-19 00:27:21 +05:00
e553f3aa1d Merge pull request #1638 from coder2020official/master
Fixes #1637, #1636
2022-07-18 18:14:52 +03:00
49d3b463ed Fixes #1636 2022-07-18 14:01:14 +05:00
147278733b Fix #1637 2022-07-18 13:47:14 +05:00
54ad1582aa Merge pull request #1635 from coder2020official/master
Fixed description
2022-07-17 00:42:55 +03:00
e379708af6 Update __init__.py 2022-07-16 20:12:38 +05:00
9f7b113e2f Merge branch 'master' into master 2022-07-16 20:11:40 +05:00
482498e1e5 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-07-16 20:09:54 +05:00
22beead3b5 Update __init__.py 2022-07-16 20:09:52 +05:00
2b01765627 Merge pull request #1633 from coder2020official/master
Update async_telebot.py
2022-07-16 18:04:52 +03:00
5dbe1b3523 Merge pull request #1631 from coder2020official/newfeatures
update_sensitive field for middlewares
2022-07-16 12:46:12 +03:00
3ffd06fcca Add feedback-bot to Bots using this library section 2022-07-15 23:26:55 +05:00
ea1efad1ea Update asyncio_helper.py 2022-07-15 22:28:43 +05:00
1efe465e9d Update async_telebot.py 2022-07-15 22:06:18 +05:00
0c6f84c79a update_sensitive field for middlewares 2022-07-15 16:24:15 +05:00
bf415e4bd7 Merge pull request #1624 from coder2020official/master
Set request_timeout to default aiohttp's timeout(5 minutes)
2022-07-13 20:01:30 +03:00
2fcfdc2584 Merge branch 'eternnoir:master' into master 2022-07-13 21:52:57 +05:00
659501efef Update async_telebot.py 2022-07-13 21:52:48 +05:00
92654ee970 Merge pull request #1623 from Badiboy/master
Fix exception with typed_middleware_handlers
2022-07-13 15:08:12 +03:00
a1bcd3c42e use_class_middlewares checks added 2022-07-13 13:10:16 +03:00
b276bfacaf Fix exception with typed_middleware_handlers
+ some additional checks
2022-07-13 12:30:13 +03:00
726b203724 Merge pull request #1621 from Badiboy/master
Typo
2022-07-12 22:48:01 +03:00
16703161aa Typo 2022-07-12 22:44:25 +03:00
a3a55e7393 Merge pull request #1617 from coder2020official/bugfixes
Extended exception handler behaviour with middlewares for synchronous version as well
2022-07-12 22:12:20 +03:00
1c11898ea1 Merge pull request #1612 from coder2020official/newfeatures
run_webhooks() built in function to listen and process webhook requests.
2022-07-11 23:33:39 +03:00
8c6f81546c A typo fixed 2022-07-11 23:24:22 +05:00
194bf6e95d Merge branch 'bugfixes' of https://github.com/coder2020official/pyTelegramBotAPI into bugfixes 2022-07-11 23:23:10 +05:00
124606fdcb Extend exception handler behaviour with middlewares 2022-07-11 23:22:11 +05:00
90a90d4a34 Divided async and sync versions into aio & sync folders 2022-07-09 22:30:36 +05:00
d67ee2a5c5 Delete webhooks.py 2022-07-08 21:16:01 +05:00
970b9d6be4 SyncWebhookListener was rewritten under fastapi. Extensions folder was divided into 2(namings are long, might be changed) 2022-07-08 21:13:07 +05:00
0b5b7ad39a Merge pull request #1614 from Badiboy/master
parse_web_app_data function fix
2022-07-08 11:19:35 +03:00
a7c420aa14 parse_web_app_data function fix 2022-07-08 11:15:50 +03:00
0cf2a0fe77 Added extra dependencies and fixed tests 2022-07-07 23:02:51 +05:00
2f32236680 Added run_webhooks for asynctelebot 2022-07-07 22:56:13 +05:00
f8f147f9f4 Fix certificate for webhooks 2022-07-07 15:53:27 +05:00
140befc132 Typehint fix if there is no flask installed 2022-07-07 15:15:24 +05:00
36fbb13663 Merge branch 'newfeatures' of https://github.com/coder2020official/pyTelegramBotAPI into newfeatures 2022-07-07 15:09:45 +05:00
e353572c03 Update webhooks listeners. 2022-07-07 15:09:02 +05:00
ebca668979 Create __init__.py 2022-07-06 23:35:59 +05:00
7b8c98d731 Test 2022-07-06 23:15:22 +05:00
3cf5305845 Rename 2022-07-06 22:06:49 +05:00
eb4cd7aba0 Webhook processing function using flask for sync 2022-07-06 21:31:03 +05:00
01f9e3b710 Merge pull request #1607 from coder2020official/newfeatures
ReadME update and filter updates
2022-07-05 22:40:54 +03:00
e1094c6f02 Update README.md 2022-07-05 21:50:11 +05:00
174bbf9c84 Merge branch 'eternnoir:master' into newfeatures 2022-07-05 21:19:21 +05:00
1df19e3b2d CallbackQuery fixes for custom filters
Now some custom filters support callback query messages
2022-07-05 21:18:42 +05:00
e0e6eee374 Merge pull request #1605 from Badiboy/master
ChatMember is now typed
2022-07-05 00:01:38 +03:00
78251cdf43 ChatMember type checking reordered 2022-07-04 22:41:01 +03:00
81cbddb8cd Added source data (json_string) to CallbackQuery 2022-07-04 22:36:42 +03:00
0aa9f0fb42 Update type fix 2022-07-04 22:26:24 +03:00
f3b1f97362 ChatMember is now typed
https://core.telegram.org/bots/api#chatmember
2022-07-03 23:33:55 +03:00
df1977911e Merge pull request #1603 from coder2020official/master
#1594 & fixes regarding handler execution in async
2022-07-02 23:12:49 +03:00
d861fd0042 Merge branch 'eternnoir:master' into master 2022-07-02 21:07:23 +05:00
1fb14e28d4 Fix handler execution and #1594 2022-07-02 21:07:00 +05:00
b691a467c0 Merge pull request #1602 from GooGuJiang/patch-1
Add bot to README.md
2022-07-02 11:18:04 +03:00
badf982147 Update README.md 2022-07-02 15:08:06 +08:00
6d52090ef9 Update README.md 2022-07-02 15:06:46 +08:00
cf3a85d699 Update README.md 2022-07-02 14:31:37 +08:00
ef86453126 Update README.md 2022-07-02 14:23:31 +08:00
d9ab5b0d28 Merge pull request #1599 from coder2020official/master
Fix group and supergroup issues with states
2022-07-01 13:55:48 +03:00
c920809fa9 Update asyncio_helper.py 2022-07-01 15:39:42 +05:00
5939e754bb Update asyncio_helper.py 2022-07-01 15:29:34 +05:00
3019b71f7f Merge branch 'eternnoir:master' into master 2022-06-30 17:54:25 +05:00
0bdf925fbe Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-06-30 17:54:16 +05:00
d14064a84d Fix group and supergroup issues 2022-06-30 17:51:58 +05:00
49c93b6027 Merge pull request #1598 from coder2020official/master
Fix typehint for set_state
2022-06-30 15:32:30 +03:00
36749cbdd9 Merge branch 'eternnoir:master' into master 2022-06-30 17:10:25 +05:00
6d12b1f2a7 Update callback_data.py 2022-06-30 17:10:14 +05:00
419bc5878f Fix typehint for ``set_state`` 2022-06-30 17:06:39 +05:00
b9b4885568 Merge pull request #1597 from Badiboy/master
Copyright update
2022-06-29 19:25:40 +03:00
ce0a974c91 Copyright update 2022-06-29 19:24:27 +03:00
c36f3a228e Update callback_data.py 2022-06-29 21:17:57 +05:00
ca525b5bea Copyright changes 2022-06-29 21:06:36 +05:00
6ff015689a Merge pull request #1595 from coder2020official/master
Add the possibility to getbase class of a State object
2022-06-29 18:34:06 +03:00
6459aded82 Merge branch 'eternnoir:master' into master 2022-06-29 20:22:59 +05:00
fec47cecaf Add the possibility to getbase class of a State object 2022-06-29 20:21:42 +05:00
3892b0fb80 Merge pull request #1593 from coder2020official/master
Fixed previous fix 🤦‍♂️
2022-06-29 12:58:43 +03:00
fbb9a73fc0 F###, forgot to put await 2022-06-29 14:52:37 +05:00
db5c53b8e5 Merge pull request #1591 from coder2020official/master
Pass only the necessary data
2022-06-28 17:58:35 +03:00
6e8abc709e Pass only the necessary data 2022-06-28 19:51:51 +05:00
752f35614c Merge pull request #1587 from coder2020official/master
Middleware update: everything in data will be passed to handler if ne…
2022-06-26 12:09:38 +03:00
a2893945b2 Async changes and sync improvements 2022-06-25 22:15:53 +05:00
1686ce4f44 Middleware update: everything in data will be passed to handler if needed. 2022-06-25 21:48:44 +05:00
24b1129c8a Merge pull request #1584 from coder2020official/master
Bot API 6.1 update
2022-06-23 10:50:39 +03:00
7f9dac4147 Fix previous commit 2022-06-23 12:46:27 +05:00
f96775e9eb Merge pull request #1585 from Prikalel/master
Rename `midddleware` to `middleware` in examples.
2022-06-23 10:41:20 +03:00
1342cab259 Fix boolean parameters 2022-06-23 12:05:26 +05:00
dd8125cbd0 Rename midddleware to middleware in examples. 2022-06-23 09:09:37 +03:00
8769802744 Bump version to 4.6.0 2022-06-21 15:44:42 +05:00
a7fb8a2bec Implemented some tests 2022-06-21 15:28:01 +05:00
d7f34ae370 Fix the typo 2022-06-21 15:27:45 +05:00
7d931abe37 Added secret token and create_invoice_link for asynctelebot 2022-06-21 15:22:00 +05:00
f52124827f Added all new changes for types (is_premium, added_to_attachment_menu, and etc) 2022-06-21 15:21:35 +05:00
0d0f9ef330 Added secret token and create_invoice_link 2022-06-21 15:20:55 +05:00
24cdcb1bcc Update README.md 2022-06-21 14:22:57 +05:00
7994a80a00 Merge pull request #1580 from e323w/master
Fixed async long polling
2022-06-15 11:21:12 +03:00
b2662274f9 Fixed async long polling 2022-06-15 03:24:51 +04:00
a21ab203a1 Merge pull request #1575 from coder2020official/bugfixes
Fix bug with unsaving data
2022-06-04 19:44:47 +03:00
e689e968db Fix bug with unsaving data 2022-06-04 21:33:05 +05:00
808810e6d6 Merge pull request #1574 from Mayson90/master
add hydrolib_bot to Bots using this library
2022-06-01 19:14:59 +03:00
5a60846c7f add hydrolib_bot to Bots using this library 2022-06-01 19:07:54 +03:00
d2c1615392 Merge pull request #1573 from tusharhero/master
Added my bot made using pyTelegramBotAPI
2022-06-01 10:56:01 +03:00
7cdb48c3e0 brought my bot ot end 2022-06-01 10:22:22 +05:30
28ae0867f8 Added my bot made using pyTelegramBotAPI
source code available here 
https://github.com/tusharhero/bincode-telegram-bot
2022-05-31 22:58:03 +05:30
e84cc0e0af Merge pull request #1571 from TECH-SAVVY-GUY/patch-1
Added `WebApp` functions
2022-05-30 15:25:05 +03:00
42ce47914d Fix typo in WebApp() functions 2022-05-30 17:21:33 +05:30
4401780ba9 Update WebApp() functions
Adjusted function name case to pythonic style.
2022-05-30 17:18:03 +05:30
82f056e88a Added WebApp functions
`validate_WebApp_data()` - Use to validate the data received by the WebApp from Telegram.

`parse_WebApp_data()` - Use to parse the data sent to the WebApp from the bot's backend.
2022-05-30 14:52:49 +05:30
6b19d631d1 Merge pull request #1565 from DevAdvik/master
Update create_invite_link.py
2022-05-23 14:16:42 +03:00
ee7adb00df Re-fix inlinekeyboardmarkup 2022-05-23 13:27:48 +05:30
82a8aa65f0 Merge pull request #1566 from coder2020official/master
More description for class-based middlewares in readme.
2022-05-22 11:21:49 +03:00
72aaf44dc7 Update README.md 2022-05-22 01:50:38 +05:00
1d0efce76e Update create_invite_link.py
Fix
2022-05-22 02:17:21 +05:30
74d0604c05 Update create_invite_link.py
Bug fix (Real FINAL FIX!!)
2022-05-22 01:54:02 +05:30
1943f659bc Merge pull request #1561 from coder2020official/bugfixes
Aiohttp client session management improvements.
2022-05-21 23:22:29 +03:00
d6ec104829 Update create_invite_link.py
Imported both inline keyboards in same line:|
2022-05-22 01:48:48 +05:30
7edaa51995 Update create_invite_link.py
Removed while loop
2022-05-22 01:43:24 +05:30
6bb47e9a44 Update create_invite_link.py
Added .types while importing inline markup keyboards (fix)
Removed threading import since message is not to be deleted
2022-05-22 01:38:32 +05:30
8da749ee05 Remove ssl 2022-05-22 01:02:55 +05:00
0c59d1187e Update requirements.txt 2022-05-22 00:11:16 +05:00
e9d1d98f03 Merge pull request #1562 from coder2020official/newfeatures
Added ability to hide a link in text
2022-05-21 16:51:51 +03:00
c63b0d6a3d Merge pull request #1560 from coder2020official/master
Changed behaviour of default parse mode in sync and async telebot.
2022-05-21 16:51:17 +03:00
388306b7fe Updated examples 2022-05-21 17:45:59 +05:00
5e3fd17436 Added ability to hide a link in text 2022-05-21 17:39:45 +05:00
ccc09ffaf3 Aiohttp client session management improvements. 2022-05-21 17:38:16 +05:00
e086303535 Merge branch 'eternnoir:master' into master 2022-05-21 17:11:14 +05:00
d6e93f85f1 Improved the comment for quick_markup 2022-05-21 17:10:57 +05:00
d954f8f5b3 Fixed default parse mode in asynctelebot 2022-05-21 17:10:45 +05:00
33375ac135 Added ability to set default parse_mode for explanation_parse_mode in send_poll 2022-05-21 17:10:29 +05:00
28662876a2 Add workspace exception in gitignore 2022-05-21 17:09:59 +05:00
0f44fd3c40 Merge pull request #1558 from DevAdvik/master
Created create_invite_link.py
2022-05-20 22:48:10 +03:00
8fefd7b5b3 Create create_invite_link.py
Added create_chat_invite_link() func example
2022-05-20 23:00:38 +05:30
7567750276 Merge pull request #1552 from m-cyx/patch-1
Small srtring formatter fix
2022-05-15 23:15:12 +03:00
f526a9d8a4 Small srtring formatter fix 2022-05-15 22:46:15 +03:00
feffe726dd Merge pull request #1551 from everpcpc/patch-1
fix: warning none_stop parameter is deprecated
2022-05-15 10:57:59 +03:00
3ff7e28467 fix: warning none_stop parameter is deprecated 2022-05-15 14:29:29 +08:00
5d74e18d1a Merge pull request #1550 from Badiboy/master
Poll type parameter parse fix
2022-05-15 01:06:04 +03:00
91b665ea94 Poll type parameter parse fix
Plus some typo
2022-05-15 00:59:35 +03:00
6e4c63b09c Merge pull request #1549 from coder2020official/master
Set escape=True by default.
2022-05-14 19:27:44 +03:00
42efb8488c Set escape=True by default. 2022-05-14 20:32:28 +05:00
03a567ea93 Merge pull request #1548 from AHOHNMYC/copy-message-fix
CopyMessage return type fix
2022-05-14 16:30:29 +03:00
3f28bb6e9d CopyMessage return type fix [async] 2022-05-14 15:48:27 +03:00
e051dda113 CopyMessage return type fix 2022-05-14 15:46:05 +03:00
856debe7d2 Merge pull request #1542 from abdullaev388/master
Added examples of multibot
2022-05-13 15:11:39 +03:00
42955d1886 added examples of multibot 2022-05-11 10:50:33 +05:00
a2f3cd03e1 Merge pull request #1540 from Badiboy/master
class File parse fix
2022-05-08 23:39:00 +03:00
7e68721475 class File parse fix 2022-05-08 23:34:56 +03:00
7965ba4f69 Merge pull request #1538 from coder2020official/master
Fix typo in readme
2022-05-08 18:45:32 +03:00
aba4d3e246 Merge pull request #1536 from atif5/master
Updated the "bots using this api" section in README
2022-05-08 18:45:07 +03:00
caae6bb93f Update README.md 2022-05-08 18:13:07 +05:00
096d7a4eea Update README.md 2022-05-08 00:02:40 +03:00
ab4140ba9f Update README.md 2022-05-08 00:01:45 +03:00
77c3587012 Update README.md 2022-05-08 00:00:46 +03:00
efb1b44e59 Merge pull request #1535 from Badiboy/master
Bump version to 4.5.1
2022-05-07 22:41:19 +03:00
2c8793b794 Bump version to 4.5.1 2022-05-07 22:40:26 +03:00
146fd57b10 Merge pull request #1531 from coder2020official/patch-3
Random unused import deleted
2022-05-05 09:26:57 +03:00
8a12ae3565 Update redis_storage.py 2022-05-04 19:55:43 +05:00
2d8c2312e3 Merge pull request #1529 from coder2020official/state-fixes
Allow only state objects
2022-05-03 13:02:24 +03:00
f9cd0d7e08 Avoid circular import 2022-05-02 14:45:43 +05:00
59cd1a00e7 Merge pull request #1528 from coder2020official/master
Seo improvements for documentation.
2022-05-02 00:30:55 +03:00
836130a718 Allow only state objects 2022-05-02 02:08:48 +05:00
a7db2d8d9c Merge branch 'eternnoir:master' into master 2022-05-01 23:20:02 +05:00
c022d49996 SEO improvements for documentation 2022-05-01 23:19:46 +05:00
825827cb1e Merge pull request #1527 from coder2020official/proxy-fix
Fixed proxy for asynctelebot
2022-05-01 17:26:54 +03:00
cd92b70d6b Update README.md 2022-05-01 19:13:49 +05:00
617c990994 Merge pull request #1526 from coder2020official/master
Key for custom filters
2022-05-01 17:01:37 +03:00
9b959373db Fixed proxy for asynctelebot 2022-05-01 18:43:07 +05:00
76c0197ab7 Key for custom filters 2022-05-01 16:11:00 +05:00
7d9658b062 Merge pull request #1525 from Badiboy/master
Polling exception logging updated
2022-05-01 14:00:50 +03:00
db0c946780 Polling exception logging updated
Polling exception logging arranged with infinity_polling mode
2022-05-01 00:17:14 +03:00
c6202da36f Merge pull request #1524 from coder2020official/master
Markdown & Html functions added(Beta version, still in progress)
2022-04-30 23:43:53 +03:00
532011138c Added examples for formatting 2022-05-01 00:58:06 +05:00
191164cba0 Fix traceback 2022-05-01 00:45:34 +05:00
5688aaa03b Markdown & Html functions added(Beta version, still in progress) 2022-05-01 00:28:00 +05:00
88a76c0a15 Merge pull request #1521 from Badiboy/master
Mistake in ChatAdministratorRights
2022-04-24 23:42:51 +03:00
e1dc6d7beb Mistake in ChatAdministratorRights 2022-04-24 23:41:08 +03:00
730d11012d Merge pull request #1517 from Badiboy/master
Bugfix in answer_web_app_query
2022-04-24 11:40:42 +03:00
b43b636ba0 Bugfix in answer_web_app_query 2022-04-24 11:33:19 +03:00
a7ca6c057e Merge pull request #1516 from Badiboy/master
i18n middleware - file revert
2022-04-24 11:29:47 +03:00
bd002c6429 i18n middleware - file revert 2022-04-24 11:28:20 +03:00
453df01f26 Merge pull request #1511 from abdullaev388/master
Added sync i18n class based middleware
2022-04-24 11:27:12 +03:00
24ae38cca6 added function based middleware i18n example 2022-04-24 12:51:01 +05:00
3b386965ea sync middleware examples separated into two folders 2022-04-24 11:52:01 +05:00
5077289d0d Merge pull request #1513 from Badiboy/master
typo
2022-04-23 20:20:17 +03:00
a54b21cb50 type 2022-04-23 20:19:25 +03:00
fa80cb0002 Merge pull request #1512 from Badiboy/master
Bot API 6.0. Deprecation fixes
2022-04-23 19:37:01 +03:00
ad5b92b650 Remove incorrect deprecation 2022-04-23 19:35:38 +03:00
9b1b324ab4 Bump version to 4.5.0 2022-04-23 16:33:59 +03:00
e444bc2a0b Payment example fix 2022-04-23 16:27:53 +03:00
dd25432359 Bot API 6.0. Deprecation fixes 2022-04-23 15:03:54 +03:00
cfbbfe84ad Merge pull request #1501 from coder2020official/master
Prepare for Bot API 6.0
2022-04-23 14:10:26 +03:00
b25d2846e9 TextFilter class supports case insensitiveness with lazy translations 2022-04-23 11:53:55 +05:00
ab64e17464 added new i18n class based middleware 2022-04-23 11:48:58 +05:00
3a5db47c1b removed old class based i18n middleware 2022-04-23 10:44:35 +05:00
4812dcb02b Fix typo in types.py 2022-04-22 23:06:11 +05:00
b146df346d Indentation fix to fit documentation. 2022-04-17 16:46:38 +05:00
5f2713bcfb Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-04-17 16:39:14 +05:00
a1bf961fd2 Bump Bot API 6.0(Beta) 2022-04-17 16:39:09 +05:00
a8451a5e30 Merge branch 'eternnoir:master' into master 2022-04-17 14:07:44 +05:00
9417c49d8e Merge pull request #1502 from Badiboy/master
Bump version to 4.4.1
2022-04-16 23:22:30 +03:00
1a40f1da7a Bump version to 4.4.1 2022-04-16 23:21:25 +03:00
5e28f27764 Bump version to 4.4.1 2022-04-16 23:17:19 +03:00
43d0e10ba4 Merge branch 'eternnoir:master' into master 2022-04-16 23:52:41 +05:00
9652fdbecb Update README.md 2022-04-16 23:50:45 +05:00
110575ca40 Merge pull request #1500 from coder2020official/master
Road to release(1st part)
2022-04-15 22:49:02 +03:00
22b4e636e2 Road to release(1st part) 2022-04-16 00:13:14 +05:00
1688a466f4 Merge pull request #1492 from nj-vs-vh/master
Issue #1491: missing await inserted
2022-04-01 18:54:37 +03:00
625da4cdd9 missing await inserted 2022-03-27 18:32:05 +04:00
a9e0f5b7b0 Merge pull request #1484 from coder2020official/master
Regular documentation update.
2022-03-21 01:36:15 +03:00
7b1b1a7caa Update PULL_REQUEST_TEMPLATE.md 2022-03-19 14:06:07 +05:00
a6477541c0 Documentation incorrect display is fixed now. 2022-03-19 13:49:36 +05:00
b652a9f6dc Update doc_req.txt 2022-03-19 13:26:09 +05:00
73b2813512 Update doc_req.txt 2022-03-10 20:22:42 +05:00
ace28983b6 Merge pull request #1475 from coder2020official/master
Fix
2022-03-08 22:53:55 +03:00
e82675320c Merge pull request #1474 from WuerfelDev/patch-1
Add TranslateThisVideoBot to the bot list
2022-03-08 22:53:13 +03:00
1e88671799 Merge branch 'eternnoir:master' into master 2022-03-08 12:07:09 +05:00
1cdf9640d7 Fix example on chat_member, fix middleware-exception for async 2022-03-08 12:07:00 +05:00
91badb53e5 Merge pull request #1473 from coder2020official/master
Docstrings of sync telebot updated to fit documentation. Basic middleware classes added.
2022-03-07 20:00:55 +03:00
477d02468d Fixed middlewares 2022-03-07 21:40:39 +05:00
9f3a270fae Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-03-07 21:13:49 +05:00
244b058648 Fix 2022-03-07 21:13:30 +05:00
886c9b9bc0 Update README.md 2022-03-07 20:38:22 +05:00
41025ba97b Update setup_python.yml 2022-03-07 20:26:11 +05:00
4875bb6188 Update requirements.txt 2022-03-07 19:04:12 +05:00
5f91c3d4e6 Added python 3.10 to tests 2022-03-07 18:56:16 +05:00
7b62915a5b Create PULL_REQUEST_TEMPLATE.md 2022-03-07 18:54:59 +05:00
05c3cb2c1d Update doc_req.txt 2022-03-07 18:08:41 +05:00
60a96d1400 Update README.md 2022-03-07 17:42:47 +05:00
dcd0df93da Update README.md 2022-03-07 17:39:42 +05:00
f15101fc6f Update doc_req.txt 2022-03-07 17:32:42 +05:00
5f03253398 Fix comments 2022-03-07 17:31:02 +05:00
8fab55e937 Create antiflood_middleware.py 2022-03-07 17:30:10 +05:00
60a23665cb Update flooding_middleware.py 2022-03-07 17:23:55 +05:00
b292b275cb Create basic_example.py 2022-03-07 17:21:59 +05:00
403028bf35 Update README.md 2022-03-07 17:14:51 +05:00
3dda5cff06 Create README.md 2022-03-07 17:14:25 +05:00
dd589e2490 Updated documentation to another theme. 2022-03-07 16:10:44 +05:00
c8fb83c97c Fix documentation 2022-03-07 14:24:28 +05:00
854f6a9506 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-03-07 13:30:41 +05:00
be0557c2b5 Multiple middlewares allowed for async 2022-03-07 13:30:39 +05:00
436422e4da TranslateThisVideo Bot 2022-03-06 22:29:57 +01:00
fc72576aaa Update README.md 2022-03-07 00:39:25 +05:00
f69a2ba044 Update docstrings for asynctelebot. 2022-03-07 00:18:11 +05:00
c45e06c694 Updated description for TeleBot class 2022-03-06 23:23:33 +05:00
78bdf1ca4e Update docstrings 2022-03-06 23:14:07 +05:00
3c7d3c0196 Fix tests 2022-03-06 19:52:42 +05:00
441a5793cc Update docstrings to correct documentation. 2022-03-06 19:41:54 +05:00
388477686b Added middlewares.
Bumped middlewares
2022-03-06 18:39:41 +05:00
4f654d9e12 Add TranslateThisBot to the bot list 2022-03-06 04:26:40 +01:00
ac12d0fc02 Merge pull request #1467 from coder2020official/master
CallbackQuery usage with states.
2022-03-02 11:56:13 +03:00
b8ebe4fd58 Merge pull request #1469 from abdullaev388/master
argument of 'func' parameter in handlers can be async function
2022-03-01 18:11:20 +03:00
c84896391e argument of 'func' parameter in handlers can be async function 2022-03-01 14:39:00 +05:00
995e28e9d8 Remove unnecessary thing 2022-02-26 22:50:55 +05:00
1bfc082d46 Update documentation 2022-02-26 22:48:03 +05:00
1a35bbb127 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-02-26 22:43:05 +05:00
e585c77830 Fix 2022-02-26 22:43:03 +05:00
5ca92ff637 Merge pull request #1465 from coder2020official/master
Added isinstance checkups in state filters
2022-02-26 13:07:53 +03:00
f4c76553ed Update asyncio_filters.py 2022-02-25 19:53:17 +05:00
75baf6dd96 Update custom_filters.py 2022-02-25 19:52:56 +05:00
301b9288a4 Merge branch 'eternnoir:master' into master 2022-02-25 19:47:50 +05:00
70b9fc86d2 Update custom_filters.py 2022-02-25 19:46:49 +05:00
dde9cd323c Update asyncio_filters.py 2022-02-25 19:45:52 +05:00
01a6827542 Merge pull request #1462 from coder2020official/master
Allow using non-class states
2022-02-23 11:32:01 +03:00
b960a9e574 Update custom_filters.py 2022-02-23 13:08:02 +05:00
102fe3a8fb Update asyncio_filters.py 2022-02-23 13:07:25 +05:00
292df419ba Merge pull request #1456 from abdullaev388/master
I18N class for sync telebot and middleware for async
2022-02-22 22:37:13 +03:00
7993e1d1c9 corrected setup middleware in async i18n middleware example 2022-02-21 20:08:03 +05:00
7309f92c36 Merge pull request #1459 from coder2020official/master
Fixed documentation and added link
2022-02-19 22:48:37 +03:00
7875ff293d Update README.md 2022-02-20 00:44:08 +05:00
4adac4d852 Update quick_start.rst 2022-02-20 00:40:25 +05:00
38bff65caf removed unused imports from util.py 2022-02-20 00:28:27 +05:00
9ecadf1bc1 Merge pull request #1458 from coder2020official/master
Bump documentation
2022-02-19 22:19:00 +03:00
5d7ae385ec token removed. 2022-02-20 00:12:14 +05:00
74e9780b30 BaseMiddleware returned to it's original place && I18N middleware is now only in examples 2022-02-20 00:08:14 +05:00
9b20f41ece I18N class removed from telebot.util.py 2022-02-19 23:57:21 +05:00
967309120e Merge pull request #1457 from Badiboy/master
Fix check of the regexp and commands types
2022-02-19 21:41:55 +03:00
94be2abdbd Typo 2022-02-19 21:39:52 +03:00
6c31b53cd9 Fix check of the regexp and commands types 2022-02-19 21:39:02 +03:00
9bfc0b2c6f preventet breaking change 2022-02-19 23:37:03 +05:00
fc374ec57a Merge pull request #1454 from Troshchk/message_handler_checking
Additional check of the regexp and commands types
2022-02-19 21:33:30 +03:00
7a8e60ddc2 Update index.rst 2022-02-19 22:41:09 +05:00
7f43f26886 Add telebot 2022-02-19 22:21:15 +05:00
4521982837 Create .readthedocs.yml 2022-02-19 22:17:29 +05:00
30c43b557c Documentation Bump 2022-02-19 21:56:51 +05:00
10b5886dcc Completed I18N examples descriptions 2022-02-19 18:56:27 +05:00
93b97fc3fe I18N middleware example was added 2022-02-19 18:47:36 +05:00
1f6e60fd74 I18N middleware implementation was added 2022-02-19 16:25:46 +05:00
5337d4838d asyncio_middlewares.py was created && BaseMiddleware class was replaced to asyncio_middlewares.py 2022-02-19 16:02:14 +05:00
ae5d183db0 slight TextFilter class improvement 2022-02-19 15:53:58 +05:00
0d85a34551 an example for i18n class was added 2022-02-19 15:07:46 +05:00
002c608d45 i18n class was added 2022-02-19 15:04:31 +05:00
ec766a3e43 Wrapping checking in private methods; warnings changed to errors 2022-02-16 14:05:54 +01:00
0ef8d04ed2 Merge pull request #1449 from abdullaev388/master
new advanced TextFilter was added && An example demostrating TextFilt…
2022-02-16 12:48:29 +03:00
3a86916e72 example of TextFilter starts_with and ends_with usage simultaneously 2022-02-16 12:43:23 +05:00
b41435f407 more descriptive exceptions 2022-02-16 12:29:27 +05:00
f689d90815 Merge pull request #1455 from Badiboy/master
Fix timer_bot.py
2022-02-15 19:55:32 +03:00
966f2e7ef7 Fix timer_bot.py 2022-02-15 19:55:12 +03:00
9075430210 Making first condition shorter, no change in functionality 2022-02-15 15:46:02 +01:00
68095ad69a Adding checks for the commands and regexp input types 2022-02-15 15:24:55 +01:00
8c3d1e608c new TextFilter examples were added 2022-02-12 21:53:40 +05:00
6822f18cbb multiple check patterns && multiple startwith, endswith fields 2022-02-12 21:41:10 +05:00
6e4f2e19d6 async text contains filter was fixed 2022-02-12 20:36:10 +05:00
8bbd062d13 text contains filter was fixed 2022-02-12 20:31:02 +05:00
5f7ccc8c9b created async TextFilter 2022-02-12 17:33:29 +05:00
5b1483f646 removed TextFilterKey in example, instead TextMatchFilter was modified 2022-02-12 17:07:59 +05:00
3cd86d0e93 token. again. 2022-02-12 15:31:54 +05:00
a893fbc358 async advanced callback_data example was added 2022-02-12 15:30:04 +05:00
6fd2a38fe9 An asyncio example demostrating TextFilter usage 2022-02-12 15:12:30 +05:00
b89ecb3e5a modified code 2022-02-12 14:32:59 +05:00
2e5590b566 token removed :) 2022-02-12 14:11:16 +05:00
733bb2ebbb new advanced TextFilter was added && An example demostrating TextFilter usage 2022-02-12 13:35:52 +05:00
64a22457e2 Merge pull request #1446 from abdullaev388/master
New CallbackData example
2022-02-10 12:26:12 +03:00
0c8e94d2c6 advanced usage of callbackdata was added 2022-02-10 13:43:19 +05:00
b9436821e0 callbackdata examples were separated into a directory 2022-02-10 13:33:44 +05:00
a8af9120de Merge pull request #1444 from skelly37/patch-1
Update README.md
2022-02-09 09:51:24 +03:00
0655a1f6b6 Update README.md
added my pyfram-telegram-bot
2022-02-09 00:27:18 +00:00
97dbedaa54 Fix > 2022-02-07 00:57:33 +03:00
4028b44d07 Update README.md 2022-02-06 21:02:14 +03:00
661218c7e3 Merge pull request #1434 from coder2020official/master
Fix States
2022-02-02 13:57:08 +03:00
cd4a9add68 Fix States 2022-02-02 14:44:02 +04:00
7d2915c7f9 Merge pull request #1433 from Badiboy/master
Extend custom exception_handler behaviour
2022-02-02 10:40:58 +03:00
ce56a035b5 Extend custom exception_handler behaviour 2022-02-01 23:58:57 +03:00
9fa79aabc0 Merge pull request #1432 from coder2020official/master
Bot API 5.7
2022-02-01 17:31:35 +03:00
62fad9ca3a Fix tests 2022-02-01 18:16:53 +04:00
388f055643 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-02-01 17:43:52 +04:00
71be20636a Bot API 5.7 2022-02-01 17:43:49 +04:00
3b38d1b46e Bot API 5.7 in readme 2022-02-01 14:51:36 +04:00
1e0c2ea633 Update README.md 2022-02-01 14:50:36 +04:00
4e7652be7a Bot API 5.7 2022-02-01 14:47:42 +04:00
723075d2da Merge pull request #1430 from barbax7/wrong_setup
Installing storage and asyncio_storage
2022-02-01 09:33:22 +03:00
7ba021871a Adding new way to install library 2022-01-31 23:09:18 +01:00
d7cb819502 Excluding tests and examples from packages to install 2022-01-31 22:58:52 +01:00
5ee2aa77c6 storage and asyncio_storage were not installed with previews setup function 2022-01-31 22:53:31 +01:00
80cf5d8d5b Merge pull request #1427 from barbax7/user
The output of get_me() is already an User object
2022-01-30 19:56:19 +03:00
69277400b7 The output of get_me() is already an User object 2022-01-30 17:53:55 +01:00
8d380b4913 Merge pull request #1423 from coder2020official/master
Code template
2022-01-25 14:42:29 +03:00
23d20e0753 Update README.md 2022-01-25 15:21:09 +04:00
6fc7beba57 Update README.md 2022-01-25 15:18:12 +04:00
8d49d22074 Merge pull request #1422 from Badiboy/master
Code base cleanup
2022-01-25 10:27:10 +03:00
6aa97d055f Bump version to 4.4.0 2022-01-25 10:25:53 +03:00
e55938e23a Keep python 3.6 check 2022-01-25 10:24:45 +03:00
4166fb229e Code base cleanup 2022-01-24 22:38:35 +03:00
2e9947277a Merge pull request #1421 from coder2020official/master
RedisStorage, middleware fix, pass_bot parameter and more
2022-01-24 21:25:16 +03:00
c350ea0ced Comment fixes 2022-01-24 21:34:50 +04:00
588b5c4d89 Fix parameter for example 2022-01-24 21:28:56 +04:00
91d0877c61 Fix parameter name to fit 2022-01-24 21:28:10 +04:00
8045ad56ea States Update 2022-01-24 21:24:56 +04:00
124b07ee44 Create __init__.py 2022-01-24 19:08:34 +04:00
195974ddc1 Fix 2022-01-24 18:33:59 +04:00
2b081b42bb Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-01-24 18:31:44 +04:00
321d241483 Delete types.py 2022-01-24 17:23:40 +04:00
ad4ff5835e Merge branch 'eternnoir:master' into master 2022-01-24 17:15:35 +04:00
a3cda2e0ff Updated sync and async. Fixes and new features. 2022-01-24 17:15:04 +04:00
cf2eb1fec7 Merge pull request #1418 from artyl/master
add default None for get_my_commands, examples for bot.set_my_commands
2022-01-21 22:48:11 +03:00
7eb759d1fd remove unused import 2022-01-21 22:25:06 +03:00
a07bf86c30 add default None for get_my_commands parameters scope and language_code sync\async, add examples for bot.set_my_commands 2022-01-21 21:50:33 +03:00
64c4aca3b7 Merge pull request #1417 from artyl/master
Add timer_bot sync and async example
2022-01-21 14:47:59 +03:00
40465643b9 Add timer_bot sync and async example 2022-01-21 12:32:23 +03:00
56fbf491bc Merge pull request #1411 from studentenherz/master
Removed redundant logger configuration in async_telebot
2022-01-12 10:26:35 +03:00
685c071056 Removed redundant logger configuration in async_telebot that made logs repeated twice 2022-01-11 19:24:16 -03:00
fdbc0e6a61 Merge pull request #1410 from barbax7/patch-1
Correct test for antiflood function
2022-01-10 18:36:15 +03:00
7fe8d27686 Correct test for antiflood function 2022-01-10 16:19:21 +01:00
9050f4af1f Merge pull request #1409 from Badiboy/master
Bugfix in send_data
2022-01-10 16:51:44 +03:00
9140044956 Tests ant type hint fix 2022-01-10 16:49:49 +03:00
2e6b6bda53 Additional bugfix
Additional bugfix
Plus protected methods removal
2022-01-10 16:40:33 +03:00
8d8aa5a380 Bugfix in send_data
Bugfix in send_data
Protected methods renamed
Some hints added
2022-01-10 14:38:28 +03:00
ae2dbd00fa Merge pull request #1405 from Badiboy/master
Bump version to 4.3.0
2022-01-08 20:03:30 +03:00
6550a5d745 Bump version to 4.3.0 2022-01-08 20:02:54 +03:00
593b27358b Merge pull request #1404 from coder2020official/master
Create webhook_fastapi_echo_bot.py
2022-01-03 23:01:37 +03:00
a51ff0f100 Fix readme typos 2022-01-03 16:30:49 +04:00
a96bc802bc Update README.md 2022-01-03 16:28:24 +04:00
df8f34e726 Create webhook_fastapi_echo_bot.py 2022-01-03 16:16:19 +04:00
00998ac9c8 Merge pull request #1402 from coder2020official/master
Push bot API to 5.6
2022-01-02 23:26:01 +03:00
3f243c64ca Fix data-typo 2022-01-02 22:09:09 +04:00
034241ba31 Fix commit 2022-01-02 14:58:15 +04:00
ed6fb57cb5 Added protect_content parameter to all methods 2022-01-02 14:53:47 +04:00
b71507387f Added spoiler 2022-01-02 14:12:15 +04:00
e7d0ec1f6c Fix asyncio_helper.py 2021-12-31 19:25:29 +04:00
b3b318fd28 Delete asyncio_types.py 2021-12-31 15:14:42 +04:00
d334f5cb8d Added protect_content parameter. Remade some methods. 2021-12-31 15:05:40 +04:00
7f06424980 Update README.md 2021-12-31 13:20:54 +04:00
7490aa0d26 Merge pull request #1400 from Badiboy/master
send_document param type fix
2021-12-25 16:26:50 +03:00
e59e2ee2ee send_document param type fix 2021-12-25 16:23:26 +03:00
f25dcad10c Merge pull request #1399 from coder2020official/master
_make_request edited
2021-12-25 15:32:51 +03:00
24a9491ec0 _make_request function edited 2021-12-25 16:04:29 +04:00
744549defe Merge pull request #1397 from EnriqueMoran/master
Added bot using pyTelegramBotAPI to readme
2021-12-23 23:38:03 +03:00
c86fc4c3fa Added bot to readme 2021-12-23 12:31:01 -08:00
943396767c Merge pull request #1392 from studentenherz/master
Added bot using pyTelegramBotAPI
2021-12-13 10:07:39 +03:00
13fffe58a1 Added bot using pyTelegramBotAPI 2021-12-12 23:23:52 -03:00
7fe60e19ef Merge pull request #1390 from coder2020official/master
Update README.md
2021-12-12 14:11:51 +03:00
ba9bf17f46 Update README.md 2021-12-12 15:54:06 +05:00
373ee4b45b Merge pull request #1389 from coder2020official/master
Polling is now asynchronous, and some readme fixes.
2021-12-12 13:28:57 +03:00
e5f0ba67fc Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-12-12 15:13:10 +05:00
096d58ae71 Update admin_filter_example.py 2021-12-12 15:13:07 +05:00
ed5b47cb96 Update README.md 2021-12-12 15:11:42 +05:00
e92946301f Asyncio.run back 2021-12-12 15:07:30 +05:00
7588c9fb9f infinity_polling 2021-12-10 21:32:57 +03:00
6d10bfefbd Merge pull request #1386 from zeph1997/master
Create a template for QWERTY custom keyboard an example for ReplyKeyboardMarkup
2021-12-10 21:32:19 +03:00
cebfbb83fa Merge branch 'master' of https://github.com/zeph1997/pyTelegramBotAPI 2021-12-11 02:28:41 +08:00
5a06d8021b changed markup.add row from hardcoded index to *row 2021-12-11 02:28:19 +08:00
a75841aa8e Merge branch 'eternnoir:master' into master 2021-12-11 02:11:44 +08:00
e76649bb49 Create reply_keyboard_markup_example.py
Example to show how to use ReplyKeyboardMarkup as well as a template for a QWERTY keyboard
2021-12-11 02:11:15 +08:00
7567c6cd71 Merge pull request #1384 from Badiboy/master
Bump version to 4.2.2
2021-12-08 23:45:27 +03:00
751deeafd7 Bump version to 4.2.2 2021-12-08 23:44:57 +03:00
3ebefa15bf Merge pull request #1383 from coder2020official/master
Bot API 5.5
2021-12-08 13:19:56 +03:00
bb19687854 fix 2021-12-08 15:15:57 +05:00
311eec6888 fix 2021-12-08 14:15:40 +05:00
08fc32b70a Comment fix 2021-12-08 14:13:39 +05:00
555257a3fe Documentation Bug fixed 2021-12-08 14:00:39 +05:00
5a03ab62d0 Update test_types.py 2021-12-07 22:27:19 +05:00
038be81db3 5.5 2021-12-07 22:17:51 +05:00
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
130 changed files with 22247 additions and 1967 deletions

14
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,14 @@
## Description
Include changes, new features and etc:
## Describe your tests
How did you test your change?
Python version:
OS:
## Checklist:
- [ ] I added/edited example on new feature/change (if exists)
- [ ] My changes won't break backward compatibility
- [ ] I made changes both for sync and async

View File

@ -20,7 +20,7 @@ jobs:
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
python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9']
name: ${{ matrix.python-version }} and tests
steps:
- uses: actions/checkout@v2

4
.gitignore vendored
View File

@ -64,3 +64,7 @@ testMain.py
#VS Code
.vscode/
.DS_Store
*.code-workspace
# documentation
_build/

19
.readthedocs.yml Normal file
View File

@ -0,0 +1,19 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
# Optionally build your docs in additional formats such as PDF and ePub
formats: all
# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
install:
- requirements: doc_req.txt

View File

@ -1,9 +1,9 @@
language: python
python:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "pypy3"
install: "pip install -r requirements.txt"
script:

190
README.md
View File

@ -1,15 +1,20 @@
[![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)
[![Documentation Status](https://readthedocs.org/projects/pytba/badge/?version=latest)](https://pytba.readthedocs.io/en/latest/?badge=latest)
[![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/)
[![PyPi status](https://img.shields.io/pypi/status/pytelegrambotapi.svg?style=flat-square)](https://pypi.python.org/pypi/pytelegrambotapi)
# <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 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">Both synchronous and asynchronous.</p>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#november-5-2021">5.4</a>!
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#june-20-2022">6.1</a>!
<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
## Contents
* [Getting started](#getting-started)
@ -43,7 +48,7 @@
* [Reply markup](#reply-markup)
* [Advanced use of the API](#advanced-use-of-the-api)
* [Using local Bot API Server](#using-local-bot-api-sever)
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
* [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)
@ -52,19 +57,22 @@
* [Proxy](#proxy)
* [Testing](#testing)
* [API conformance](#api-conformance)
* [AsyncTeleBot](#asynctelebot)
* [F.A.Q.](#faq)
* [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)
* [Telegram Channel](#telegram-channel)
* [More examples](#more-examples)
* [Bots using this API](#bots-using-this-api)
* [Code Template](#code-template)
* [Bots using this library](#bots-using-this-library)
## Getting started
This API is tested with 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)*:
* Installation using pip (a Python package manager):
```
$ pip install pyTelegramBotAPI
@ -76,10 +84,17 @@ $ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
$ cd pyTelegramBotAPI
$ python setup.py install
```
or:
```
$ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git
```
It is generally recommended to use the first option.
**While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling `pip install pytelegrambotapi --upgrade`*
*While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling*
```
pip install pytelegrambotapi --upgrade
```
## Writing your first bot
@ -182,8 +197,8 @@ 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.|
|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`
|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:
@ -341,7 +356,27 @@ def start(message):
assert message.another_text == message.text + ':changed'
```
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
#### Class-based middlewares
There are class-based middlewares.
Basic class-based middleware looks like this:
```python
class Middleware(BaseMiddleware):
def __init__(self):
self.update_types = ['message']
def pre_process(self, message, data):
data['foo'] = 'Hello' # just for example
# we edited the data. now, this data is passed to handler.
# return SkipHandler() -> this will skip handler
# return CancelUpdate() -> this will cancel update
def post_process(self, message, data, exception=None):
print(data['foo'])
if exception: # check for exception
print(exception)
```
Class-based middleware should have to functions: post and pre process.
So, as you can see, class-based middlewares work before and after handler execution.
For more, check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based)
#### Custom filters
Also, you can use built-in custom filters. Or, you can create your own filter.
@ -363,7 +398,7 @@ 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'
key='is_chat_admin'
@staticmethod
def check(message: telebot.types.Message):
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
@ -372,10 +407,10 @@ class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
bot.add_custom_filter(IsAdmin())
# Now, you can use it in handler.
@bot.message_handler(is_admin=True)
@bot.message_handler(is_chat_admin=True)
def admin_of_group(message):
bot.send_message(message.chat.id, 'You are admin of this group'!)
bot.send_message(message.chat.id, 'You are admin of this group!')
```
@ -555,26 +590,26 @@ apihelper.API_URL = "http://localhost:4200/bot{0}/{1}"
*Note: 4200 is an example port*
### 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 your bot __significantly__, but it has unwanted side effects if used without caution.
### 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:
@ -639,6 +674,8 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
```
### Proxy
For sync:
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
```python
@ -653,6 +690,14 @@ If you want to use socket5 proxy you need install dependency `pip install reques
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
For async:
```python
from telebot import asyncio_helper
asyncio_helper.proxy = 'http://127.0.0.1:3128' #url
```
### Testing
You can disable or change the interaction with real Telegram server by using
```python
@ -682,9 +727,13 @@ Result will be:
## API conformance
* ✔ [Bot API 6.1](https://core.telegram.org/bots/api#june-20-2022)
* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022)
* ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022)
* ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021)
* ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021)
* ✔ [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.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMember* 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)
@ -698,30 +747,64 @@ Result will be:
* ✔ [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 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)
* ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017)
* ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017)
* ✔ [Bot API 3.1](https://core.telegram.org/bots/api-changelog#june-30-2017)
* ✔ [Bot API 3.0](https://core.telegram.org/bots/api-changelog#may-18-2017)
* ✔ [Bot API 2.3.1](https://core.telegram.org/bots/api-changelog#december-4-2016)
* ✔ [Bot API 2.3](https://core.telegram.org/bots/api-changelog#november-21-2016)
* ✔ [Bot API 2.2](https://core.telegram.org/bots/api-changelog#october-3-2016)
* ✔ [Bot API 2.1](https://core.telegram.org/bots/api-changelog#may-22-2016)
* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016)
## 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.
### EchoBot
Echo Bot example on AsyncTeleBot:
```python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
from telebot.async_telebot import AsyncTeleBot
import asyncio
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)
asyncio.run(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 is asynchronous. It uses aiohttp instead of requests module.
### Examples
See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder
## F.A.Q.
### How can I distinguish a User and a GroupChat in message.chat?
Telegram Bot API support new type Chat for message.chat.
- Check the ```type``` attribute in ```Chat``` object:
-
```python
if message.chat.type == "private":
# private chat message
# private chat message
if message.chat.type == "group":
# group chat message
@ -743,14 +826,26 @@ 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)
## Telegram Channel
Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releases and updates.
## More examples
* [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py)
* [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py)
* [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
## Bots using this API
## Code Template
Template is a ready folder that contains architecture of basic project.
Here are some examples of template:
* [AsyncTeleBot template](https://github.com/coder2020official/asynctelebot_template)
* [TeleBot template](https://github.com/coder2020official/telebot_template)
## Bots using this library
* [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*
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you.
@ -795,7 +890,16 @@ Get help. Discuss. Chat.
* [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.
* [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search.
* [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram.
* [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram.
* [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English
* [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX.
* [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text.
* [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...).
* [Gugumoe-bot](http://t.me/gugumoe_bot) ([source](https://github.com/GooGuJiang/Gugumoe-bot)) by [咕谷酱](https://gmoe.cc) GuXiaoJiang is a multi-functional robot, such as OSU game information query, IP test, animation screenshot search and other functions.
* [Feedback-bot](https://github.com/coder2020official/feedbackbot) A feedback bot for user-admin communication. Made on AsyncTeleBot, using [template](https://github.com/coder2020official/asynctelebot_template).
**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.**

5
doc_req.txt Normal file
View File

@ -0,0 +1,5 @@
-r requirements.txt
furo
sphinx_copybutton
git+https://github.com/eternnoir/pyTelegramBotAPI.git

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -0,0 +1,57 @@
====================
AsyncTeleBot
====================
.. meta::
:description: Asynchronous pyTelegramBotAPI
:keywords: ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation
AsyncTeleBot methods
--------------------
.. automodule:: telebot.async_telebot
:members:
:undoc-members:
:show-inheritance:
Asyncio filters
---------------
.. automodule:: telebot.asyncio_filters
:members:
:undoc-members:
:show-inheritance:
Asynchronous storage for states
-------------------------------
.. automodule:: telebot.asyncio_storage
:members:
:undoc-members:
:show-inheritance:
Asyncio handler backends
------------------------
.. automodule:: telebot.asyncio_handler_backends
:members:
:undoc-members:
:show-inheritance:
Extensions
------------------------
.. automodule:: telebot.ext.aio.webhooks
:members:
:undoc-members:
:show-inheritance:

17
docs/source/calldata.rst Normal file
View File

@ -0,0 +1,17 @@
=====================
Callback data factory
=====================
.. meta::
:description: Callback data factory in pyTelegramBotAPI
:keywords: ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, factory
callback\_data file
-----------------------------
.. automodule:: telebot.callback_data
:members:
:undoc-members:
:show-inheritance:

70
docs/source/conf.py Normal file
View File

@ -0,0 +1,70 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'pyTelegramBotAPI Documentation'
copyright = '2022, coder2020official'
author = 'coder2020official'
# The full version, including alpha/beta/rc tags
release = '4.6.1'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autosectionlabel',
'sphinx.ext.autodoc',
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"sphinx_copybutton",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'furo'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
#html_logo = 'logo.png'
html_theme_options = {
"light_css_variables": {
"color-brand-primary": "#7C4DFF",
"color-brand-content": "#7C4DFF",
},
"light_logo": "logo.png",
"dark_logo": "logo2.png",
}

View File

@ -0,0 +1,12 @@
==================
Formatting options
==================
.. meta::
:description: Formatting options in pyTelegramBotAPI
:keywords: html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI
.. automodule:: telebot.formatting
:members:
:undoc-members:
:show-inheritance:

69
docs/source/index.rst Normal file
View File

@ -0,0 +1,69 @@
.. pyTelegramBotAPI documentation master file, created by
sphinx-quickstart on Fri Feb 18 20:58:37 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to pyTelegramBotAPI's documentation!
============================================
.. meta::
:description: Official documentation of pyTelegramBotAPI
:keywords: ptba, pytba, pyTelegramBotAPI, documentation, guide
=======
TeleBot
=======
TeleBot is synchronous and asynchronous implementation of `Telegram Bot API <https://core.telegram.org/bots/api>`_.
Chats
-----
English chat: `Private chat <https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A>`__
Russian chat: `@pytelegrambotapi_talks_ru <https://t.me/pytelegrambotapi_talks_ru>`__
News: `@pyTelegramBotAPI <https://t.me/pytelegrambotapi>`__
Pypi: `Pypi <https://pypi.org/project/pyTelegramBotAPI/>`__
Source: `Github repository <https://github.com/eternnoir/pyTelegramBotAPI>`__
Some features:
--------------
Easy to learn and use.
Easy to understand.
Both sync and async.
Examples on features.
States
And more...
Content
--------
.. toctree::
install
quick_start
types
sync_version/index
async_version/index
calldata
util
formatting
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

45
docs/source/install.rst Normal file
View File

@ -0,0 +1,45 @@
==================
Installation Guide
==================
.. meta::
:description: Installation of pyTelegramBotAPI
:keywords: ptba, pytba, pyTelegramBotAPI, installation, guide
Using PIP
----------
.. code-block:: bash
$ pip install pyTelegramBotAPI
Using pipenv
------------
.. code-block:: bash
$ pipenv install pyTelegramBotAPI
By cloning repository
---------------------
.. code-block:: bash
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
$ cd pyTelegramBotAPI
$ python setup.py install
Directly using pip
------------------
.. code-block:: bash
$ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git
It is generally recommended to use the first option.
While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling:
.. code-block:: bash
$ pip install pytelegrambotapi --upgrade

View File

@ -0,0 +1,20 @@
===========
Quick start
===========
.. meta::
:description: Quickstart guide
:keywords: ptba, pytba, pyTelegramBotAPI, quickstart, guide
Synchronous TeleBot
-------------------
.. literalinclude:: ../../examples/echo_bot.py
:language: python
Asynchronous TeleBot
--------------------
.. literalinclude:: ../../examples/asynchronous_telebot/echo_bot.py
:language: python

View File

@ -0,0 +1,49 @@
===============
TeleBot version
===============
.. meta::
:description: Synchronous pyTelegramBotAPI documentation
:keywords: ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync
TeleBot methods
---------------
.. automodule:: telebot
:members:
:undoc-members:
:show-inheritance:
custom_filters file
------------------------------
.. automodule:: telebot.custom_filters
:members:
:undoc-members:
:show-inheritance:
Synchronous storage for states
-------------------------------
.. automodule:: telebot.storage
:members:
:undoc-members:
:show-inheritance:
handler_backends file
--------------------------------
.. automodule:: telebot.handler_backends
:members:
:undoc-members:
:show-inheritance:
Extensions
------------------------
.. automodule:: telebot.ext.aio.webhooks
:members:
:undoc-members:
:show-inheritance:

10
docs/source/types.rst Normal file
View File

@ -0,0 +1,10 @@
============
Types of API
============
.. automodule:: telebot.types
:members:
:undoc-members:
:show-inheritance:

16
docs/source/util.rst Normal file
View File

@ -0,0 +1,16 @@
============
Utils
============
.. meta::
:description: Utils in pyTelegramBotAPI
:keywords: ptba, pytba, pyTelegramBotAPI, utils, guide
util file
-------------------
.. automodule:: telebot.util
:members:
:undoc-members:
:show-inheritance:

View File

@ -9,7 +9,7 @@ import telebot
from telebot import types
# Initialize bot with your token
bot = telebot.TeleBot(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 = {}
@ -47,7 +47,7 @@ def find(message: types.Message):
if message.chat.id not in users:
bot.send_message(message.chat.id, 'Finding...')
if freeid == None:
if freeid is None:
freeid = message.chat.id
else:
# Question:

View File

@ -0,0 +1,26 @@
from telebot import types
from telebot.async_telebot import AsyncTeleBot
from telebot.asyncio_filters import AdvancedCustomFilter
from telebot.callback_data import CallbackData, CallbackDataFilter
calendar_factory = CallbackData("year", "month", prefix="calendar")
calendar_zoom = CallbackData("year", prefix="calendar_zoom")
class CalendarCallbackFilter(AdvancedCustomFilter):
key = 'calendar_config'
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
class CalendarZoomCallbackFilter(AdvancedCustomFilter):
key = 'calendar_zoom_config'
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
def bind_filters(bot: AsyncTeleBot):
bot.add_custom_filter(CalendarCallbackFilter())
bot.add_custom_filter(CalendarZoomCallbackFilter())

View File

@ -0,0 +1,92 @@
import calendar
from datetime import date, timedelta
from filters import calendar_factory, calendar_zoom
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
EMTPY_FIELD = '1'
WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)]
MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)]
def generate_calendar_days(year: int, month: int):
keyboard = InlineKeyboardMarkup(row_width=7)
today = date.today()
keyboard.add(
InlineKeyboardButton(
text=date(year=year, month=month, day=1).strftime('%b %Y'),
callback_data=EMTPY_FIELD
)
)
keyboard.add(*[
InlineKeyboardButton(
text=day,
callback_data=EMTPY_FIELD
)
for day in WEEK_DAYS
])
for week in calendar.Calendar().monthdayscalendar(year=year, month=month):
week_buttons = []
for day in week:
day_name = ' '
if day == today.day and today.year == year and today.month == month:
day_name = '🔘'
elif day != 0:
day_name = str(day)
week_buttons.append(
InlineKeyboardButton(
text=day_name,
callback_data=EMTPY_FIELD
)
)
keyboard.add(*week_buttons)
previous_date = date(year=year, month=month, day=1) - timedelta(days=1)
next_date = date(year=year, month=month, day=1) + timedelta(days=31)
keyboard.add(
InlineKeyboardButton(
text='Previous month',
callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month)
),
InlineKeyboardButton(
text='Zoom out',
callback_data=calendar_zoom.new(year=year)
),
InlineKeyboardButton(
text='Next month',
callback_data=calendar_factory.new(year=next_date.year, month=next_date.month)
),
)
return keyboard
def generate_calendar_months(year: int):
keyboard = InlineKeyboardMarkup(row_width=3)
keyboard.add(
InlineKeyboardButton(
text=date(year=year, month=1, day=1).strftime('Year %Y'),
callback_data=EMTPY_FIELD
)
)
keyboard.add(*[
InlineKeyboardButton(
text=month,
callback_data=calendar_factory.new(year=year, month=month_number)
)
for month_number, month in MONTHS
])
keyboard.add(
InlineKeyboardButton(
text='Previous year',
callback_data=calendar_zoom.new(year=year - 1)
),
InlineKeyboardButton(
text='Next year',
callback_data=calendar_zoom.new(year=year + 1)
)
)
return keyboard

View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""
This Example will show you an advanced usage of CallbackData.
In this example calendar was implemented
"""
import asyncio
from datetime import date
from filters import calendar_factory, calendar_zoom, bind_filters
from keyboards import generate_calendar_days, generate_calendar_months, EMTPY_FIELD
from telebot import types
from telebot.async_telebot import AsyncTeleBot
API_TOKEN = ''
bot = AsyncTeleBot(API_TOKEN)
@bot.message_handler(commands='start')
async def start_command_handler(message: types.Message):
await bot.send_message(message.chat.id,
f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard."
"\nPress /calendar to see it.")
@bot.message_handler(commands='calendar')
async def calendar_command_handler(message: types.Message):
now = date.today()
await bot.send_message(message.chat.id, 'Calendar',
reply_markup=generate_calendar_days(year=now.year, month=now.month))
@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter())
async def calendar_action_handler(call: types.CallbackQuery):
callback_data: dict = calendar_factory.parse(callback_data=call.data)
year, month = int(callback_data['year']), int(callback_data['month'])
await bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
reply_markup=generate_calendar_days(year=year, month=month))
@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter())
async def calendar_zoom_out_handler(call: types.CallbackQuery):
callback_data: dict = calendar_zoom.parse(callback_data=call.data)
year = int(callback_data.get('year'))
await bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
reply_markup=generate_calendar_months(year=year))
@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD)
async def callback_empty_field_handler(call: types.CallbackQuery):
await bot.answer_callback_query(call.id)
if __name__ == '__main__':
bind_filters(bot)
asyncio.run(bot.infinity_polling())

View File

@ -0,0 +1,88 @@
# -*- 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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,12 @@
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)
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,34 @@
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', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended',
#'video_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)
import asyncio
asyncio.run(bot.polling(allowed_updates=util.update_types))

View File

@ -0,0 +1,14 @@
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))
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
"""
This Example will show you usage of TextFilter
In this example you will see how to use TextFilter
with (message_handler, callback_query_handler, poll_handler)
"""
import asyncio
from telebot import types
from telebot.async_telebot import AsyncTeleBot
from telebot.asyncio_filters import TextMatchFilter, TextFilter, IsReplyFilter
bot = AsyncTeleBot("")
@bot.message_handler(text=TextFilter(equals='hello'))
async def hello_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True))
async def hello_handler_ignore_case(message: types.Message):
await bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(contains=['good', 'bad']))
async def contains_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True))
async def contains_handler_ignore_case(message: types.Message):
await bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE
async def starts_with_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE
async def starts_with_handler_ignore_case(message: types.Message):
await bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay
async def ends_with_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY
async def ends_with_handler_ignore_case(message: types.Message):
await bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(equals='/callback'))
async def send_callback(message: types.Message):
keyboard = types.InlineKeyboardMarkup(
keyboard=[
[types.InlineKeyboardButton(text='callback data', callback_data='example')],
[types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')]
]
)
await bot.send_message(message.chat.id, message.text, reply_markup=keyboard)
@bot.callback_query_handler(func=None, text=TextFilter(equals='example'))
async def callback_query_handler(call: types.CallbackQuery):
await bot.answer_callback_query(call.id, call.data, show_alert=True)
@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True))
async def callback_query_handler_ignore_case(call: types.CallbackQuery):
await bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True)
@bot.message_handler(text=TextFilter(equals='/poll'))
async def send_poll(message: types.Message):
await bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night'])
await bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night'])
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?'))
async def poll_question_handler(poll: types.Poll):
print(poll.question)
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True))
async def poll_question_handler_ignore_case(poll: types.Poll):
print(poll.question + ' ignore case')
# either hi or contains one of (привет, salom)
@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True))
async def multiple_patterns_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
# starts with one of (mi, mea) for ex. minor, milk, meal, meat
@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True))
async def multiple_starts_with_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson
@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True))
async def multiple_ends_with_handler(message: types.Message):
await bot.send_message(message.chat.id, message.text)
# !ban /ban .ban !бан /бан .бан
@bot.message_handler(is_reply=True,
text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True))
async def ban_command_handler(message: types.Message):
if len(message.text) == 4 and message.chat.type != 'private':
try:
await bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id)
await bot.reply_to(message.reply_to_message, 'Banned.')
except Exception as err:
print(err.args)
return
if __name__ == '__main__':
bot.add_custom_filter(TextMatchFilter())
bot.add_custom_filter(IsReplyFilter())
asyncio.run(bot.polling())

View File

@ -0,0 +1,44 @@
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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,18 @@
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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,23 @@
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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,21 @@
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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,91 @@
from telebot import asyncio_filters
from telebot.async_telebot import AsyncTeleBot
# list of storages, you can use any storage
from telebot.asyncio_storage import StateMemoryStorage
# new feature for states.
from telebot.asyncio_handler_backends import State, StatesGroup
# default state storage is statememorystorage
bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage())
# Just create different statesgroup
class MyStates(StatesGroup):
name = State() # statesgroup should contain states
surname = State()
age = State()
# set_state -> sets a new state
# delete_state -> delets state if exists
# get_state -> returns state if exists
@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, message.chat.id)
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, message.chat.id)
@bot.message_handler(state=MyStates.name)
async def name_get(message):
"""
State 1. Will process when user's state is MyStates.name.
"""
await bot.send_message(message.chat.id, f'Now write me a surname')
await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
async with bot.retrieve_data(message.from_user.id, message.chat.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 MyStates.surname.
"""
await bot.send_message(message.chat.id, "What is your age?")
await bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
data['surname'] = message.text
# result
@bot.message_handler(state=MyStates.age, is_digit=True)
async def ready_for_answer(message):
"""
State 3. Will process when user's state is MyStates.age.
"""
async with bot.retrieve_data(message.from_user.id, message.chat.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, message.chat.id)
#incorrect number
@bot.message_handler(state=MyStates.age, is_digit=False)
async def age_incorrect(message):
"""
Will process for wrong input when state is MyState.age
"""
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())
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,21 @@
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')
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,27 @@
#!/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)
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,28 @@
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
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,54 @@
from telebot.async_telebot import AsyncTeleBot
from telebot import formatting
bot = AsyncTeleBot('token')
@bot.message_handler(commands=['start'])
async def start_message(message):
await bot.send_message(
message.chat.id,
# function which connects all strings
formatting.format_text(
formatting.mbold(message.from_user.first_name),
formatting.mitalic(message.from_user.first_name),
formatting.munderline(message.from_user.first_name),
formatting.mstrikethrough(message.from_user.first_name),
formatting.mcode(message.from_user.first_name),
separator=" " # separator separates all strings
),
parse_mode='MarkdownV2'
)
# just a bold text using markdownv2
await bot.send_message(
message.chat.id,
formatting.mbold(message.from_user.first_name),
parse_mode='MarkdownV2'
)
# html
await bot.send_message(
message.chat.id,
formatting.format_text(
formatting.hbold(message.from_user.first_name),
formatting.hitalic(message.from_user.first_name),
formatting.hunderline(message.from_user.first_name),
formatting.hstrikethrough(message.from_user.first_name),
formatting.hcode(message.from_user.first_name),
# hide_link is only for html
formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'),
separator=" "
),
parse_mode='HTML'
)
# just a bold text in html
await bot.send_message(
message.chat.id,
formatting.hbold(message.from_user.first_name),
parse_mode='HTML'
)
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,38 @@
# Just a little example of middleware handlers
from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate
from telebot.async_telebot import AsyncTeleBot
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!')
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,49 @@
#!/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'])
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,120 @@
import contextvars
import gettext
import os
from telebot.asyncio_handler_backends import BaseMiddleware
try:
from babel.support import LazyProxy
babel_imported = True
except ImportError:
babel_imported = False
class I18N(BaseMiddleware):
"""
This middleware provides high-level tool for internationalization
It is based on gettext util.
"""
context_lang = contextvars.ContextVar('language', default=None)
def __init__(self, translations_path, domain_name: str):
super().__init__()
self.update_types = self.process_update_types()
self.path = translations_path
self.domain = domain_name
self.translations = self.find_translations()
@property
def available_translations(self):
return list(self.translations)
def gettext(self, text: str, lang: str = None):
"""
Singular translations
"""
if lang is None:
lang = self.context_lang.get()
if lang not in self.translations:
return text
translator = self.translations[lang]
return translator.gettext(text)
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
"""
Plural translations
"""
if lang is None:
lang = self.context_lang.get()
if lang not in self.translations:
if n == 1:
return singular
return plural
translator = self.translations[lang]
return translator.ngettext(singular, plural, n)
def lazy_gettext(self, text: str, lang: str = None):
if not babel_imported:
raise RuntimeError('babel module is not imported. Check that you installed it.')
return LazyProxy(self.gettext, text, lang, enable_cache=False)
def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1):
if not babel_imported:
raise RuntimeError('babel module is not imported. Check that you installed it.')
return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False)
async def get_user_language(self, obj):
"""
You need to override this method and return user language
"""
raise NotImplementedError
def process_update_types(self) -> list:
"""
You need to override this method and return any update types which you want to be processed
"""
raise NotImplementedError
async def pre_process(self, message, data):
"""
context language variable will be set each time when update from 'process_update_types' comes
value is the result of 'get_user_language' method
"""
self.context_lang.set(await self.get_user_language(obj=message))
async def post_process(self, message, data, exception):
pass
def find_translations(self):
"""
Looks for translations with passed 'domain' in passed 'path'
"""
if not os.path.exists(self.path):
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
result = {}
for name in os.listdir(self.path):
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
if not os.path.isdir(translations_path):
continue
po_file = os.path.join(translations_path, self.domain + '.po')
mo_file = po_file[:-2] + 'mo'
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
with open(mo_file, 'rb') as file:
result[name] = gettext.GNUTranslations(file)
return result

View File

@ -0,0 +1,34 @@
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
def languages_keyboard():
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text="English", callback_data='en'),
InlineKeyboardButton(text="Русский", callback_data='ru'),
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
]
]
)
def clicker_keyboard(_):
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text=_("click"), callback_data='click'),
]
]
)
def menu_keyboard(_):
keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
keyboard.add(
KeyboardButton(text=_("My user id")),
KeyboardButton(text=_("My user name")),
KeyboardButton(text=_("My first name"))
)
return keyboard

View File

@ -0,0 +1,81 @@
# English translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr ""
#: keyboards.py:29
msgid "My user id"
msgstr ""
#: keyboards.py:30
msgid "My user name"
msgstr ""
#: keyboards.py:31
msgid "My first name"
msgstr ""
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
#: main.py:121
msgid "Language has been changed"
msgstr ""
#: main.py:130 main.py:150
#, fuzzy
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] ""
msgstr[1] ""
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr ""
#: main.py:203
msgid "Seems you confused language"
msgstr ""
#~ msgid ""
#~ "Hello, {user_fist_name}!\n"
#~ "This is the example of multilanguage bot.\n"
#~ "Available commands:\n"
#~ "\n"
#~ "/lang - change your language\n"
#~ "/plural - pluralization example"
#~ msgstr ""

View File

@ -0,0 +1,82 @@
# Russian translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ru\n"
"Language-Team: ru <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "Клик"
#: keyboards.py:29
msgid "My user id"
msgstr "Мой user id"
#: keyboards.py:30
msgid "My user name"
msgstr "Мой user name"
#: keyboards.py:31
msgid "My first name"
msgstr "Мой first name"
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
"Привет, {user_fist_name}!\n"
"Это пример мультиязычного бота.\n"
"Доступные команды:\n"
"\n"
"/lang - изменить язык\n"
"/plural - пример плюрализации\n"
"/menu - Пример текстового меню"
#: main.py:121
msgid "Language has been changed"
msgstr "Язык был сменён"
#: main.py:130 main.py:150
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "У вас {number} клик"
msgstr[1] "У вас {number} клика"
msgstr[2] "У вас {number} кликов"
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Это кликер.\n"
"\n"
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте."
#: main.py:203
msgid "Seems you confused language"
msgstr "Кажется, вы перепутали язык"

View File

@ -0,0 +1,80 @@
# Uzbek (Latin) translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: uz_Latn\n"
"Language-Team: uz_Latn <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "clik"
#: keyboards.py:29
msgid "My user id"
msgstr "Mani user id"
#: keyboards.py:30
msgid "My user name"
msgstr "Mani user name"
#: keyboards.py:31
msgid "My first name"
msgstr "Mani first name"
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
"Salom, {user_fist_name}!\n"
"Bu multilanguage bot misoli.\n"
"Mavjud buyruqlar:\n"
"\n"
"/lang - tilni ozgartirish\n"
"/plural - pluralizatsiya misoli\n"
"/menu - text menu misoli"
#: main.py:121
msgid "Language has been changed"
msgstr "Til ozgartirildi"
#: main.py:130 main.py:150
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "Sizda {number}ta clik"
msgstr[1] "Sizda {number}ta clik"
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Bu clicker.\n"
"\n"
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli."
#: main.py:203
msgid "Seems you confused language"
msgstr "Tilni adashtirdiz"

View File

@ -0,0 +1,214 @@
"""
In this example you will learn how to adapt your bot to different languages
Using built-in middleware I18N.
You need to install babel package 'https://pypi.org/project/Babel/'
Babel provides a command-line interface for working with message catalogs
After installing babel package you have a script called 'pybabel'
Too see all the commands open terminal and type 'pybabel --help'
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
Create a directory 'locales' where our translations will be stored
First we need to extract texts:
pybabel extract -o locales/{domain_name}.pot --input-dirs .
{domain_name}.pot - is the file where all translations are saved
The name of this file should be the same as domain which you pass to I18N class
In this example domain_name will be 'messages'
For gettext (singular texts) we use '_' alias and it works perfect
You may also you some alias for ngettext (plural texts) but you can face with a problem that
your plural texts are not being extracted
That is because by default 'pybabel extract' recognizes the following keywords:
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
To add your own keyword you can use '-k' flag
In this example for 'ngettext' i will assign double underscore alias '__'
Full command with pluralization support will look so:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
pybabel init -i locales/{domain_name}.pot -d locales -l en
pybabel init -i locales/{domain_name}.pot -d locales -l ru
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
After you translated all the texts you need to compile .po files:
pybabel compile -d locales
When you delete/update your texts you also need to update them in .po files:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
pybabel update -i locales/{domain_name}.pot -d locales
- translate
pybabel compile -d locales
If you have any exceptions check:
- you have installed babel
- translations are ready, so you just compiled it
- in the commands above you replaced {domain_name} to messages
- you are writing commands from correct path in terminal
"""
import asyncio
from typing import Union
import keyboards
from telebot import types
from telebot.async_telebot import AsyncTeleBot
from telebot.asyncio_filters import TextMatchFilter, TextFilter
from i18n_base_middleware import I18N
from telebot.asyncio_storage.memory_storage import StateMemoryStorage
class I18NMiddleware(I18N):
def process_update_types(self) -> list:
"""
Here you need to return a list of update types which you want to be processed
"""
return ['message', 'callback_query']
async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]):
"""
This method is called when new update comes (only updates which you return in 'process_update_types' method)
Returned language will be used in 'pre_process' method of parent class
Returned language will be set to context language variable.
If you need to get translation with user's actual language you don't have to pass it manually
It will be automatically passed from context language value.
However if you need some other language you can always pass it.
"""
user_id = obj.from_user.id
if user_id not in users_lang:
users_lang[user_id] = 'en'
return users_lang[user_id]
storage = StateMemoryStorage()
bot = AsyncTeleBot("", state_storage=storage)
i18n = I18NMiddleware(translations_path='locales', domain_name='messages')
_ = i18n.gettext # for singular translations
__ = i18n.ngettext # for plural translations
# These are example storages, do not use it in a production development
users_lang = {}
users_clicks = {}
@bot.message_handler(commands='start')
async def start_handler(message: types.Message):
text = _("Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example")
# remember don't use f string for interpolation, use .format method instead
text = text.format(user_fist_name=message.from_user.first_name)
await bot.send_message(message.from_user.id, text)
@bot.message_handler(commands='lang')
async def change_language_handler(message: types.Message):
await bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
reply_markup=keyboards.languages_keyboard())
@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn']))
async def language_handler(call: types.CallbackQuery):
lang = call.data
users_lang[call.from_user.id] = lang
# When you changed user language, you have to pass it manually beacause it is not changed in context
await bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
@bot.message_handler(commands='plural')
async def pluralization_handler(message: types.Message):
if not users_clicks.get(message.from_user.id):
users_clicks[message.from_user.id] = 0
clicks = users_clicks[message.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
await bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
@bot.callback_query_handler(func=None, text=TextFilter(equals='click'))
async def click_handler(call: types.CallbackQuery):
if not users_clicks.get(call.from_user.id):
users_clicks[call.from_user.id] = 1
else:
users_clicks[call.from_user.id] += 1
clicks = users_clicks[call.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
await bot.edit_message_text(text, call.from_user.id, call.message.message_id,
reply_markup=keyboards.clicker_keyboard(_))
@bot.message_handler(commands='menu')
async def menu_handler(message: types.Message):
text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.")
await bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_))
# For lazy tranlations
# lazy gettext is used when you don't know user's locale
# It can be used for example to handle text buttons in multilanguage bot
# The actual translation will be delayed until update comes and context language is set
l_ = i18n.lazy_gettext
# Handlers below will handle text according to user's language
@bot.message_handler(text=l_("My user id"))
async def return_user_id(message: types.Message):
await bot.send_message(message.chat.id, str(message.from_user.id))
@bot.message_handler(text=l_("My user name"))
async def return_user_id(message: types.Message):
username = message.from_user.username
if not username:
username = '-'
await bot.send_message(message.chat.id, username)
# You can make it case insensitive
@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True))
async def return_user_id(message: types.Message):
await bot.send_message(message.chat.id, message.from_user.first_name)
all_menu_texts = []
for language in i18n.available_translations:
for menu_text in ("My user id", "My user name", "My first name"):
all_menu_texts.append(_(menu_text, language))
# When user confused language. (handles all menu buttons texts)
@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True))
async def missed_message(message: types.Message):
await bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_))
if __name__ == '__main__':
bot.setup_middleware(i18n)
bot.add_custom_filter(TextMatchFilter())
asyncio.run(bot.infinity_polling())

View File

@ -0,0 +1,17 @@
You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot.
<br>
This type of bots are called <b>multibots</b>. They are created using webhooks.
<br>
This is the example of simple multibot.<br>
In order to reproduce this example you need to have <b>domain and ssl connection</b>.
<br>
If you have, go to config.py and specify your data.
<br>
There is also file called <b>nginx_conf.conf</b>, we will use nginx as proxy-server and this file is example nginx.conf file.
<br>
Make sure that server_name and port are the same in both config and nginx_conf
<br>
(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it)
<br>
Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts.

View File

@ -0,0 +1,6 @@
MAIN_BOT_TOKEN = "your_main_bot_token"
WEBHOOK_HOST = "your_domain.com"
WEBHOOK_PATH = "telegram_webhook"
WEBAPP_HOST = "0.0.0.0"
WEBAPP_PORT = 3500

View File

@ -0,0 +1,15 @@
from telebot.async_telebot import AsyncTeleBot
from telebot import types
async def hello_handler(message: types.Message, bot: AsyncTeleBot):
await bot.send_message(message.chat.id, "Hi :)")
async def echo_handler(message: types.Message, bot: AsyncTeleBot):
await bot.send_message(message.chat.id, message.text)
def register_handlers(bot: AsyncTeleBot):
bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True)
bot.register_message_handler(echo_handler, pass_bot=True)

View File

@ -0,0 +1,56 @@
import asyncio
from aiohttp import web
from telebot import types, util
from telebot.async_telebot import AsyncTeleBot
from handlers import register_handlers
import config
main_bot = AsyncTeleBot(config.MAIN_BOT_TOKEN)
app = web.Application()
tokens = {config.MAIN_BOT_TOKEN: True}
async def webhook(request):
token = request.match_info.get('token')
if not tokens.get(token):
return web.Response(status=404)
if request.headers.get('content-type') != 'application/json':
return web.Response(status=403)
json_string = await request.json()
update = types.Update.de_json(json_string)
if token == main_bot.token:
await main_bot.process_new_updates([update])
return web.Response()
from_update_bot = AsyncTeleBot(token)
register_handlers(from_update_bot)
await from_update_bot.process_new_updates([update])
return web.Response()
app.router.add_post("/" + config.WEBHOOK_PATH + "/{token}", webhook)
@main_bot.message_handler(commands=['add_bot'])
async def add_bot(message: types.Message):
token = util.extract_arguments(message.text)
tokens[token] = True
new_bot = AsyncTeleBot(token)
await new_bot.delete_webhook()
await new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}")
await new_bot.send_message(message.chat.id, "Webhook was set.")
async def main():
await main_bot.delete_webhook()
await main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}")
web.run_app(app, host=config.WEBAPP_HOST, port=config.WEBAPP_PORT)
if __name__ == '__main__':
asyncio.run(main())

View File

@ -0,0 +1,8 @@
server {
server_name your_domain.com;
location /telegram_webhook/ {
proxy_pass http://localhost:3500;
}
}

View File

@ -0,0 +1,20 @@
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..
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,29 @@
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)])
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,35 @@
#!/usr/bin/python
# This is a set_my_commands example.
# Press on [/] button in telegram client.
# Important, to update the command menu, be sure to exit the chat with the bot and log in again
# Important, command for chat_id and for group have a higher priority than for all
import asyncio
import telebot
from telebot.async_telebot import AsyncTeleBot
API_TOKEN = '<api_token>'
bot = AsyncTeleBot(API_TOKEN)
async def main():
# use in for delete with the necessary scope and language_code if necessary
await bot.delete_my_commands(scope=None, language_code=None)
await bot.set_my_commands(
commands=[
telebot.types.BotCommand("command1", "command1 description"),
telebot.types.BotCommand("command2", "command2 description")
],
# scope=telebot.types.BotCommandScopeChat(12345678) # use for personal command menu for users
# scope=telebot.types.BotCommandScopeAllPrivateChats() # use for all private chats
)
cmd = await bot.get_my_commands(scope=None, language_code=None)
print([c.to_json() for c in cmd])
if __name__ == '__main__':
asyncio.run(main())

View File

@ -0,0 +1,15 @@
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)
import asyncio
asyncio.run(bot.polling(skip_pending=True)) # to skip updates

View File

@ -0,0 +1,52 @@
#!/usr/bin/python3
# This is a simple bot with schedule timer
# https://github.com/ibrb/python-aioschedule
# https://schedule.readthedocs.io
import asyncio
import aioschedule
from telebot.async_telebot import AsyncTeleBot
API_TOKEN = '<api_token>'
bot = AsyncTeleBot(API_TOKEN)
async def beep(chat_id) -> None:
"""Send the beep message."""
await bot.send_message(chat_id, text='Beep!')
aioschedule.clear(chat_id) # return schedule.CancelJob not working in aioschedule use tag for delete
@bot.message_handler(commands=['help', 'start'])
async def send_welcome(message):
await bot.reply_to(message, "Hi! Use /set <seconds> to set a timer")
@bot.message_handler(commands=['set'])
async def set_timer(message):
args = message.text.split()
if len(args) > 1 and args[1].isdigit():
sec = int(args[1])
aioschedule.every(sec).seconds.do(beep, message.chat.id).tag(message.chat.id)
else:
await bot.reply_to(message, 'Usage: /set <seconds>')
@bot.message_handler(commands=['unset'])
def unset_timer(message):
aioschedule.clean(message.chat.id)
async def scheduler():
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def main():
await asyncio.gather(bot.infinity_polling(), scheduler())
if __name__ == '__main__':
asyncio.run(main())

View File

@ -0,0 +1,16 @@
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)
import asyncio
asyncio.run(bot.polling())

View File

@ -0,0 +1,45 @@
#!/usr/bin/python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
# Example on built-in function to receive and process webhooks.
from telebot.async_telebot import AsyncTeleBot
import asyncio
bot = AsyncTeleBot('TOKEN')
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
DOMAIN = '1.2.3.4' # either domain, or ip address of vps
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
# 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)
# it uses fastapi + uvicorn
asyncio.run(bot.run_webhooks(
listen=DOMAIN,
certificate=WEBHOOK_SSL_CERT,
certificate_key=WEBHOOK_SSL_PRIV
))

View File

@ -0,0 +1,25 @@
import telebot
from telebot import types, AdvancedCustomFilter
from telebot.callback_data import CallbackData, CallbackDataFilter
calendar_factory = CallbackData("year", "month", prefix="calendar")
calendar_zoom = CallbackData("year", prefix="calendar_zoom")
class CalendarCallbackFilter(AdvancedCustomFilter):
key = 'calendar_config'
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
class CalendarZoomCallbackFilter(AdvancedCustomFilter):
key = 'calendar_zoom_config'
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
return config.check(query=call)
def bind_filters(bot: telebot.TeleBot):
bot.add_custom_filter(CalendarCallbackFilter())
bot.add_custom_filter(CalendarZoomCallbackFilter())

View File

@ -0,0 +1,92 @@
import calendar
from datetime import date, timedelta
from examples.callback_data_examples.advanced_calendar_example.filters import calendar_factory, calendar_zoom
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
EMTPY_FIELD = '1'
WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)]
MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)]
def generate_calendar_days(year: int, month: int):
keyboard = InlineKeyboardMarkup(row_width=7)
today = date.today()
keyboard.add(
InlineKeyboardButton(
text=date(year=year, month=month, day=1).strftime('%b %Y'),
callback_data=EMTPY_FIELD
)
)
keyboard.add(*[
InlineKeyboardButton(
text=day,
callback_data=EMTPY_FIELD
)
for day in WEEK_DAYS
])
for week in calendar.Calendar().monthdayscalendar(year=year, month=month):
week_buttons = []
for day in week:
day_name = ' '
if day == today.day and today.year == year and today.month == month:
day_name = '🔘'
elif day != 0:
day_name = str(day)
week_buttons.append(
InlineKeyboardButton(
text=day_name,
callback_data=EMTPY_FIELD
)
)
keyboard.add(*week_buttons)
previous_date = date(year=year, month=month, day=1) - timedelta(days=1)
next_date = date(year=year, month=month, day=1) + timedelta(days=31)
keyboard.add(
InlineKeyboardButton(
text='Previous month',
callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month)
),
InlineKeyboardButton(
text='Zoom out',
callback_data=calendar_zoom.new(year=year)
),
InlineKeyboardButton(
text='Next month',
callback_data=calendar_factory.new(year=next_date.year, month=next_date.month)
),
)
return keyboard
def generate_calendar_months(year: int):
keyboard = InlineKeyboardMarkup(row_width=3)
keyboard.add(
InlineKeyboardButton(
text=date(year=year, month=1, day=1).strftime('Year %Y'),
callback_data=EMTPY_FIELD
)
)
keyboard.add(*[
InlineKeyboardButton(
text=month,
callback_data=calendar_factory.new(year=year, month=month_number)
)
for month_number, month in MONTHS
])
keyboard.add(
InlineKeyboardButton(
text='Previous year',
callback_data=calendar_zoom.new(year=year - 1)
),
InlineKeyboardButton(
text='Next year',
callback_data=calendar_zoom.new(year=year + 1)
)
)
return keyboard

View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
"""
This Example will show you an advanced usage of CallbackData.
In this example calendar was implemented
"""
from datetime import date
from examples.callback_data_examples.advanced_calendar_example.keyboards import generate_calendar_days, \
generate_calendar_months, EMTPY_FIELD
from filters import calendar_factory, calendar_zoom, bind_filters
from telebot import types, TeleBot
API_TOKEN = ''
bot = TeleBot(API_TOKEN)
@bot.message_handler(commands='start')
def start_command_handler(message: types.Message):
bot.send_message(message.chat.id,
f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard."
"\nPress /calendar to see it.")
@bot.message_handler(commands='calendar')
def calendar_command_handler(message: types.Message):
now = date.today()
bot.send_message(message.chat.id, 'Calendar', reply_markup=generate_calendar_days(year=now.year, month=now.month))
@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter())
def calendar_action_handler(call: types.CallbackQuery):
callback_data: dict = calendar_factory.parse(callback_data=call.data)
year, month = int(callback_data['year']), int(callback_data['month'])
bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
reply_markup=generate_calendar_days(year=year, month=month))
@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter())
def calendar_zoom_out_handler(call: types.CallbackQuery):
callback_data: dict = calendar_zoom.parse(callback_data=call.data)
year = int(callback_data.get('year'))
bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
reply_markup=generate_calendar_months(year=year))
@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD)
def callback_empty_field_handler(call: types.CallbackQuery):
bot.answer_callback_query(call.id)
if __name__ == '__main__':
bind_filters(bot)
bot.infinity_polling()

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()

View File

@ -23,8 +23,8 @@ def my_chat_m(message: types.ChatMemberUpdated):
#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'
#'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended',
#'video_chat_participants_invited', 'message_auto_delete_timer_changed'
# this handler deletes service messages
@bot.message_handler(content_types=util.content_type_service)

View File

@ -0,0 +1,33 @@
import telebot
from time import sleep, time
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton #Only for creating Inline Buttons, not necessary for creating Invite Links
Token = "api_token" #Your Bot Access Token
Group_ID = -1234567890 #Group ID for which invite link is to be created
bot = telebot.TeleBot(Token, parse_mode="HTML")
#/start command message
@bot.message_handler(commands=['start'])
def startmsg(msg):
bot.reply_to(msg, "Hey there, I'm a bot made by pyTelegramBotAPI!")
#Get notified of incoming members in group
@bot.message_handler(content_types=['new_chat_members'])
def newmember(msg):
#Create an invite link class that contains info about the created invite link using create_chat_invite_link() with parameters
invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds
InviteLink = invite.invite_link #Get the actual invite link from 'invite' class
mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup
mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard
bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group.", reply_markup=mrkplink)
#This will send a message with the newly-created invite link as markup button.
#The member limit will be 1 and expiring time will be 45 sec.
bot.infinity_polling()

View File

@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
"""
This Example will show you usage of TextFilter
In this example you will see how to use TextFilter
with (message_handler, callback_query_handler, poll_handler)
"""
from telebot import TeleBot, types
from telebot.custom_filters import TextFilter, TextMatchFilter, IsReplyFilter
bot = TeleBot("")
@bot.message_handler(text=TextFilter(equals='hello'))
def hello_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True))
def hello_handler_ignore_case(message: types.Message):
bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(contains=['good', 'bad']))
def contains_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True))
def contains_handler_ignore_case(message: types.Message):
bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE
def starts_with_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE
def starts_with_handler_ignore_case(message: types.Message):
bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay
def ends_with_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY
def ends_with_handler_ignore_case(message: types.Message):
bot.send_message(message.chat.id, message.text + ' ignore case')
@bot.message_handler(text=TextFilter(equals='/callback'))
def send_callback(message: types.Message):
keyboard = types.InlineKeyboardMarkup(
keyboard=[
[types.InlineKeyboardButton(text='callback data', callback_data='example')],
[types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')]
]
)
bot.send_message(message.chat.id, message.text, reply_markup=keyboard)
@bot.callback_query_handler(func=None, text=TextFilter(equals='example'))
def callback_query_handler(call: types.CallbackQuery):
bot.answer_callback_query(call.id, call.data, show_alert=True)
@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True))
def callback_query_handler_ignore_case(call: types.CallbackQuery):
bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True)
@bot.message_handler(text=TextFilter(equals='/poll'))
def send_poll(message: types.Message):
bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night'])
bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night'])
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?'))
def poll_question_handler(poll: types.Poll):
print(poll.question)
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True))
def poll_question_handler_ignore_case(poll: types.Poll):
print(poll.question + ' ignore case')
# either hi or contains one of (привет, salom)
@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True))
def multiple_patterns_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
# starts with one of (mi, mea) for ex. minor, milk, meal, meat
@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True))
def multiple_starts_with_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson
@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True))
def multiple_ends_with_handler(message: types.Message):
bot.send_message(message.chat.id, message.text)
# !ban /ban .ban !бан /бан .бан
@bot.message_handler(is_reply=True,
text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True))
def ban_command_handler(message: types.Message):
if len(message.text) == 4 and message.chat.type != 'private':
try:
bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id)
bot.reply_to(message.reply_to_message, 'Banned.')
except Exception as err:
print(err.args)
return
if __name__ == '__main__':
bot.add_custom_filter(TextMatchFilter())
bot.add_custom_filter(IsReplyFilter())
bot.infinity_polling()

View File

@ -1,14 +1,39 @@
import telebot
import telebot # telebot
from telebot import custom_filters
from telebot.handler_backends import State, StatesGroup #States
bot = telebot.TeleBot("")
# States storage
from telebot.storage import StateMemoryStorage
class MyStates:
name = 1
surname = 2
age = 3
# Starting from version 4.4.0+, we support storages.
# StateRedisStorage -> Redis-based storage.
# StatePickleStorage -> Pickle-based storage.
# For redis, you will need to install redis.
# Pass host, db, password, or anything else,
# if you need to change config for redis.
# Pickle requires path. Default path is in folder .state-saves.
# If you were using older version of pytba for pickle,
# you need to migrate from old pickle to new by using
# StatePickleStorage().convert_old_to_new()
# Now, you can pass storage to bot.
state_storage = StateMemoryStorage() # you can init here another storage
bot = telebot.TeleBot("TOKEN",
state_storage=state_storage)
# States group.
class MyStates(StatesGroup):
# Just name variables differently
name = State() # creating instances of State class is enough from now
surname = State()
age = State()
@ -17,50 +42,60 @@ def start_ex(message):
"""
Start command. Here we are starting state
"""
bot.set_state(message.from_user.id, MyStates.name)
bot.set_state(message.from_user.id, MyStates.name, message.chat.id)
bot.send_message(message.chat.id, 'Hi, write me a name')
@bot.message_handler(state="*", commands='cancel')
# Any state
@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.delete_state(message.from_user.id, message.chat.id)
@bot.message_handler(state=MyStates.name)
def name_get(message):
"""
State 1. Will process when user's state is 1.
State 1. Will process when user's state is MyStates.name.
"""
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:
bot.send_message(message.chat.id, 'Now write me a surname')
bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
with bot.retrieve_data(message.from_user.id, message.chat.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.
State 2. Will process when user's state is MyStates.surname.
"""
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:
bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
with bot.retrieve_data(message.from_user.id, message.chat.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)
"""
State 3. Will process when user's state is MyStates.age.
"""
with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
msg = ("Ready, take a look:\n<b>"
f"Name: {data['name']}\n"
f"Surname: {data['surname']}\n"
f"Age: {message.text}</b>")
bot.send_message(message.chat.id, msg, parse_mode="html")
bot.delete_state(message.from_user.id, message.chat.id)
#incorrect number
@bot.message_handler(state=MyStates.age, is_digit=False)
def age_incorrect(message):
"""
Wrong response for MyStates.age
"""
bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
# register filters
@ -68,7 +103,4 @@ def age_incorrect(message):
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)
bot.infinity_polling(skip_pending=True)

View File

@ -0,0 +1,53 @@
from telebot import TeleBot
from telebot import formatting
bot = TeleBot('TOKEN')
@bot.message_handler(commands=['start'])
def start_message(message):
bot.send_message(
message.chat.id,
# function which connects all strings
formatting.format_text(
formatting.mbold(message.from_user.first_name),
formatting.mitalic(message.from_user.first_name),
formatting.munderline(message.from_user.first_name),
formatting.mstrikethrough(message.from_user.first_name),
formatting.mcode(message.from_user.first_name),
separator=" " # separator separates all strings
),
parse_mode='MarkdownV2'
)
# just a bold text using markdownv2
bot.send_message(
message.chat.id,
formatting.mbold(message.from_user.first_name),
parse_mode='MarkdownV2'
)
# html
bot.send_message(
message.chat.id,
formatting.format_text(
formatting.hbold(message.from_user.first_name),
formatting.hitalic(message.from_user.first_name),
formatting.hunderline(message.from_user.first_name),
formatting.hstrikethrough(message.from_user.first_name),
formatting.hcode(message.from_user.first_name),
# hide_link is only for html
formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'),
separator=" "
),
parse_mode='HTML'
)
# just a bold text in html
bot.send_message(
message.chat.id,
formatting.hbold(message.from_user.first_name),
parse_mode='HTML'
)
bot.infinity_polling()

View File

@ -0,0 +1,35 @@
# Middlewares
## Type of middlewares in pyTelegramBotAPI
Currently, synchronous version of pyTelegramBotAPI has two types of middlewares:
- Class-based middlewares
- Function-based middlewares
## Purpose of middlewares
Middlewares are designed to get update before handler's execution.
## Class-based middlewares
This type of middleware has more functionality compared to function-based one.
Class based middleware should be instance of `telebot.handler_backends.BaseMiddleware.`
Each middleware should have 2 main functions:
`pre_process` -> is a method of class which receives update, and data.
Data - is a dictionary, which could be passed right to handler, and `post_process` function.
`post_process` -> is a function of class which receives update, data, and exception, that happened in handler. If handler was executed correctly - then exception will equal to None.
## Function-based middlewares
To use function-based middleware, you should set `apihelper.ENABLE_MIDDLEWARE = True`.
This type of middleware is created by using a decorator for middleware.
With this type middleware, you can retrieve update immediately after update came. You should set update_types as well.
## Why class-based middlewares are better?
- You can pass data between post, pre_process functions, and handler.
- If there is an exception in handler, you will get exception parameter with exception class in post_process.
- Has post_process -> method which works after the handler's execution.
## Take a look at examples for more.

View File

@ -0,0 +1,39 @@
# Just a little example of middleware handlers
from telebot.handler_backends import BaseMiddleware
from telebot import TeleBot
from telebot.handler_backends import CancelUpdate
bot = TeleBot('TOKEN',
use_class_middlewares=True) # if you don't set it to true, middlewares won't work
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
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
bot.send_message(message.chat.id, 'You are making request too often')
return CancelUpdate()
self.last_time[message.from_user.id] = message.date
def post_process(self, message, data, exception):
pass
bot.setup_middleware(SimpleMiddleware(2))
@bot.message_handler(commands=['start'])
def start(message): # you don't have to put data in handler.
bot.send_message(message.chat.id, 'Hello!')
bot.infinity_polling()

View File

@ -0,0 +1,32 @@
from telebot import TeleBot
from telebot.handler_backends import BaseMiddleware
bot = TeleBot('TOKEN', use_class_middlewares=True) # set use_class_middlewares to True!
# otherwise, class-based middlewares won't execute.
# You can use this classes for cancelling update or skipping handler:
# from telebot.handler_backends import CancelUpdate, SkipHandler
class Middleware(BaseMiddleware):
def __init__(self):
self.update_types = ['message']
def pre_process(self, message, data):
data['foo'] = 'Hello' # just for example
# we edited the data. now, this data is passed to handler.
# return SkipHandler() -> this will skip handler
# return CancelUpdate() -> this will cancel update
def post_process(self, message, data, exception=None):
print(data['foo'])
if exception: # check for exception
print(exception)
@bot.message_handler(commands=['start'])
def start(message, data: dict): # you don't have to put data parameter in handler if you don't need it.
bot.send_message(message.chat.id, data['foo'])
data['foo'] = 'Processed' # we changed value of data.. this data is now passed to post_process.
# Setup middleware
bot.setup_middleware(Middleware())
bot.infinity_polling()

View File

@ -0,0 +1,121 @@
import gettext
import os
import threading
from telebot.handler_backends import BaseMiddleware
try:
from babel.support import LazyProxy
babel_imported = True
except ImportError:
babel_imported = False
class I18N(BaseMiddleware):
"""
This middleware provides high-level tool for internationalization
It is based on gettext util.
"""
context_lang = threading.local()
def __init__(self, translations_path, domain_name: str):
super().__init__()
self.update_types = self.process_update_types()
self.path = translations_path
self.domain = domain_name
self.translations = self.find_translations()
@property
def available_translations(self):
return list(self.translations)
def gettext(self, text: str, lang: str = None):
"""
Singular translations
"""
if lang is None:
lang = self.context_lang.language
if lang not in self.translations:
return text
translator = self.translations[lang]
return translator.gettext(text)
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
"""
Plural translations
"""
if lang is None:
lang = self.context_lang.language
if lang not in self.translations:
if n == 1:
return singular
return plural
translator = self.translations[lang]
return translator.ngettext(singular, plural, n)
def lazy_gettext(self, text: str, lang: str = None):
if not babel_imported:
raise RuntimeError('babel module is not imported. Check that you installed it.')
return LazyProxy(self.gettext, text, lang, enable_cache=False)
def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1):
if not babel_imported:
raise RuntimeError('babel module is not imported. Check that you installed it.')
return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False)
def get_user_language(self, obj):
"""
You need to override this method and return user language
"""
raise NotImplementedError
def process_update_types(self) -> list:
"""
You need to override this method and return any update types which you want to be processed
"""
raise NotImplementedError
def pre_process(self, message, data):
"""
context language variable will be set each time when update from 'process_update_types' comes
value is the result of 'get_user_language' method
"""
self.context_lang.language = self.get_user_language(obj=message)
def post_process(self, message, data, exception):
pass
def find_translations(self):
"""
Looks for translations with passed 'domain' in passed 'path'
"""
if not os.path.exists(self.path):
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
result = {}
for name in os.listdir(self.path):
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
if not os.path.isdir(translations_path):
continue
po_file = os.path.join(translations_path, self.domain + '.po')
mo_file = po_file[:-2] + 'mo'
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
with open(mo_file, 'rb') as file:
result[name] = gettext.GNUTranslations(file)
return result

View File

@ -0,0 +1,34 @@
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
def languages_keyboard():
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text="English", callback_data='en'),
InlineKeyboardButton(text="Русский", callback_data='ru'),
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
]
]
)
def clicker_keyboard(_):
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text=_("click"), callback_data='click'),
]
]
)
def menu_keyboard(_):
keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
keyboard.add(
KeyboardButton(text=_("My user id")),
KeyboardButton(text=_("My user name")),
KeyboardButton(text=_("My first name"))
)
return keyboard

View File

@ -0,0 +1,81 @@
# English translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr ""
#: keyboards.py:29
msgid "My user id"
msgstr ""
#: keyboards.py:30
msgid "My user name"
msgstr ""
#: keyboards.py:31
msgid "My first name"
msgstr ""
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
#: main.py:121
msgid "Language has been changed"
msgstr ""
#: main.py:130 main.py:150
#, fuzzy
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] ""
msgstr[1] ""
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr ""
#: main.py:203
msgid "Seems you confused language"
msgstr ""
#~ msgid ""
#~ "Hello, {user_fist_name}!\n"
#~ "This is the example of multilanguage bot.\n"
#~ "Available commands:\n"
#~ "\n"
#~ "/lang - change your language\n"
#~ "/plural - pluralization example"
#~ msgstr ""

View File

@ -0,0 +1,82 @@
# Russian translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ru\n"
"Language-Team: ru <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "Клик"
#: keyboards.py:29
msgid "My user id"
msgstr "Мой user id"
#: keyboards.py:30
msgid "My user name"
msgstr "Мой user name"
#: keyboards.py:31
msgid "My first name"
msgstr "Мой first name"
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
"Привет, {user_fist_name}!\n"
"Это пример мультиязычного бота.\n"
"Доступные команды:\n"
"\n"
"/lang - изменить язык\n"
"/plural - пример плюрализации\n"
"/menu - Пример текстового меню"
#: main.py:121
msgid "Language has been changed"
msgstr "Язык был сменён"
#: main.py:130 main.py:150
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "У вас {number} клик"
msgstr[1] "У вас {number} клика"
msgstr[2] "У вас {number} кликов"
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Это кликер.\n"
"\n"
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте."
#: main.py:203
msgid "Seems you confused language"
msgstr "Кажется, вы перепутали язык"

View File

@ -0,0 +1,80 @@
# Uzbek (Latin) translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: uz_Latn\n"
"Language-Team: uz_Latn <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "clik"
#: keyboards.py:29
msgid "My user id"
msgstr "Mani user id"
#: keyboards.py:30
msgid "My user name"
msgstr "Mani user name"
#: keyboards.py:31
msgid "My first name"
msgstr "Mani first name"
#: main.py:97
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example"
msgstr ""
"Salom, {user_fist_name}!\n"
"Bu multilanguage bot misoli.\n"
"Mavjud buyruqlar:\n"
"\n"
"/lang - tilni ozgartirish\n"
"/plural - pluralizatsiya misoli\n"
"/menu - text menu misoli"
#: main.py:121
msgid "Language has been changed"
msgstr "Til ozgartirildi"
#: main.py:130 main.py:150
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "Sizda {number}ta clik"
msgstr[1] "Sizda {number}ta clik"
#: main.py:135 main.py:155
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Bu clicker.\n"
"\n"
#: main.py:163
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli."
#: main.py:203
msgid "Seems you confused language"
msgstr "Tilni adashtirdiz"

View File

@ -0,0 +1,211 @@
"""
In this example you will learn how to adapt your bot to different languages
Using built-in middleware I18N.
You need to install babel package 'https://pypi.org/project/Babel/'
Babel provides a command-line interface for working with message catalogs
After installing babel package you have a script called 'pybabel'
Too see all the commands open terminal and type 'pybabel --help'
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
Create a directory 'locales' where our translations will be stored
First we need to extract texts:
pybabel extract -o locales/{domain_name}.pot --input-dirs .
{domain_name}.pot - is the file where all translations are saved
The name of this file should be the same as domain which you pass to I18N class
In this example domain_name will be 'messages'
For gettext (singular texts) we use '_' alias and it works perfect
You may also you some alias for ngettext (plural texts) but you can face with a problem that
your plural texts are not being extracted
That is because by default 'pybabel extract' recognizes the following keywords:
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
To add your own keyword you can use '-k' flag
In this example for 'ngettext' i will assign double underscore alias '__'
Full command with pluralization support will look so:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
pybabel init -i locales/{domain_name}.pot -d locales -l en
pybabel init -i locales/{domain_name}.pot -d locales -l ru
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
After you translated all the texts you need to compile .po files:
pybabel compile -d locales
When you delete/update your texts you also need to update them in .po files:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
pybabel update -i locales/{domain_name}.pot -d locales
- translate
pybabel compile -d locales
If you have any exceptions check:
- you have installed babel
- translations are ready, so you just compiled it
- in the commands above you replaced {domain_name} to messages
- you are writing commands from correct path in terminal
"""
import asyncio
from typing import Union
import keyboards
from i18n_base_middleware import I18N
from telebot import TeleBot
from telebot import types, StateMemoryStorage
from telebot.custom_filters import TextMatchFilter, TextFilter
class I18NMiddleware(I18N):
def process_update_types(self) -> list:
"""
Here you need to return a list of update types which you want to be processed
"""
return ['message', 'callback_query']
def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]):
"""
This method is called when new update comes (only updates which you return in 'process_update_types' method)
Returned language will be used in 'pre_process' method of parent class
Returned language will be set to context language variable.
If you need to get translation with user's actual language you don't have to pass it manually
It will be automatically passed from context language value.
However if you need some other language you can always pass it.
"""
user_id = obj.from_user.id
if user_id not in users_lang:
users_lang[user_id] = 'en'
return users_lang[user_id]
storage = StateMemoryStorage()
bot = TeleBot("", state_storage=storage, use_class_middlewares=True)
i18n = I18NMiddleware(translations_path='locales', domain_name='messages')
_ = i18n.gettext # for singular translations
__ = i18n.ngettext # for plural translations
# These are example storages, do not use it in a production development
users_lang = {}
users_clicks = {}
@bot.message_handler(commands=['start'])
def start_handler(message: types.Message):
text = _("Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n\n"
"/lang - change your language\n"
"/plural - pluralization example\n"
"/menu - text menu example")
# remember don't use f string for interpolation, use .format method instead
text = text.format(user_fist_name=message.from_user.first_name)
bot.send_message(message.from_user.id, text)
@bot.message_handler(commands=['lang'])
def change_language_handler(message: types.Message):
bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
reply_markup=keyboards.languages_keyboard())
@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn']))
def language_handler(call: types.CallbackQuery):
lang = call.data
users_lang[call.from_user.id] = lang
# When you changed user language, you have to pass it manually beacause it is not changed in context
bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
@bot.message_handler(commands=['plural'])
def pluralization_handler(message: types.Message):
if not users_clicks.get(message.from_user.id):
users_clicks[message.from_user.id] = 0
clicks = users_clicks[message.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
@bot.callback_query_handler(func=None, text=TextFilter(equals='click'))
def click_handler(call: types.CallbackQuery):
if not users_clicks.get(call.from_user.id):
users_clicks[call.from_user.id] = 1
else:
users_clicks[call.from_user.id] += 1
clicks = users_clicks[call.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
bot.edit_message_text(text, call.from_user.id, call.message.message_id,
reply_markup=keyboards.clicker_keyboard(_))
@bot.message_handler(commands=['menu'])
def menu_handler(message: types.Message):
text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.")
bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_))
# For lazy tranlations
# lazy gettext is used when you don't know user's locale
# It can be used for example to handle text buttons in multilanguage bot
# The actual translation will be delayed until update comes and context language is set
l_ = i18n.lazy_gettext
# Handlers below will handle text according to user's language
@bot.message_handler(text=l_("My user id"))
def return_user_id(message: types.Message):
bot.send_message(message.chat.id, str(message.from_user.id))
@bot.message_handler(text=l_("My user name"))
def return_user_id(message: types.Message):
username = message.from_user.username
if not username:
username = '-'
bot.send_message(message.chat.id, username)
# You can make it case-insensitive
@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True))
def return_user_id(message: types.Message):
bot.send_message(message.chat.id, message.from_user.first_name)
all_menu_texts = []
for language in i18n.available_translations:
for menu_text in ("My user id", "My user name", "My first name"):
all_menu_texts.append(_(menu_text, language))
# When user confused language. (handles all menu buttons texts)
@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True))
def missed_message(message: types.Message):
bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_))
if __name__ == '__main__':
bot.setup_middleware(i18n)
bot.add_custom_filter(TextMatchFilter())
asyncio.run(bot.infinity_polling())

View File

@ -0,0 +1,77 @@
import gettext
import os
import threading
class I18N:
"""
This class provides high-level tool for internationalization
It is based on gettext util.
"""
context_lang = threading.local()
def __init__(self, translations_path, domain_name: str):
self.path = translations_path
self.domain = domain_name
self.translations = self.find_translations()
@property
def available_translations(self):
return list(self.translations)
def gettext(self, text: str, lang: str = None):
"""
Singular translations
"""
if lang is None:
lang = self.context_lang.language
if lang not in self.translations:
return text
translator = self.translations[lang]
return translator.gettext(text)
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
"""
Plural translations
"""
if lang is None:
lang = self.context_lang.language
if lang not in self.translations:
if n == 1:
return singular
return plural
translator = self.translations[lang]
return translator.ngettext(singular, plural, n)
def find_translations(self):
"""
Looks for translations with passed 'domain' in passed 'path'
"""
if not os.path.exists(self.path):
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
result = {}
for name in os.listdir(self.path):
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
if not os.path.isdir(translations_path):
continue
po_file = os.path.join(translations_path, self.domain + '.po')
mo_file = po_file[:-2] + 'mo'
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
with open(mo_file, 'rb') as file:
result[name] = gettext.GNUTranslations(file)
return result

View File

@ -0,0 +1,23 @@
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
def languages_keyboard():
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text="English", callback_data='en'),
InlineKeyboardButton(text="Русский", callback_data='ru'),
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
]
]
)
def clicker_keyboard(_):
return InlineKeyboardMarkup(
keyboard=[
[
InlineKeyboardButton(text=_("click"), callback_data='click'),
]
]
)

View File

@ -0,0 +1,50 @@
# English translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr ""
#: main.py:78
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example"
msgstr ""
#: main.py:102
msgid "Language has been changed"
msgstr ""
#: main.py:114
#, fuzzy
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] ""
msgstr[1] ""
#: main.py:120
msgid ""
"This is clicker.\n"
"\n"
msgstr ""

View File

@ -0,0 +1,59 @@
# Russian translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: ru\n"
"Language-Team: ru <LL@li.org>\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "Клик"
#: main.py:78
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example"
msgstr ""
"Привет, {user_fist_name}!\n"
"Это пример мультиязычного бота.\n"
"Доступные команды:\n"
"\n"
"/lang - изменить язык\n"
"/plural - пример плюрализации"
#: main.py:102
msgid "Language has been changed"
msgstr "Язык был сменён"
#: main.py:114
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "У вас {number} клик"
msgstr[1] "У вас {number} клика"
msgstr[2] "У вас {number} кликов"
#: main.py:120
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Это кликер.\n"
"\n"

View File

@ -0,0 +1,57 @@
# Uzbek (Latin) translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: uz_Latn\n"
"Language-Team: uz_Latn <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
#: keyboards.py:20
msgid "click"
msgstr "clik"
#: main.py:78
msgid ""
"Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n"
"\n"
"/lang - change your language\n"
"/plural - pluralization example"
msgstr ""
"Salom, {user_fist_name}!\n"
"Bu multilanguage bot misoli.\n"
"Mavjud buyruqlar:\n"
"\n"
"/lang - tilni ozgartirish\n"
"/plural - pluralizatsiya misoli"
#: main.py:102
msgid "Language has been changed"
msgstr "Til ozgartirildi"
#: main.py:114
msgid "You have {number} click"
msgid_plural "You have {number} clicks"
msgstr[0] "Sizda {number}ta clik"
msgstr[1] "Sizda {number}ta clik"
#: main.py:120
msgid ""
"This is clicker.\n"
"\n"
msgstr ""
"Bu clicker.\n"
"\n"

View File

@ -0,0 +1,134 @@
"""
In this example you will learn how to adapt your bot to different languages
Using built-in class I18N.
You need to install babel package 'https://pypi.org/project/Babel/'
Babel provides a command-line interface for working with message catalogs
After installing babel package you have a script called 'pybabel'
Too see all the commands open terminal and type 'pybabel --help'
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
Create a directory 'locales' where our translations will be stored
First we need to extract texts:
pybabel extract -o locales/{domain_name}.pot --input-dirs .
{domain_name}.pot - is the file where all translations are saved
The name of this file should be the same as domain which you pass to I18N class
In this example domain_name will be 'messages'
For gettext (singular texts) we use '_' alias and it works perfect
You may also you some alias for ngettext (plural texts) but you can face with a problem that
your plural texts are not being extracted
That is because by default 'pybabel extract' recognizes the following keywords:
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
To add your own keyword you can use '-k' flag
In this example for 'ngettext' i will assign double underscore alias '__'
Full command with pluralization support will look so:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
pybabel init -i locales/{domain_name}.pot -d locales -l en
pybabel init -i locales/{domain_name}.pot -d locales -l ru
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
After you translated all the texts you need to compile .po files:
pybabel compile -d locales
When you delete/update your texts you also need to update them in .po files:
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
pybabel update -i locales/{domain_name}.pot -d locales
- translate
pybabel compile -d locales
If you have any exceptions check:
- you have installed babel
- translations are ready, so you just compiled it
- in the commands above you replaced {domain_name} to messages
- you are writing commands from correct path in terminal
"""
from telebot import TeleBot, types, custom_filters
from telebot import apihelper
from telebot.storage.memory_storage import StateMemoryStorage
import keyboards
from i18n_class import I18N
apihelper.ENABLE_MIDDLEWARE = True
storage = StateMemoryStorage()
# IMPORTANT! This example works only if polling is non-threaded.
bot = TeleBot("", state_storage=storage, threaded=False)
i18n = I18N(translations_path='locales', domain_name='messages')
_ = i18n.gettext # for singular translations
__ = i18n.ngettext # for plural translations
# These are example storages, do not use it in a production development
users_lang = {}
users_clicks = {}
@bot.middleware_handler(update_types=['message', 'callback_query'])
def set_contex_language(bot_instance, message):
i18n.context_lang.language = users_lang.get(message.from_user.id, 'en')
@bot.message_handler(commands=['start'])
def start_handler(message: types.Message):
text = _("Hello, {user_fist_name}!\n"
"This is the example of multilanguage bot.\n"
"Available commands:\n\n"
"/lang - change your language\n"
"/plural - pluralization example")
# remember don't use f string for interpolation, use .format method instead
text = text.format(user_fist_name=message.from_user.first_name)
bot.send_message(message.from_user.id, text)
@bot.message_handler(commands=['lang'])
def change_language_handler(message: types.Message):
bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
reply_markup=keyboards.languages_keyboard())
@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn']))
def language_handler(call: types.CallbackQuery):
lang = call.data
users_lang[call.from_user.id] = lang
# When you change user's language, pass language explicitly coz it's not changed in context
bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
bot.delete_state(call.from_user.id)
@bot.message_handler(commands=['plural'])
def pluralization_handler(message: types.Message):
if not users_clicks.get(message.from_user.id):
users_clicks[message.from_user.id] = 0
clicks = users_clicks[message.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks,
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click'))
def click_handler(call: types.CallbackQuery):
if not users_clicks.get(call.from_user.id):
users_clicks[call.from_user.id] = 1
else:
users_clicks[call.from_user.id] += 1
clicks = users_clicks[call.from_user.id]
text = __(
singular="You have {number} click",
plural="You have {number} clicks",
n=clicks
)
text = _("This is clicker.\n\n") + text.format(number=clicks)
bot.edit_message_text(text, call.from_user.id, call.message.message_id,
reply_markup=keyboards.clicker_keyboard(_))
if __name__ == '__main__':
bot.add_custom_filter(custom_filters.TextMatchFilter())
bot.infinity_polling()

View File

@ -0,0 +1,17 @@
You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot.
<br>
This type of bots are called <b>multibots</b>. They are created using webhooks.
<br>
This is the example of simple multibot.<br>
In order to reproduce this example you need to have <b>domain and ssl connection</b>.
<br>
If you have, go to config.py and specify your data.
<br>
There is also file called <b>nginx_conf.conf</b>, we will use nginx as proxy-server and this file is example nginx.conf file.
<br>
Make sure that server_name and port are the same in both config and nginx_conf
<br>
(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it)
<br>
Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts.

View File

@ -0,0 +1,6 @@
MAIN_BOT_TOKEN = "your_main_bot_token"
WEBHOOK_HOST = "your_domain.com"
WEBHOOK_PATH = "telegram_webhook"
WEBAPP_HOST = "0.0.0.0"
WEBAPP_PORT = 3500

View File

@ -0,0 +1,14 @@
from telebot import types, TeleBot
def hello_handler(message: types.Message, bot: TeleBot):
bot.send_message(message.chat.id, "Hi :)")
def echo_handler(message: types.Message, bot: TeleBot):
bot.send_message(message.chat.id, message.text)
def register_handlers(bot: TeleBot):
bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True)
bot.register_message_handler(echo_handler, pass_bot=True)

48
examples/multibot/main.py Normal file
View File

@ -0,0 +1,48 @@
from flask import Flask
from flask import request, abort
from telebot import TeleBot, types, util
from handlers import register_handlers
import config
main_bot = TeleBot(config.MAIN_BOT_TOKEN)
app = Flask(__name__)
tokens = {config.MAIN_BOT_TOKEN: True}
@app.route(f"/{config.WEBHOOK_PATH}/<token>", methods=['POST'])
def webhook(token: str):
if not tokens.get(token):
return abort(404)
if request.headers.get('content-type') != 'application/json':
return abort(403)
json_string = request.get_data().decode('utf-8')
update = types.Update.de_json(json_string)
if token == main_bot.token:
main_bot.process_new_updates([update])
return ''
from_update_bot = TeleBot(token)
register_handlers(from_update_bot)
from_update_bot.process_new_updates([update])
return ''
@main_bot.message_handler(commands=['add_bot'])
def add_bot(message: types.Message):
token = util.extract_arguments(message.text)
tokens[token] = True
new_bot = TeleBot(token)
new_bot.delete_webhook()
new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}")
new_bot.send_message(message.chat.id, "Webhook was set.")
if __name__ == '__main__':
main_bot.delete_webhook()
main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}")
app.run(host=config.WEBAPP_HOST, port=config.WEBAPP_PORT)

View File

@ -0,0 +1,8 @@
server {
server_name your_domain.com;
location /telegram_webhook/ {
proxy_pass http://localhost:3500;
}
}

View File

@ -38,21 +38,20 @@ def command_pay(message):
"Real cards won't work with me, no money will be debited from your account."
" Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`"
"\n\nThis is your demo invoice:", parse_mode='Markdown')
bot.send_invoice(message.chat.id, title='Working Time Machine',
description='Want to visit your great-great-great-grandparents?'
' Make a fortune at the races?'
' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?'
' Order our Working Time Machine today!',
provider_token=provider_token,
currency='usd',
bot.send_invoice(
message.chat.id, #chat_id
'Working Time Machine', #title
' Want to visit your great-great-great-grandparents? Make a fortune at the races? Shake hands with Hammurabi and take a stroll in the Hanging Gardens? Order our Working Time Machine today!', #description
'HAPPY FRIDAYS COUPON', #invoice_payload
provider_token, #provider_token
'usd', #currency
prices, #prices
photo_url='http://erkelzaar.tsudao.com/models/perrotta/TIME_MACHINE.jpg',
photo_height=512, # !=0/None or picture won't be shown
photo_width=512,
photo_size=512,
is_flexible=False, # True If you need to set up Shipping Fee
prices=prices,
start_parameter='time-machine-example',
invoice_payload='HAPPY FRIDAYS COUPON')
start_parameter='time-machine-example')
@bot.shipping_query_handler(func=lambda query: True)

View File

@ -0,0 +1,63 @@
# This example shows you how to create a custom QWERTY keyboard using reply keyboard markup
import telebot
from telebot.types import ReplyKeyboardMarkup, KeyboardButton
TOKEN = "<your_token>"
bot = telebot.TeleBot(TOKEN)
keys = ["1","2","3","4","5","6","7","8","9","0","q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m"]
symbols = ["1","2","3","4","5","6","7","8","9","0","!","@","#","$","%","^","&","*","(",")","\'","\"","/","\\",",",".",";",":"]
def keyboard(key_type="Normal"):
markup = ReplyKeyboardMarkup(row_width=10)
if key_type == "Normal":
row = [KeyboardButton(x) for x in keys[:10]]
markup.add(*row)
row = [KeyboardButton(x) for x in keys[10:20]]
markup.add(*row)
row = [KeyboardButton(x) for x in keys[20:29]]
markup.add(*row)
row = [KeyboardButton(x) for x in keys[29:]]
markup.add(*row)
markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done"))
elif key_type == "Symbols":
row = [KeyboardButton(x) for x in symbols[:10]]
markup.add(*row)
row = [KeyboardButton(x) for x in symbols[10:20]]
markup.add(*row)
row = [KeyboardButton(x) for x in symbols[20:]]
markup.add(*row)
markup.add(KeyboardButton("Caps Lock"),KeyboardButton("Normal"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done"))
else:
row = [KeyboardButton(x.upper()) for x in keys[:10]]
markup.add(*row)
row = [KeyboardButton(x.upper()) for x in keys[10:20]]
markup.add(*row)
row = [KeyboardButton(x.upper()) for x in keys[20:29]]
markup.add(*row)
row = [KeyboardButton(x.upper()) for x in keys[29:]]
markup.add(*row)
markup.add(KeyboardButton("Normal"),KeyboardButton("Symbols"),KeyboardButton("🔙Delete"),KeyboardButton("✅Done"))
return markup
@bot.message_handler(commands=["start"])
def start_message(message):
bot.send_message(message.chat.id,"You can use the keyboard",reply_markup=keyboard())
@bot.message_handler(func=lambda message:True)
def all_messages(message):
if message.text == "✅Done":
markup = telebot.types.ReplyKeyboardRemove()
bot.send_message(message.from_user.id,"Done with Keyboard",reply_markup=markup)
elif message.text == "Symbols":
bot.send_message(message.from_user.id,"Special characters",reply_markup=keyboard("Symbols"))
elif message.text == "Normal":
bot.send_message(message.from_user.id,"Normal Keyboard",reply_markup=keyboard("Normal"))
elif message.text == "Caps Lock":
bot.send_message(message.from_user.id,"Caps Lock",reply_markup=keyboard("Caps"))
elif message.text == "🔙Delete":
bot.delete_message(message.from_user.id,message.message_id)
else:
bot.send_message(message.chat.id,message.text)
bot.infinity_polling()

View File

@ -0,0 +1,28 @@
#!/usr/bin/python
# This is a set_my_commands example.
# Press on [/] button in telegram client.
# Important, to update the command menu, be sure to exit the chat with the bot and enter to chat again
# Important, command for chat_id and for group have a higher priority than for all
import telebot
API_TOKEN = '<api_token>'
bot = telebot.TeleBot(API_TOKEN)
# use in for delete with the necessary scope and language_code if necessary
bot.delete_my_commands(scope=None, language_code=None)
bot.set_my_commands(
commands=[
telebot.types.BotCommand("command1", "command1 description"),
telebot.types.BotCommand("command2", "command2 description")
],
# scope=telebot.types.BotCommandScopeChat(12345678) # use for personal command for users
# scope=telebot.types.BotCommandScopeAllPrivateChats() # use for all private chats
)
# check command
cmd = bot.get_my_commands(scope=None, language_code=None)
print([c.to_json() for c in cmd])

42
examples/timer_bot.py Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/python
# This is a simple bot with schedule timer
# https://schedule.readthedocs.io
import time, threading, schedule
from telebot import TeleBot
API_TOKEN = '<api_token>'
bot = TeleBot(API_TOKEN)
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message, "Hi! Use /set <seconds> to set a timer")
def beep(chat_id) -> None:
"""Send the beep message."""
bot.send_message(chat_id, text='Beep!')
@bot.message_handler(commands=['set'])
def set_timer(message):
args = message.text.split()
if len(args) > 1 and args[1].isdigit():
sec = int(args[1])
schedule.every(sec).seconds.do(beep, message.chat.id).tag(message.chat.id)
else:
bot.reply_to(message, 'Usage: /set <seconds>')
@bot.message_handler(commands=['unset'])
def unset_timer(message):
schedule.clear(message.chat.id)
if __name__ == '__main__':
threading.Thread(target=bot.infinity_polling, name='bot_infinity_polling', daemon=True).start()
while True:
schedule.run_pending()
time.sleep(1)

View File

@ -50,5 +50,12 @@ There are 5 examples in this directory using different libraries:
* **Cons:**
* Twisted is low-level, which may be good or bad depending on use case
* Considerable learning curve - reading docs is a must.
* **FastAPI(0.70.1):** *webhook_fastapi_echo_bot.py*
* **Pros:**
* Can be written for both sync and async
* Good documentation
* **Cons:**
* Requires python 3.6+
*Latest update of this document: 2020-12-17*
*Latest update of this document: 01-03-2022

View File

@ -0,0 +1,45 @@
#!/usr/bin/python
# This is a simple echo bot using the decorator mechanism.
# It echoes any incoming text messages.
# Example on built-in function to receive and process webhooks.
import telebot
API_TOKEN = 'TOKEN'
bot = telebot.TeleBot(API_TOKEN)
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
DOMAIN = '1.2.3.4' # either domain, or ip address of vps
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
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)
def echo_message(message):
bot.reply_to(message, message.text)
bot.run_webhooks(
listen=DOMAIN,
certificate=WEBHOOK_SSL_CERT,
certificate_key=WEBHOOK_SSL_PRIV
)

View File

@ -0,0 +1,79 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This is a simple echo bot using decorators and webhook with fastapi
# It echoes any incoming text messages and does not use the polling method.
import logging
import fastapi
import telebot
API_TOKEN = 'TOKEN'
WEBHOOK_HOST = '<ip/domain>'
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
# Quick'n'dirty SSL certificate generation:
#
# openssl genrsa -out webhook_pkey.pem 2048
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
#
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
# with the same value in you put in WEBHOOK_HOST
WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.INFO)
bot = telebot.TeleBot(API_TOKEN)
app = fastapi.FastAPI()
# Process webhook calls
@app.post(f'/{API_TOKEN}/')
def process_webhook(update: dict):
if update:
update = telebot.types.Update.de_json(update)
bot.process_new_updates([update])
else:
return
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message,
("Hi there, I am EchoBot.\n"
"I am here to echo your kind words back to you."))
# Handle all other messages
@bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
import uvicorn
uvicorn.run(
app,
host=WEBHOOK_LISTEN,
port=WEBHOOK_PORT,
ssl_certfile=WEBHOOK_SSL_CERT,
ssl_keyfile=WEBHOOK_SSL_PRIV
)

View File

@ -5,6 +5,7 @@
# Documenation to Tornado: http://tornadoweb.org
import signal
from typing import Optional, Awaitable
import tornado.httpserver
import tornado.ioloop
@ -33,12 +34,18 @@ bot = telebot.TeleBot(API_TOKEN)
class Root(tornado.web.RequestHandler):
def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
pass
def get(self):
self.write("Hi! This is webhook example!")
self.finish()
class webhook_serv(tornado.web.RequestHandler):
class WebhookServ(tornado.web.RequestHandler):
def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
pass
def get(self):
self.write("What are you doing here?")
self.finish()
@ -93,7 +100,7 @@ tornado.options.parse_command_line()
signal.signal(signal.SIGINT, signal_handler)
application = tornado.web.Application([
(r"/", Root),
(r"/" + WEBHOOK_SECRET, webhook_serv)
(r"/" + WEBHOOK_SECRET, WebhookServ)
])
http_server = tornado.httpserver.HTTPServer(application, ssl_options={

View File

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

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
from setuptools import setup
from setuptools import setup, find_packages
from io import open
import re
@ -19,19 +19,24 @@ setup(name='pyTelegramBotAPI',
author='eternnoir',
author_email='eternnoir@gmail.com',
url='https://github.com/eternnoir/pyTelegramBotAPI',
packages=['telebot'],
packages = find_packages(exclude = ['tests', 'examples']),
license='GPL2',
keywords='telegram bot api tools',
install_requires=['requests'],
extras_require={
'json': 'ujson',
'PIL': 'Pillow',
'redis': 'redis>=3.4.1'
'redis': 'redis>=3.4.1',
'aioredis': 'aioredis',
'aiohttp': 'aiohttp',
'fastapi': 'fastapi',
'uvicorn': 'uvicorn',
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 3',
'Environment :: Console',
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
]
],
)

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More