mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
124 Commits
Author | SHA1 | Date | |
---|---|---|---|
fbf34f5953 | |||
4347dd3dd9 | |||
d830ae0b15 | |||
4f198bc6f5 | |||
66615a41c4 | |||
a5ee5f816c | |||
fb52137bff | |||
7ee07f4dc7 | |||
f224069a34 | |||
6cca77f755 | |||
084289baa4 | |||
e2dbb88459 | |||
a2822c74ed | |||
4cd30c75ac | |||
f4b9480588 | |||
482589af49 | |||
bbe4a96984 | |||
60294d0c41 | |||
3035763277 | |||
51eabde320 | |||
a5305f551c | |||
1f918dece5 | |||
fc152f37ad | |||
411c7e915a | |||
5bf2415653 | |||
7d9856dae3 | |||
a308ab12fa | |||
d58336adcb | |||
bfc0b8ecd5 | |||
a9b422783f | |||
e015b4c010 | |||
f666c15a1f | |||
6770011dd7 | |||
d7b0513fb1 | |||
9932ade00e | |||
8b6eba8203 | |||
9c8ea29fc6 | |||
714ae7d67f | |||
1e4477c148 | |||
53f9232f36 | |||
1f05b47ad6 | |||
98d63d235f | |||
7925bdc6c9 | |||
4ba23562ef | |||
e22a7fecea | |||
9b99bb5f21 | |||
c39b3aaaf3 | |||
d14ac2fe85 | |||
04f3e518da | |||
5f5298bcd1 | |||
7f1497c5e9 | |||
5ac71baafe | |||
695c699893 | |||
bf96f13296 | |||
62b1ec04ab | |||
e412d2f084 | |||
8003ff5e59 | |||
becce1f580 | |||
3a6073e3a0 | |||
fc347ae166 | |||
8dcfa0c282 | |||
6808ab3ebe | |||
31097c5380 | |||
d49c57699e | |||
ed6616e4c7 | |||
953e2286b8 | |||
06c8782127 | |||
2623fa362c | |||
4a274ba440 | |||
bfef7e1ce2 | |||
558b37b1c3 | |||
d1c26f82aa | |||
1a351bc8c7 | |||
27546daad9 | |||
bb58d3fead | |||
099d638a7e | |||
5fb48e68a0 | |||
b979c2fa1f | |||
b6625baec6 | |||
98044d6faa | |||
2113846567 | |||
5c9d4edca9 | |||
4dce9340b0 | |||
bf79e8341e | |||
dadfdc2f13 | |||
585f627e1f | |||
eead303d47 | |||
14cc15c711 | |||
bf8736e17e | |||
5014ca2459 | |||
f337abe06e | |||
ff35f25211 | |||
2e4280a947 | |||
4a6b5b3d28 | |||
a28af3903d | |||
d1d5b9effb | |||
062fababf2 | |||
946afcc3c1 | |||
6e502cd1c6 | |||
0a7f897f05 | |||
b35f17124f | |||
44b44ac2c5 | |||
39e875c1ea | |||
be7317cc86 | |||
e1c33a1de6 | |||
8149551a15 | |||
ab648ef3db | |||
e721910c0c | |||
9287eced49 | |||
967b94b14f | |||
2df6f00ba5 | |||
beb4f8df44 | |||
92ac5a4166 | |||
716323e56a | |||
cd92d95f91 | |||
9c86ed623d | |||
c6ff9b07df | |||
38cc96d0f3 | |||
82518d8664 | |||
aba2a9e179 | |||
c5c4d081ea | |||
f854163626 | |||
fc31a2d466 | |||
86a0a8cd68 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ var/
|
||||
|
||||
.idea/
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
|
@ -4,6 +4,7 @@ python:
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
script:
|
||||
|
149
README.md
149
README.md
@ -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.**
|
||||
|
86
examples/CallbackData_example.py
Normal file
86
examples/CallbackData_example.py
Normal 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
117
examples/anonymous_bot.py
Normal 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)
|
87
examples/asynchronous_telebot/CallbackData_example.py
Normal file
87
examples/asynchronous_telebot/CallbackData_example.py
Normal 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()
|
11
examples/asynchronous_telebot/chat_join_request.py
Normal file
11
examples/asynchronous_telebot/chat_join_request.py
Normal 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)
|
33
examples/asynchronous_telebot/chat_member_example.py
Normal file
33
examples/asynchronous_telebot/chat_member_example.py
Normal 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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
74
examples/asynchronous_telebot/custom_states.py
Normal file
74
examples/asynchronous_telebot/custom_states.py
Normal 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()
|
20
examples/asynchronous_telebot/download_file_example.py
Normal file
20
examples/asynchronous_telebot/download_file_example.py
Normal 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)
|
26
examples/asynchronous_telebot/echo_bot.py
Normal file
26
examples/asynchronous_telebot/echo_bot.py
Normal 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()
|
27
examples/asynchronous_telebot/exception_handler.py
Normal file
27
examples/asynchronous_telebot/exception_handler.py
Normal 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)
|
@ -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()
|
48
examples/asynchronous_telebot/middleware/i18n.py
Normal file
48
examples/asynchronous_telebot/middleware/i18n.py
Normal 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()
|
18
examples/asynchronous_telebot/register_handler.py
Normal file
18
examples/asynchronous_telebot/register_handler.py
Normal 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)
|
27
examples/asynchronous_telebot/send_file_example.py
Normal file
27
examples/asynchronous_telebot/send_file_example.py
Normal 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)
|
13
examples/asynchronous_telebot/skip_updates_example.py
Normal file
13
examples/asynchronous_telebot/skip_updates_example.py
Normal 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
|
14
examples/asynchronous_telebot/update_listener.py
Normal file
14
examples/asynchronous_telebot/update_listener.py
Normal 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()
|
11
examples/chat_join_request.py
Normal file
11
examples/chat_join_request.py
Normal 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)
|
@ -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)
|
||||
|
12
examples/custom_filters/admin_filter_example.py
Normal file
12
examples/custom_filters/admin_filter_example.py
Normal 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()
|
@ -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
|
||||
|
@ -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()
|
||||
|
21
examples/custom_filters/is_filter_example.py
Normal file
21
examples/custom_filters/is_filter_example.py
Normal 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()
|
@ -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
74
examples/custom_states.py
Normal 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)
|
@ -74,4 +74,4 @@ def send_welcome(message):
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -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()
|
||||
|
@ -25,4 +25,4 @@ def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -61,7 +61,7 @@ def default_query(inline_query):
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
bot.infinity_polling()
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -50,4 +50,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, _('hello'))
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -58,4 +58,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, bot.session['state'])
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -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)
|
||||
|
21
examples/register_handler.py
Normal file
21
examples/register_handler.py
Normal 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()
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -81,4 +81,4 @@ def listener(messages):
|
||||
|
||||
|
||||
bot.set_update_listener(listener)
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -1,3 +1,4 @@
|
||||
pytest==3.0.2
|
||||
requests==2.20.0
|
||||
wheel==0.24.0
|
||||
aiohttp>=3.8.0,<3.9.0
|
@ -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)
|
||||
|
@ -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
2881
telebot/async_telebot.py
Normal file
File diff suppressed because it is too large
Load Diff
178
telebot/asyncio_filters.py
Normal file
178
telebot/asyncio_filters.py
Normal 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()
|
219
telebot/asyncio_handler_backends.py
Normal file
219
telebot/asyncio_handler_backends.py
Normal 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
1635
telebot/asyncio_helper.py
Normal file
File diff suppressed because it is too large
Load Diff
115
telebot/callback_data.py
Normal file
115
telebot/callback_data.py
Normal 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)
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
@ -1,3 +1,3 @@
|
||||
# Versions should comply with PEP440.
|
||||
# This line is parsed in setup.py:
|
||||
__version__ = '4.0.1'
|
||||
__version__ = '4.2.1'
|
||||
|
@ -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):
|
||||
|
@ -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'
|
||||
|
@ -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"}}'
|
||||
|
Reference in New Issue
Block a user