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

Compare commits

...

124 Commits
4.0.1 ... 4.2.1

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

Added RETRY_ENGINE = 2 based on native "requests" retry mechanism.
2021-11-07 23:02:23 +03:00
695c699893 Merge pull request #1353 from coder2020official/master
Bot API 5.4 features
2021-11-06 18:02:01 +03:00
bf96f13296 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-11-06 19:59:59 +05:00
62b1ec04ab Update __init__.py 2021-11-06 19:59:44 +05:00
e412d2f084 Update README.md 2021-11-06 19:56:10 +05:00
8003ff5e59 Delete states.pkl 2021-11-06 19:51:29 +05:00
becce1f580 Update apihelper.py 2021-11-06 19:51:05 +05:00
3a6073e3a0 Update custom_states.py 2021-11-06 13:08:49 +05:00
fc347ae166 Update custom_states.py 2021-11-06 13:06:43 +05:00
8dcfa0c282 Little fix for states 2021-11-06 12:52:41 +05:00
6808ab3ebe Update test_types.py 2021-11-06 12:42:48 +05:00
31097c5380 Update test_types.py 2021-11-06 12:34:49 +05:00
d49c57699e Tests 2021-11-06 12:27:19 +05:00
ed6616e4c7 Bot API 5.4 2021-11-06 12:21:02 +05:00
953e2286b8 Bot API 5.4 2021-11-06 12:15:28 +05:00
06c8782127 Little update
Allowed other handlers, checked methods and other things
2021-11-05 23:22:03 +05:00
2623fa362c Merge pull request #1350 from Badiboy/master
Custom request sender
2021-11-03 18:50:25 +03:00
4a274ba440 Custom request sender
Added apihelper.CUSTOM_REQUEST_SENDER option. It allows to substitute requests.request to your own function.
2021-11-03 18:48:46 +03:00
bfef7e1ce2 Merge pull request #1349 from barbax7/antiflood
Antiflood
2021-11-03 17:38:32 +03:00
558b37b1c3 New antiflood function 2021-11-03 15:30:10 +01:00
d1c26f82aa Merge pull request #1346 from DevAdvik/patch-1
Added A New Bot - @ETHGasFeeTrackerBot
2021-10-24 19:09:04 +03:00
1a351bc8c7 Added A New Bot 2021-10-24 20:38:15 +05:30
27546daad9 Merge pull request #1345 from resinprotein2333/patch-1
Update README.md
2021-10-24 11:48:02 +03:00
bb58d3fead Update README.md
Add my bot into the bot list
2021-10-24 16:45:49 +08:00
099d638a7e Merge pull request #1338 from barbax7/exceptions
Added description of the ApiTelegramException as attribute of the class
2021-10-17 11:29:44 +03:00
5fb48e68a0 Added description of the ApiTelegramException as attribute of the class 2021-10-16 17:45:15 +02:00
b979c2fa1f Merge pull request #1336 from coder2020official/master
File support for states
2021-10-14 17:23:43 +03:00
b6625baec6 Update __init__.py 2021-10-13 19:02:17 +05:00
98044d6faa File support for states
File support. Now states can be saved in pickle file
2021-10-13 18:34:36 +05:00
2113846567 Merge pull request #1330 from Badiboy/master
Bump version 4.1.1
2021-10-09 22:31:59 +03:00
5c9d4edca9 Bump version 4.1.1 2021-10-09 22:31:34 +03:00
4dce9340b0 Merge pull request #1326 from TrevorWinstral/master
Added my bots to README
2021-10-08 13:45:24 +03:00
bf79e8341e Moved bots to bottom of list 2021-10-08 10:39:59 +00:00
dadfdc2f13 Updated README with my bots 2021-10-06 14:15:30 +02:00
585f627e1f Updated README with my bots 2021-10-06 14:14:05 +02:00
eead303d47 Updated README with my bots 2021-10-06 14:13:05 +02:00
14cc15c711 Merge pull request #1325 from coder2020official/master
Critical fix
2021-10-01 21:47:50 +03:00
bf8736e17e Critical fix 2021-10-01 23:29:59 +05:00
5014ca2459 Merge pull request #1324 from coder2020official/master
Update of state handlers
2021-10-01 14:55:09 +03:00
f337abe06e Update __init__.py 2021-10-01 16:09:20 +05:00
ff35f25211 Update __init__.py 2021-10-01 16:08:01 +05:00
2e4280a947 Update of state handlers
No need to create state handlers
2021-10-01 15:56:54 +05:00
4a6b5b3d28 Merge pull request #1322 from Badiboy/master
Bugfix with one_time_keyboard = False
2021-09-30 11:57:54 +03:00
a28af3903d Bugfix with one_time_keyboard = False 2021-09-30 11:56:36 +03:00
d1d5b9effb Merge pull request #1320 from Badiboy/master
polling should leave our world. :)
2021-09-28 19:18:32 +03:00
062fababf2 polling should leave our world. :) 2021-09-28 19:17:09 +03:00
946afcc3c1 Merge pull request #1317 from coder2020official/master
Update handler_backends.py
2021-09-25 21:35:25 +03:00
6e502cd1c6 Merge branch 'master' into master 2021-09-25 23:29:50 +05:00
0a7f897f05 Merge pull request #1318 from Badiboy/master
States minor update
2021-09-25 21:15:52 +03:00
b35f17124f States minor update 2021-09-25 21:15:24 +03:00
44b44ac2c5 Optimization 2021-09-25 23:05:36 +05:00
39e875c1ea Update handler_backends.py 2021-09-25 22:49:32 +05:00
be7317cc86 Merge pull request #1315 from coder2020official/master
States, New filter, and more
2021-09-25 20:43:33 +03:00
e1c33a1de6 Merge pull request #1316 from Badiboy/master
Release 4.1.0
2021-09-25 20:34:05 +03:00
8149551a15 Release 4.1.0 2021-09-25 20:33:32 +03:00
ab648ef3db Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2021-09-25 22:19:09 +05:00
e721910c0c Update __init__.py 2021-09-25 22:19:07 +05:00
9287eced49 Update a typo 2021-09-25 21:10:29 +05:00
967b94b14f Update handler_backends.py 2021-09-25 20:27:03 +05:00
2df6f00ba5 Optimization
Optimized code, added filters support
2021-09-25 18:22:54 +05:00
beb4f8df44 Update register_handler.py 2021-09-25 17:15:33 +05:00
92ac5a4166 States, and some minor improvements 2021-09-25 17:12:32 +05:00
716323e56a Register_XXX_Handler 2021-09-22 22:46:19 +05:00
cd92d95f91 Create admin_filter_example.py 2021-09-22 22:37:57 +05:00
9c86ed623d Update custom_filters.py 2021-09-22 22:37:18 +05:00
c6ff9b07df Merge pull request #1311 from SwissCorePy/master
added property `user` to TeleBot class
2021-09-20 15:40:14 +03:00
38cc96d0f3 added property user to TeleBot class
Added property `user` to TeleBot class. The idea is to have easy access to the user object representing the bot without doing an API call every time.
2021-09-20 14:31:00 +02:00
82518d8664 Merge pull request #1309 from bim-ba/master
Added new example: anonymous chat-bot
2021-09-19 17:50:51 +03:00
aba2a9e179 Improve readabilty of "elif-zone" 2021-09-19 17:41:07 +03:00
c5c4d081ea Added new example: anonymous chat-bot 2021-09-18 19:46:53 +03:00
f854163626 Merge pull request #1308 from coder2020official/master
Added example and editd description.
2021-09-14 13:08:45 +03:00
fc31a2d466 Update custom_filters.py 2021-09-14 15:02:54 +05:00
86a0a8cd68 Little fixes and example
Fixed is_forwarded custom filter & created example
2021-09-14 15:00:27 +05:00
59 changed files with 6610 additions and 429 deletions

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ var/
.idea/
venv/
.venv/
# PyInstaller
# Usually these files are written by a python script from a template

View File

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

149
README.md
View File

@ -6,13 +6,14 @@
# <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">Supports both sync and async ways.</p>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#june-25-2021">5.3</a>!
## <p align="center">Supporting Bot API version: <a href="https://core.telegram.org/bots/api#november-5-2021">5.4</a>!
## Contents
* [Getting started.](#getting-started)
* [Getting started](#getting-started)
* [Writing your first bot](#writing-your-first-bot)
* [Prerequisites](#prerequisites)
* [A simple echo bot](#a-simple-echo-bot)
@ -31,6 +32,7 @@
* [Poll Answer Handler](#poll-answer-handler)
* [My Chat Member Handler](#my-chat-member-handler)
* [Chat Member Handler](#chat-member-handler)
* [Chat Join request handler](#chat-join-request-handler)
* [Inline Mode](#inline-mode)
* [Inline handler](#inline-handler)
* [Chosen Inline handler](#chosen-inline-handler)
@ -42,14 +44,16 @@
* [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)
* [Using web hooks](#using-web-hooks)
* [Logging](#logging)
* [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)
@ -57,9 +61,9 @@
* [More examples](#more-examples)
* [Bots using this API](#bots-using-this-api)
## Getting started.
## 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)*:
@ -121,13 +125,13 @@ This one echoes all incoming text messages back to the sender. It uses a lambda
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
```python
bot.polling()
bot.infinity_polling()
```
Alright, that's it! Our source file now looks like this:
```python
import telebot
bot = telebot.TeleBot("TOKEN")
bot = telebot.TeleBot("YOUR_BOT_TOKEN")
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
@ -137,7 +141,7 @@ def send_welcome(message):
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
bot.infinity_polling()
```
To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages.
@ -180,8 +184,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:
@ -242,7 +246,7 @@ Handle edited channel post messages
Handle callback queries
```python
@bot.callback_query_handler(func=lambda call: True)
def test_callback(call): # <- passes a CallbackQuery type object to your function
def test_callback(call): # <- passes a CallbackQuery type object to your function
logger.info(call)
```
@ -271,6 +275,10 @@ Handle updates of a chat member's status in a chat
`@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
*Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`*
#### Chat Join Request Handler
Handle chat join requests using:
`@bot.chat_join_request_handler() # <- passes ChatInviteLink type object to your function`
### Inline Mode
More information about [Inline mode](https://core.telegram.org/bots/inline).
@ -368,8 +376,8 @@ bot.add_custom_filter(IsAdmin())
# Now, you can use it in handler.
@bot.message_handler(is_admin=True)
def admin_of_group(message):
bot.send_message(message.chat.id, 'You are admin of this group'!)
bot.send_message(message.chat.id, 'You are admin of this group!')
```
@ -381,12 +389,10 @@ TOKEN = '<token_string>'
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
# - interval: True/False (default False) - The interval between polling requests
# Note: Editing this parameter harms the bot's response time
# - interval: int (default 0) - The interval between polling requests
# - timeout: integer (default 20) - Timeout in seconds for long polling.
# - allowed_updates: List of Strings (default None) - List of update types to request
tb.polling(none_stop=False, interval=0, timeout=20)
tb.infinity_polling(interval=0, timeout=20)
# getMe
user = tb.get_me()
@ -398,6 +404,7 @@ tb.remove_webhook()
# getUpdates
updates = tb.get_updates()
# or
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
# sendMessage
@ -550,26 +557,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:
@ -614,7 +621,7 @@ def handle_messages(messages):
bot.reply_to(message, 'Hi')
bot.set_update_listener(handle_messages)
bot.polling()
bot.infinity_polling()
```
### Using web hooks
@ -623,7 +630,6 @@ When using webhooks telegram sends one Update per call, for processing it you sh
There are some examples using webhooks in the [examples/webhook_examples](examples/webhook_examples) directory.
### Logging
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
@ -635,7 +641,6 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
```
### Proxy
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
```python
@ -650,9 +655,37 @@ If you want to use socket5 proxy you need install dependency `pip install reques
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
```
### Testing
You can disable or change the interaction with real Telegram server by using
```python
apihelper.CUSTOM_REQUEST_SENDER = your_handler
```
parameter. You can pass there your own function that will be called instead of _requests.request_.
For example:
```python
def custom_sender(method, url, **kwargs):
print("custom_sender. method: {}, url: {}, params: {}".format(method, url, kwargs.get("params")))
result = util.CustomRequestResponse('{"ok":true,"result":{"message_id": 1, "date": 1, "chat": {"id": 1, "type": "private"}}}')
return result
```
Then you can use API and proceed requests in your handler code.
```python
apihelper.CUSTOM_REQUEST_SENDER = custom_sender
tb = TeleBot("test")
res = tb.send_message(123, "Test")
```
Result will be:
`custom_sender. method: post, url: https://api.telegram.org/botololo/sendMessage, params: {'chat_id': '123', 'text': 'Test'}`
## API conformance
* ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021)
* [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMemberXXX classes are full copies of ChatMember
* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021)
* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021)
@ -681,6 +714,52 @@ apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
* ✔ [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
bot = AsyncTeleBot('TOKEN')
# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
async def send_welcome(message):
await bot.reply_to(message, """\
Hi there, I am EchoBot.
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
""")
# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
@bot.message_handler(func=lambda message: True)
async def echo_message(message):
await bot.reply_to(message, message.text)
bot.polling()
```
As you can see here, keywords are await and async.
### Why should I use async?
Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other.
### Differences in AsyncTeleBot
AsyncTeleBot has different middlewares. See example on [middlewares](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot/middleware)
### Examples
See more examples in our [examples](https://github.com/coder2020official/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder
## F.A.Q.
### How can I distinguish a User and a GroupChat in message.chat?
@ -690,7 +769,7 @@ Telegram Bot API support new type Chat for message.chat.
-
```python
if message.chat.type == "private":
# private chat message
# private chat message
if message.chat.type == "group":
# group chat message
@ -760,5 +839,11 @@ Get help. Discuss. Chat.
* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent.
* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems)
* [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings)
* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user
* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded
* [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities.
* [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI
* [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet.
* [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development.
**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.**

View File

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

117
examples/anonymous_bot.py Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,4 +30,4 @@ def my_chat_m(message: types.ChatMemberUpdated):
@bot.message_handler(content_types=util.content_type_service)
def delall(message: types.Message):
bot.delete_message(message.chat.id,message.message_id)
bot.polling(allowed_updates=util.update_types)
bot.infinity_polling(allowed_updates=util.update_types)

View File

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

View File

@ -39,4 +39,4 @@ def bye_user(message):
bot.add_custom_filter(MainFilter())
bot.add_custom_filter(IsAdmin())
bot.polling(skip_pending=True,non_stop=True) # Skip old updates
bot.infinity_polling(skip_pending=True) # Skip old updates

View File

@ -13,10 +13,7 @@ def admin_rep(message):
def not_admin(message):
bot.send_message(message.chat.id, "You are not allowed to use this command")
# Do not forget to register
bot.add_custom_filter(custom_filters.ChatFilter())
bot.polling(non_stop=True)
bot.infinity_polling()

View File

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

View File

@ -18,4 +18,4 @@ def text_filter(message):
bot.add_custom_filter(custom_filters.TextMatchFilter())
bot.add_custom_filter(custom_filters.TextStartsFilter())
bot.polling(non_stop=True)
bot.infinity_polling()

74
examples/custom_states.py Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,4 +10,4 @@ def send_welcome(message):
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling(skip_pending=True)# Skip pending skips old updates
bot.infinity_polling(skip_pending=True)# Skip pending skips old updates

View File

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

View File

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

View File

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

View File

@ -12,10 +12,11 @@ from typing import Any, Callable, List, Optional, Union
# this imports are used to avoid circular import error
import telebot.util
import telebot.types
from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter
logger = logging.getLogger('TeleBot')
formatter = logging.Formatter(
'%(asctime)s (%(filename)s:%(lineno)d %(threadName)s) %(levelname)s - %(name)s: "%(message)s"'
)
@ -27,7 +28,9 @@ logger.addHandler(console_output_handler)
logger.setLevel(logging.ERROR)
from telebot import apihelper, util, types
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend, StateMemory, StateFile
from telebot.custom_filters import SimpleCustomFilter, AdvancedCustomFilter
REPLY_MARKUP_TYPES = Union[
@ -35,6 +38,7 @@ REPLY_MARKUP_TYPES = Union[
types.ReplyKeyboardRemove, types.ForceReply]
"""
Module : telebot
"""
@ -185,7 +189,12 @@ class TeleBot:
self.poll_answer_handlers = []
self.my_chat_member_handlers = []
self.chat_member_handlers = []
self.chat_join_request_handlers = []
self.custom_filters = {}
self.state_handlers = []
self.current_states = StateMemory()
if apihelper.ENABLE_MIDDLEWARE:
self.typed_middleware_handlers = {
@ -201,13 +210,24 @@ class TeleBot:
'poll': [],
'poll_answer': [],
'my_chat_member': [],
'chat_member': []
'chat_member': [],
'chat_join_request': []
}
self.default_middleware_handlers = []
self.threaded = threaded
if self.threaded:
self.worker_pool = util.ThreadPool(num_threads=num_threads)
@property
def user(self) -> types.User:
"""
The User object representing this bot.
Equivalent to bot.get_me() but the result is cached so only one API call is needed
"""
if not hasattr(self, "_user"):
self._user = types.User.de_json(self.get_me())
return self._user
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"):
"""
@ -224,6 +244,16 @@ class TeleBot:
"""
self.next_step_backend = FileHandlerBackend(self.next_step_backend.handlers, filename, delay)
def enable_saving_states(self, filename="./.state-save/states.pkl"):
"""
Enable saving states (by default saving disabled)
:param filename: Filename of saving file
"""
self.current_states = StateFile(filename=filename)
self.current_states._create_dir()
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
"""
Enable saving reply handlers (by default saving disable)
@ -332,7 +362,7 @@ class TeleBot:
"""
return apihelper.delete_webhook(self.token, drop_pending_updates, timeout)
def get_webhook_info(self, timeout=None):
def get_webhook_info(self, timeout: Optional[int]=None):
"""
Use this method to get current webhook status. Requires no parameters.
If the bot is using getUpdates, will return an object with the url field empty.
@ -401,6 +431,7 @@ class TeleBot:
new_poll_answers = None
new_my_chat_members = None
new_chat_members = None
chat_join_request = None
for update in updates:
if apihelper.ENABLE_MIDDLEWARE:
@ -455,6 +486,9 @@ class TeleBot:
if update.chat_member:
if new_chat_members is None: new_chat_members = []
new_chat_members.append(update.chat_member)
if update.chat_join_request:
if chat_join_request is None: chat_join_request = []
chat_join_request.append(update.chat_join_request)
if new_messages:
self.process_new_messages(new_messages)
@ -482,6 +516,9 @@ class TeleBot:
self.process_new_my_chat_member(new_my_chat_members)
if new_chat_members:
self.process_new_chat_member(new_chat_members)
if chat_join_request:
self.process_new_chat_join_request(chat_join_request)
def process_new_messages(self, new_messages):
self._notify_next_handlers(new_messages)
@ -525,6 +562,9 @@ class TeleBot:
def process_new_chat_member(self, chat_members):
self._notify_command_handlers(self.chat_member_handlers, chat_members)
def process_new_chat_join_request(self, chat_join_request):
self._notify_command_handlers(self.chat_join_request_handlers, chat_join_request)
def process_middlewares(self, update):
for update_type, middlewares in self.typed_middleware_handlers.items():
if getattr(update, update_type) is not None:
@ -549,8 +589,9 @@ class TeleBot:
for listener in self.update_listener:
self._exec_task(listener, new_messages)
def infinity_polling(self, timeout=20, skip_pending=False, long_polling_timeout=20, logger_level=logging.ERROR,
allowed_updates=None, *args, **kwargs):
def infinity_polling(self, timeout: int=20, skip_pending: bool=False, long_polling_timeout: int=20, logger_level=logging.ERROR,
allowed_updates: Optional[List[str]]=None, *args, **kwargs):
"""
Wrap polling with infinite loop and exception handling to avoid bot stops polling.
@ -659,7 +700,10 @@ class TeleBot:
# self.worker_pool.clear_exceptions()
logger.info("Waiting for {0} seconds until retry".format(error_interval))
time.sleep(error_interval)
error_interval *= 2
if error_interval * 2 < 60:
error_interval *= 2
else:
error_interval = 60
else:
# polling_thread.clear_exceptions()
# self.worker_pool.clear_exceptions()
@ -1638,41 +1682,49 @@ class TeleBot:
return apihelper.set_chat_permissions(self.token, chat_id, permissions)
def create_chat_invite_link(
self, chat_id: Union[int, str],
self, chat_id: Union[int, str],
name: Optional[str]=None,
expire_date: Optional[Union[int, datetime]]=None,
member_limit: Optional[int]=None) -> types.ChatInviteLink:
member_limit: Optional[int]=None,
creates_join_request: Optional[bool]=None) -> types.ChatInviteLink:
"""
Use this method to create an additional invite link for a chat.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
:param chat_id: Id: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param name: Invite link name; 0-32 characters
:param expire_date: Point in time (Unix timestamp) when the link will expire
:param member_limit: Maximum number of users that can be members of the chat simultaneously
:param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified
:return:
"""
return types.ChatInviteLink.de_json(
apihelper.create_chat_invite_link(self.token, chat_id, expire_date, member_limit)
apihelper.create_chat_invite_link(self.token, chat_id, name, expire_date, member_limit, creates_join_request)
)
def edit_chat_invite_link(
self, chat_id: Union[int, str], invite_link: str,
expire_date: Optional[Union[int, datetime]]=None,
member_limit: Optional[int]=None) -> types.ChatInviteLink:
self, chat_id: Union[int, str],
invite_link: Optional[str] = None,
name: Optional[str]=None,
expire_date: Optional[Union[int, datetime]]=None,
member_limit: Optional[int]=None,
creates_join_request: Optional[bool]=None) -> types.ChatInviteLink:
"""
Use this method to edit a non-primary invite link created by the bot.
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
:param invite_link:
:param chat_id: Id: Unique identifier for the target chat or username of the target channel
(in the format @channelusername)
:param name: Invite link name; 0-32 characters
:param invite_link: The invite link to edit
:param expire_date: Point in time (Unix timestamp) when the link will expire
:param member_limit: Maximum number of users that can be members of the chat simultaneously
:param creates_join_request: True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified
:return:
"""
return types.ChatInviteLink.de_json(
apihelper.edit_chat_invite_link(self.token, chat_id, invite_link, expire_date, member_limit)
apihelper.edit_chat_invite_link(self.token, chat_id, name, invite_link, expire_date, member_limit, creates_join_request)
)
def revoke_chat_invite_link(
@ -1702,6 +1754,32 @@ class TeleBot:
"""
return apihelper.export_chat_invite_link(self.token, chat_id)
def approve_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool:
"""
Use this method to approve a chat join request.
The bot must be an administrator in the chat for this to work and must have
the can_invite_users administrator right. Returns True on success.
:param chat_id: Unique identifier for the target chat or username of the target supergroup
(in the format @supergroupusername)
:param user_id: Unique identifier of the target user
:return: True on success.
"""
return apihelper.approve_chat_join_request(self.token, chat_id, user_id)
def decline_chat_join_request(self, chat_id: Union[str, int], user_id: Union[int, str]) -> bool:
"""
Use this method to decline a chat join request.
The bot must be an administrator in the chat for this to work and must have
the can_invite_users administrator right. Returns True on success.
:param chat_id: Unique identifier for the target chat or username of the target supergroup
(in the format @supergroupusername)
:param user_id: Unique identifier of the target user
:return: True on success.
"""
return apihelper.decline_chat_join_request(self.token, chat_id, user_id)
def set_chat_photo(self, chat_id: Union[int, str], photo: Any) -> bool:
"""
Use this method to set a new profile photo for the chat. Photos can't be changed for private chats.
@ -2352,6 +2430,41 @@ class TeleBot:
chat_id = message.chat.id
self.register_next_step_handler_by_chat_id(chat_id, callback, *args, **kwargs)
def set_state(self, chat_id: int, state: Union[int, str]):
"""
Sets a new state of a user.
:param chat_id:
:param state: new state. can be string or integer.
"""
self.current_states.add_state(chat_id, state)
def delete_state(self, chat_id: int):
"""
Delete the current state of a user.
:param chat_id:
:return:
"""
self.current_states.delete_state(chat_id)
def retrieve_data(self, chat_id: int):
return self.current_states.retrieve_data(chat_id)
def get_state(self, chat_id: int):
"""
Get current state of a user.
:param chat_id:
:return: state of a user
"""
return self.current_states.current_state(chat_id)
def add_data(self, chat_id: int, **kwargs):
"""
Add data to states.
:param chat_id:
"""
for key, value in kwargs.items():
self.current_states._add_data(chat_id, key, value)
def register_next_step_handler_by_chat_id(
self, chat_id: Union[int, str], callback: Callable, *args, **kwargs) -> None:
"""
@ -2416,6 +2529,7 @@ class TeleBot:
if need_pop:
new_messages.pop(i) # removing message that was detected with next_step_handler
@staticmethod
def _build_handler_dict(handler, **filters):
"""
@ -2651,6 +2765,7 @@ class TeleBot:
**kwargs)
self.add_edited_message_handler(handler_dict)
def channel_post_handler(self, commands=None, regexp=None, func=None, content_types=None, **kwargs):
"""
Channel post handler decorator
@ -3082,6 +3197,39 @@ class TeleBot:
handler_dict = self._build_handler_dict(callback, func=func, **kwargs)
self.add_chat_member_handler(handler_dict)
def chat_join_request_handler(self, func=None, **kwargs):
"""
chat_join_request handler
:param func:
:param kwargs:
:return:
"""
def decorator(handler):
handler_dict = self._build_handler_dict(handler, func=func, **kwargs)
self.add_chat_join_request_handler(handler_dict)
return handler
return decorator
def add_chat_join_request_handler(self, handler_dict):
"""
Adds a chat_join_request handler
:param handler_dict:
:return:
"""
self.chat_join_request_handlers.append(handler_dict)
def register_chat_join_request_handler(self, callback, func=None, **kwargs):
"""
Registers chat join request handler.
:param callback: function to be called
:param func:
:return: decorated function
"""
handler_dict = self._build_handler_dict(callback, func=func, **kwargs)
self.add_chat_join_request_handler(handler_dict)
def _test_message_handler(self, message_handler, message):
"""
Test message handler
@ -3164,338 +3312,6 @@ class TeleBot:
break
class AsyncTeleBot(TeleBot):
def __init__(self, *args, **kwargs):
TeleBot.__init__(self, *args, **kwargs)
# I'm not sure if `get_updates` should be added here too
@util.async_dec()
def enable_save_next_step_handlers(self, delay=120, filename="./.handler-saves/step.save"):
return TeleBot.enable_save_next_step_handlers(self, delay, filename)
@util.async_dec()
def enable_save_reply_handlers(self, delay=120, filename="./.handler-saves/reply.save"):
return TeleBot.enable_save_reply_handlers(self, delay, filename)
@util.async_dec()
def disable_save_next_step_handlers(self):
return TeleBot.disable_save_next_step_handlers(self)
@util.async_dec()
def disable_save_reply_handlers(self):
return TeleBot.enable_save_reply_handlers(self)
@util.async_dec()
def load_next_step_handlers(self, filename="./.handler-saves/step.save", del_file_after_loading=True):
return TeleBot.load_next_step_handlers(self, filename, del_file_after_loading)
@util.async_dec()
def load_reply_handlers(self, filename="./.handler-saves/reply.save", del_file_after_loading=True):
return TeleBot.load_reply_handlers(self, filename, del_file_after_loading)
@util.async_dec()
def get_me(self):
return TeleBot.get_me(self)
@util.async_dec()
def log_out(self):
return TeleBot.log_out(self)
@util.async_dec()
def close(self):
return TeleBot.close(self)
@util.async_dec()
def get_my_commands(self, *args, **kwargs): # needed args because new scope and language_code
return TeleBot.get_my_commands(self, *args, **kwargs)
@util.async_dec()
def set_my_commands(self, *args, **kwargs):
return TeleBot.set_my_commands(self, *args, **kwargs)
@util.async_dec()
def delete_my_commands(self, *args, **kwargs):
return TeleBot.delete_my_commands(self, *args, **kwargs)
@util.async_dec()
def get_file(self, *args):
return TeleBot.get_file(self, *args)
@util.async_dec()
def download_file(self, *args):
return TeleBot.download_file(self, *args)
@util.async_dec()
def get_user_profile_photos(self, *args, **kwargs):
return TeleBot.get_user_profile_photos(self, *args, **kwargs)
@util.async_dec()
def get_chat(self, *args):
return TeleBot.get_chat(self, *args)
@util.async_dec()
def leave_chat(self, *args):
return TeleBot.leave_chat(self, *args)
@util.async_dec()
def get_chat_administrators(self, *args):
return TeleBot.get_chat_administrators(self, *args)
@util.async_dec()
def get_chat_members_count(self, *args):
logger.info('get_chat_members_count is deprecated. Use get_chat_member_count instead')
return TeleBot.get_chat_member_count(self, *args)
@util.async_dec()
def get_chat_member_count(self, *args):
return TeleBot.get_chat_member_count(self, *args)
@util.async_dec()
def set_chat_sticker_set(self, *args):
return TeleBot.set_chat_sticker_set(self, *args)
@util.async_dec()
def delete_chat_sticker_set(self, *args):
return TeleBot.delete_chat_sticker_set(self, *args)
@util.async_dec()
def get_chat_member(self, *args):
return TeleBot.get_chat_member(self, *args)
@util.async_dec()
def send_message(self, *args, **kwargs):
return TeleBot.send_message(self, *args, **kwargs)
@util.async_dec()
def send_dice(self, *args, **kwargs):
return TeleBot.send_dice(self, *args, **kwargs)
@util.async_dec()
def send_animation(self, *args, **kwargs):
return TeleBot.send_animation(self, *args, **kwargs)
@util.async_dec()
def forward_message(self, *args, **kwargs):
return TeleBot.forward_message(self, *args, **kwargs)
@util.async_dec()
def copy_message(self, *args, **kwargs):
return TeleBot.copy_message(self, *args, **kwargs)
@util.async_dec()
def delete_message(self, *args):
return TeleBot.delete_message(self, *args)
@util.async_dec()
def send_photo(self, *args, **kwargs):
return TeleBot.send_photo(self, *args, **kwargs)
@util.async_dec()
def send_audio(self, *args, **kwargs):
return TeleBot.send_audio(self, *args, **kwargs)
@util.async_dec()
def send_voice(self, *args, **kwargs):
return TeleBot.send_voice(self, *args, **kwargs)
@util.async_dec()
def send_document(self, *args, **kwargs):
return TeleBot.send_document(self, *args, **kwargs)
@util.async_dec()
def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs)
@util.async_dec()
def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs)
@util.async_dec()
def send_video_note(self, *args, **kwargs):
return TeleBot.send_video_note(self, *args, **kwargs)
@util.async_dec()
def send_media_group(self, *args, **kwargs):
return TeleBot.send_media_group(self, *args, **kwargs)
@util.async_dec()
def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs)
@util.async_dec()
def edit_message_live_location(self, *args, **kwargs):
return TeleBot.edit_message_live_location(self, *args, **kwargs)
@util.async_dec()
def stop_message_live_location(self, *args, **kwargs):
return TeleBot.stop_message_live_location(self, *args, **kwargs)
@util.async_dec()
def send_venue(self, *args, **kwargs):
return TeleBot.send_venue(self, *args, **kwargs)
@util.async_dec()
def send_contact(self, *args, **kwargs):
return TeleBot.send_contact(self, *args, **kwargs)
@util.async_dec()
def send_chat_action(self, *args, **kwargs):
return TeleBot.send_chat_action(self, *args, **kwargs)
@util.async_dec()
def kick_chat_member(self, *args, **kwargs):
logger.info('kick_chat_member is deprecated. Use ban_chat_member instead.')
return TeleBot.ban_chat_member(self, *args, **kwargs)
@util.async_dec()
def ban_chat_member(self, *args, **kwargs):
return TeleBot.ban_chat_member(self, *args, **kwargs)
@util.async_dec()
def unban_chat_member(self, *args, **kwargs):
return TeleBot.unban_chat_member(self, *args, **kwargs)
@util.async_dec()
def restrict_chat_member(self, *args, **kwargs):
return TeleBot.restrict_chat_member(self, *args, **kwargs)
@util.async_dec()
def promote_chat_member(self, *args, **kwargs):
return TeleBot.promote_chat_member(self, *args, **kwargs)
@util.async_dec()
def set_chat_administrator_custom_title(self, *args, **kwargs):
return TeleBot.set_chat_administrator_custom_title(self, *args, **kwargs)
@util.async_dec()
def set_chat_permissions(self, *args, **kwargs):
return TeleBot.set_chat_permissions(self, *args, **kwargs)
@util.async_dec()
def create_chat_invite_link(self, *args, **kwargs):
return TeleBot.create_chat_invite_link(self, *args, **kwargs)
@util.async_dec()
def edit_chat_invite_link(self, *args, **kwargs):
return TeleBot.edit_chat_invite_link(self, *args, **kwargs)
@util.async_dec()
def revoke_chat_invite_link(self, *args, **kwargs):
return TeleBot.revoke_chat_invite_link(self, *args, **kwargs)
@util.async_dec()
def export_chat_invite_link(self, *args):
return TeleBot.export_chat_invite_link(self, *args)
@util.async_dec()
def set_chat_photo(self, *args):
return TeleBot.set_chat_photo(self, *args)
@util.async_dec()
def delete_chat_photo(self, *args):
return TeleBot.delete_chat_photo(self, *args)
@util.async_dec()
def set_chat_title(self, *args):
return TeleBot.set_chat_title(self, *args)
@util.async_dec()
def set_chat_description(self, *args):
return TeleBot.set_chat_description(self, *args)
@util.async_dec()
def pin_chat_message(self, *args, **kwargs):
return TeleBot.pin_chat_message(self, *args, **kwargs)
@util.async_dec()
def unpin_chat_message(self, *args):
return TeleBot.unpin_chat_message(self, *args)
@util.async_dec()
def unpin_all_chat_messages(self, *args):
return TeleBot.unpin_all_chat_messages(self, *args)
@util.async_dec()
def edit_message_text(self, *args, **kwargs):
return TeleBot.edit_message_text(self, *args, **kwargs)
@util.async_dec()
def edit_message_media(self, *args, **kwargs):
return TeleBot.edit_message_media(self, *args, **kwargs)
@util.async_dec()
def edit_message_reply_markup(self, *args, **kwargs):
return TeleBot.edit_message_reply_markup(self, *args, **kwargs)
@util.async_dec()
def send_game(self, *args, **kwargs):
return TeleBot.send_game(self, *args, **kwargs)
@util.async_dec()
def set_game_score(self, *args, **kwargs):
return TeleBot.set_game_score(self, *args, **kwargs)
@util.async_dec()
def get_game_high_scores(self, *args, **kwargs):
return TeleBot.get_game_high_scores(self, *args, **kwargs)
@util.async_dec()
def send_invoice(self, *args, **kwargs):
return TeleBot.send_invoice(self, *args, **kwargs)
@util.async_dec()
def answer_shipping_query(self, *args, **kwargs):
return TeleBot.answer_shipping_query(self, *args, **kwargs)
@util.async_dec()
def answer_pre_checkout_query(self, *args, **kwargs):
return TeleBot.answer_pre_checkout_query(self, *args, **kwargs)
@util.async_dec()
def edit_message_caption(self, *args, **kwargs):
return TeleBot.edit_message_caption(self, *args, **kwargs)
@util.async_dec()
def answer_inline_query(self, *args, **kwargs):
return TeleBot.answer_inline_query(self, *args, **kwargs)
@util.async_dec()
def answer_callback_query(self, *args, **kwargs):
return TeleBot.answer_callback_query(self, *args, **kwargs)
@util.async_dec()
def get_sticker_set(self, *args, **kwargs):
return TeleBot.get_sticker_set(self, *args, **kwargs)
@util.async_dec()
def upload_sticker_file(self, *args, **kwargs):
return TeleBot.upload_sticker_file(self, *args, **kwargs)
@util.async_dec()
def create_new_sticker_set(self, *args, **kwargs):
return TeleBot.create_new_sticker_set(self, *args, **kwargs)
@util.async_dec()
def add_sticker_to_set(self, *args, **kwargs):
return TeleBot.add_sticker_to_set(self, *args, **kwargs)
@util.async_dec()
def set_sticker_position_in_set(self, *args, **kwargs):
return TeleBot.set_sticker_position_in_set(self, *args, **kwargs)
@util.async_dec()
def delete_sticker_from_set(self, *args, **kwargs):
return TeleBot.delete_sticker_from_set(self, *args, **kwargs)
@util.async_dec()
def set_sticker_set_thumb(self, *args, **kwargs):
return TeleBot.set_sticker_set_thumb(self, *args, **kwargs)
@util.async_dec()
def send_poll(self, *args, **kwargs):
return TeleBot.send_poll(self, *args, **kwargs)
@util.async_dec()
def stop_poll(self, *args, **kwargs):
return TeleBot.stop_poll(self, *args, **kwargs)

View File

@ -9,6 +9,7 @@ except ImportError:
import requests
from requests.exceptions import HTTPError, ConnectionError, Timeout
from requests.adapters import HTTPAdapter
try:
# noinspection PyUnresolvedReferences
@ -38,12 +39,15 @@ SESSION_TIME_TO_LIVE = 600 # In seconds. None - live forever, 0 - one-time
RETRY_ON_ERROR = False
RETRY_TIMEOUT = 2
MAX_RETRIES = 15
RETRY_ENGINE = 1
CUSTOM_SERIALIZER = None
CUSTOM_REQUEST_SENDER = None
ENABLE_MIDDLEWARE = False
def _get_req_session(reset=False):
if SESSION_TIME_TO_LIVE:
# If session TTL is set - check time passed
@ -105,36 +109,44 @@ def _make_request(token, method_name, method='get', params=None, files=None):
params = params or None # Set params to None if empty
result = None
if RETRY_ON_ERROR:
if RETRY_ON_ERROR and RETRY_ENGINE == 1:
got_result = False
current_try = 0
while not got_result and current_try<MAX_RETRIES-1:
current_try+=1
try:
result = _get_req_session().request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
got_result = True
except HTTPError:
logger.debug("HTTP Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
except ConnectionError:
logger.debug("Connection Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
except Timeout:
logger.debug("Timeout Error on {0} method (Try #{1})".format(method_name, current_try))
time.sleep(RETRY_TIMEOUT)
if not got_result:
result = _get_req_session().request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
elif RETRY_ON_ERROR and RETRY_ENGINE == 2:
http = _get_req_session()
retry_strategy = requests.packages.urllib3.util.retry.Retry(
total=MAX_RETRIES,
)
adapter = HTTPAdapter(max_retries=retry_strategy)
for prefix in ('http://', 'https://'):
http.mount(prefix, adapter)
result = http.request(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
elif CUSTOM_REQUEST_SENDER:
result = CUSTOM_REQUEST_SENDER(
method, request_url, params=params, files=files,
timeout=(connect_timeout, read_timeout), proxies=proxy)
else:
result = _get_req_session().request(
method, request_url, params=params, files=files,
@ -966,7 +978,7 @@ def set_chat_permissions(token, chat_id, permissions):
return _make_request(token, method_url, params=payload, method='post')
def create_chat_invite_link(token, chat_id, expire_date, member_limit):
def create_chat_invite_link(token, chat_id, name, expire_date, member_limit, creates_join_request):
method_url = 'createChatInviteLink'
payload = {
'chat_id': chat_id
@ -979,11 +991,15 @@ def create_chat_invite_link(token, chat_id, expire_date, member_limit):
payload['expire_date'] = expire_date
if member_limit:
payload['member_limit'] = member_limit
if creates_join_request is not None:
payload['creates_join_request'] = creates_join_request
if name:
payload['name'] = name
return _make_request(token, method_url, params=payload, method='post')
def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit):
def edit_chat_invite_link(token, chat_id, invite_link, name, expire_date, member_limit, creates_join_request):
method_url = 'editChatInviteLink'
payload = {
'chat_id': chat_id,
@ -998,6 +1014,10 @@ def edit_chat_invite_link(token, chat_id, invite_link, expire_date, member_limit
if member_limit is not None:
payload['member_limit'] = member_limit
if name:
payload['name'] = name
if creates_join_request is not None:
payload['creates_join_request'] = creates_join_request
return _make_request(token, method_url, params=payload, method='post')
@ -1017,6 +1037,24 @@ def export_chat_invite_link(token, chat_id):
return _make_request(token, method_url, params=payload, method='post')
def approve_chat_join_request(token, chat_id, user_id):
method_url = 'approveChatJoinRequest'
payload = {
'chat_id': chat_id,
'user_id': user_id
}
return _make_request(token, method_url, params=payload, method='post')
def decline_chat_join_request(token, chat_id, user_id):
method_url = 'declineChatJoinRequest'
payload = {
'chat_id': chat_id,
'user_id': user_id
}
return _make_request(token, method_url, params=payload, method='post')
def set_chat_photo(token, chat_id, photo):
method_url = 'setChatPhoto'
payload = {'chat_id': chat_id}
@ -1661,4 +1699,5 @@ class ApiTelegramException(ApiException):
result)
self.result_json = result_json
self.error_code = result_json['error_code']
self.description = result_json['description']

2881
telebot/async_telebot.py Normal file

File diff suppressed because it is too large Load Diff

178
telebot/asyncio_filters.py Normal file
View File

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

View File

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

1635
telebot/asyncio_helper.py Normal file

File diff suppressed because it is too large Load Diff

115
telebot/callback_data.py Normal file
View File

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

View File

@ -87,7 +87,7 @@ class ChatFilter(AdvancedCustomFilter):
class ForwardFilter(SimpleCustomFilter):
"""
Check whether message was forwarded.
Check whether message was forwarded from channel or group.
Example:
@ -130,3 +130,47 @@ class LanguageFilter(AdvancedCustomFilter):
if type(text) is list:return message.from_user.language_code in text
else: return message.from_user.language_code == text
class IsAdminFilter(SimpleCustomFilter):
"""
Check whether the user is administrator / owner of the chat.
Example:
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
"""
key = 'is_chat_admin'
def __init__(self, bot):
self._bot = bot
def check(self, message):
return self._bot.get_chat_member(message.chat.id, message.from_user.id).status in ['creator', 'administrator']
class StateFilter(AdvancedCustomFilter):
"""
Filter to check state.
Example:
@bot.message_handler(state=1)
"""
def __init__(self, bot):
self.bot = bot
key = 'state'
def check(self, message, text):
if self.bot.current_states.current_state(message.from_user.id) is False: return False
elif text == '*': return True
elif type(text) is list: return self.bot.current_states.current_state(message.from_user.id) in text
return self.bot.current_states.current_state(message.from_user.id) == text
class IsDigitFilter(SimpleCustomFilter):
"""
Filter to check whether the string is made up of only digits.
Example:
@bot.message_handler(is_digit=True)
"""
key = 'is_digit'
def check(self, message):
return message.text.isdigit()

View File

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

View File

@ -107,13 +107,14 @@ class Update(JsonDeserializable):
poll_answer = PollAnswer.de_json(obj.get('poll_answer'))
my_chat_member = ChatMemberUpdated.de_json(obj.get('my_chat_member'))
chat_member = ChatMemberUpdated.de_json(obj.get('chat_member'))
chat_join_request = ChatJoinRequest.de_json(obj.get('chat_join_request'))
return cls(update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
my_chat_member, chat_member)
my_chat_member, chat_member, chat_join_request)
def __init__(self, update_id, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
my_chat_member, chat_member):
my_chat_member, chat_member, chat_join_request):
self.update_id = update_id
self.message = message
self.edited_message = edited_message
@ -128,6 +129,7 @@ class Update(JsonDeserializable):
self.poll_answer = poll_answer
self.my_chat_member = my_chat_member
self.chat_member = chat_member
self.chat_join_request = chat_join_request
class ChatMemberUpdated(JsonDeserializable):
@ -166,6 +168,22 @@ class ChatMemberUpdated(JsonDeserializable):
dif[key] = [old[key], new[key]]
return dif
class ChatJoinRequest(JsonDeserializable):
@classmethod
def de_json(cls, json_string):
if json_string is None: return None
obj = cls.check_json(json_string)
obj['chat'] = Chat.de_json(obj['chat'])
obj['from_user'] = User.de_json(obj['from'])
obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link'))
return cls(**obj)
def __init__(self, chat, from_user, date, bio=None, invite_link=None, **kwargs):
self.chat = chat
self.from_user = from_user
self.date = date
self.bio = bio
self.invite_link = invite_link
class WebhookInfo(JsonDeserializable):
@classmethod
@ -873,7 +891,7 @@ class ForceReply(JsonSerializable):
def to_json(self):
json_dict = {'force_reply': True}
if self.selective is not None:
json_dict['selective'] = True
json_dict['selective'] = self.selective
if self.input_field_placeholder:
json_dict['input_field_placeholder'] = self.input_field_placeholder
return json.dumps(json_dict)
@ -886,7 +904,7 @@ class ReplyKeyboardRemove(JsonSerializable):
def to_json(self):
json_dict = {'remove_keyboard': True}
if self.selective:
json_dict['selective'] = True
json_dict['selective'] = self.selective
return json.dumps(json_dict)
@ -960,11 +978,11 @@ class ReplyKeyboardMarkup(JsonSerializable):
"""
json_dict = {'keyboard': self.keyboard}
if self.one_time_keyboard is not None:
json_dict['one_time_keyboard'] = True
json_dict['one_time_keyboard'] = self.one_time_keyboard
if self.resize_keyboard is not None:
json_dict['resize_keyboard'] = True
json_dict['resize_keyboard'] = self.resize_keyboard
if self.selective is not None:
json_dict['selective'] = True
json_dict['selective'] = self.selective
if self.input_field_placeholder:
json_dict['input_field_placeholder'] = self.input_field_placeholder
return json.dumps(json_dict)
@ -2752,14 +2770,17 @@ class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable):
obj['creator'] = User.de_json(obj['creator'])
return cls(**obj)
def __init__(self, invite_link, creator, is_primary, is_revoked,
expire_date=None, member_limit=None, **kwargs):
def __init__(self, invite_link, creator, creates_join_request , is_primary, is_revoked,
name=None, expire_date=None, member_limit=None, pending_join_request_count=None, **kwargs):
self.invite_link: str = invite_link
self.creator: User = creator
self.creates_join_request: bool = creates_join_request
self.is_primary: bool = is_primary
self.is_revoked: bool = is_revoked
self.name: str = name
self.expire_date: int = expire_date
self.member_limit: int = member_limit
self.pending_join_request_count: int = pending_join_request_count
def to_json(self):
return json.dumps(self.to_dict())
@ -2769,12 +2790,17 @@ class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable):
"invite_link": self.invite_link,
"creator": self.creator.to_dict(),
"is_primary": self.is_primary,
"is_revoked": self.is_revoked
"is_revoked": self.is_revoked,
"creates_join_request": self.creates_join_request
}
if self.expire_date:
json_dict["expire_date"] = self.expire_date
if self.member_limit:
json_dict["member_limit"] = self.member_limit
if self.pending_join_request_count:
json_dict["pending_join_request_count"] = self.pending_join_request_count
if self.name:
json_dict["name"] = self.name
return json_dict

View File

@ -12,6 +12,11 @@ import logging
from telebot import types
try:
import ujson as json
except ImportError:
import json
try:
# noinspection PyPackageRequirements
from PIL import Image
@ -41,7 +46,7 @@ content_type_service = [
update_types = [
"update_id", "message", "edited_message", "channel_post", "edited_channel_post", "inline_query",
"chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "poll", "poll_answer",
"my_chat_member", "chat_member"
"my_chat_member", "chat_member", "chat_join_request"
]
class WorkerThread(threading.Thread):
@ -165,6 +170,16 @@ class AsyncTask:
return self.result
class CustomRequestResponse():
def __init__(self, json_text, status_code = 200, reason = ""):
self.status_code = status_code
self.text = json_text
self.reason = reason
def json(self):
return json.loads(self.text)
def async_dec():
def decorator(fn):
def wrapper(*args, **kwargs):
@ -455,3 +470,25 @@ def webhook_google_functions(bot, request):
return 'Bot FAIL', 400
else:
return 'Bot ON'
def antiflood(function, *args, **kwargs):
"""
Use this function inside loops in order to avoid getting TooManyRequests error.
Example:
from telebot.util import antiflood
for chat_id in chat_id_list:
msg = antiflood(bot.send_message, chat_id, text)
You want get the
"""
from telebot.apihelper import ApiTelegramException
from time import sleep
try:
msg = function(*args, **kwargs)
except ApiTelegramException as ex:
if ex.error_code == 429:
sleep(ex.result_json['parameters']['retry_after'])
msg = function(*args, **kwargs)
finally:
return msg

View File

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

View File

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

View File

@ -455,6 +455,13 @@ class TestTeleBot:
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
assert new_msg.message_id
def test_antiflood(self):
text = "Flooding"
tb = telebot.TeleBot(TOKEN)
for _ in range(0,100):
util.antiflood(tb.send_message, CHAT_ID, text)
assert _
@staticmethod
def create_text_message(text):
params = {'text': text}
@ -478,9 +485,10 @@ class TestTeleBot:
poll_answer = None
my_chat_member = None
chat_member = None
chat_join_request = None
return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer,
my_chat_member, chat_member)
my_chat_member, chat_member, chat_join_request)
def test_is_string_unicode(self):
s1 = u'string'

View File

@ -222,14 +222,19 @@ def test_KeyboardButtonPollType():
def test_json_chat_invite_link():
json_string = r'{"invite_link": "https://t.me/joinchat/z-abCdEFghijKlMn", "creator": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "is_primary": false, "is_revoked": false, "expire_date": 1624119999, "member_limit": 10}'
json_string = r'{"invite_link":"https://t.me/joinchat/MeASP-Wi...","creator":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"ru"},"pending_join_request_count":1,"creates_join_request":true,"is_primary":false,"is_revoked":false}'
invite_link = types.ChatInviteLink.de_json(json_string)
assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn'
assert invite_link.invite_link == 'https://t.me/joinchat/MeASP-Wi...'
assert isinstance(invite_link.creator, types.User)
assert not invite_link.is_primary
assert not invite_link.is_revoked
assert invite_link.expire_date == 1624119999
assert invite_link.member_limit == 10
assert invite_link.expire_date is None
assert invite_link.member_limit is None
assert invite_link.name is None
assert invite_link.creator.id == 927266710
assert invite_link.pending_join_request_count == 1
assert invite_link.creates_join_request
def test_chat_member_updated():
json_string = r'{"chat": {"id": -1234567890123, "type": "supergroup", "title": "No Real Group", "username": "NoRealGroup"}, "from": {"id": 133869498, "is_bot": false, "first_name": "Vincent"}, "date": 1624119999, "old_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "member"}, "new_chat_member": {"user": {"id": 77777777, "is_bot": false, "first_name": "Pepe"}, "status": "administrator"}}'