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

Compare commits

...

30 Commits

Author SHA1 Message Date
Badiboy
91badb53e5
Merge pull request #1473 from coder2020official/master
Docstrings of sync telebot updated to fit documentation. Basic middleware classes added.
2022-03-07 20:00:55 +03:00
coder2020official
477d02468d Fixed middlewares 2022-03-07 21:40:39 +05:00
coder2020official
9f3a270fae Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-03-07 21:13:49 +05:00
coder2020official
244b058648 Fix 2022-03-07 21:13:30 +05:00
_run
886c9b9bc0
Update README.md 2022-03-07 20:38:22 +05:00
_run
41025ba97b
Update setup_python.yml 2022-03-07 20:26:11 +05:00
_run
4875bb6188
Update requirements.txt 2022-03-07 19:04:12 +05:00
_run
5f91c3d4e6
Added python 3.10 to tests 2022-03-07 18:56:16 +05:00
_run
7b62915a5b
Create PULL_REQUEST_TEMPLATE.md 2022-03-07 18:54:59 +05:00
_run
05c3cb2c1d
Update doc_req.txt 2022-03-07 18:08:41 +05:00
_run
60a96d1400
Update README.md 2022-03-07 17:42:47 +05:00
_run
dcd0df93da
Update README.md 2022-03-07 17:39:42 +05:00
_run
f15101fc6f
Update doc_req.txt 2022-03-07 17:32:42 +05:00
coder2020official
5f03253398 Fix comments 2022-03-07 17:31:02 +05:00
_run
8fab55e937
Create antiflood_middleware.py 2022-03-07 17:30:10 +05:00
_run
60a23665cb
Update flooding_middleware.py 2022-03-07 17:23:55 +05:00
_run
b292b275cb
Create basic_example.py 2022-03-07 17:21:59 +05:00
_run
403028bf35
Update README.md 2022-03-07 17:14:51 +05:00
_run
3dda5cff06
Create README.md 2022-03-07 17:14:25 +05:00
coder2020official
dd589e2490 Updated documentation to another theme. 2022-03-07 16:10:44 +05:00
coder2020official
c8fb83c97c Fix documentation 2022-03-07 14:24:28 +05:00
coder2020official
854f6a9506 Merge branch 'master' of https://github.com/coder2020official/pyTelegramBotAPI 2022-03-07 13:30:41 +05:00
coder2020official
be0557c2b5 Multiple middlewares allowed for async 2022-03-07 13:30:39 +05:00
_run
fc72576aaa
Update README.md 2022-03-07 00:39:25 +05:00
coder2020official
f69a2ba044 Update docstrings for asynctelebot. 2022-03-07 00:18:11 +05:00
coder2020official
c45e06c694 Updated description for TeleBot class 2022-03-06 23:23:33 +05:00
coder2020official
78bdf1ca4e Update docstrings 2022-03-06 23:14:07 +05:00
coder2020official
3c7d3c0196 Fix tests 2022-03-06 19:52:42 +05:00
coder2020official
441a5793cc Update docstrings to correct documentation. 2022-03-06 19:41:54 +05:00
coder2020official
388477686b Added middlewares.
Bumped middlewares
2022-03-06 18:39:41 +05:00
23 changed files with 1262 additions and 479 deletions

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

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

View File

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.6','3.7','3.8','3.9', 'pypy-3.7', 'pypy-3.8' ] #'pypy-3.9' NOT SUPPORTED NOW
python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9']
name: ${{ matrix.python-version }} and tests
steps:
- uses: actions/checkout@v2

2
.gitignore vendored
View File

@ -67,5 +67,3 @@ testMain.py
# documentation
_build/
_static/
_templates/

View File

@ -1,15 +1,17 @@
[![PyPi Package Version](https://img.shields.io/pypi/v/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyTelegramBotAPI.svg)](https://pypi.python.org/pypi/pyTelegramBotAPI)
[![Documentation Status](https://readthedocs.org/projects/pytba/badge/?version=latest)](https://pytba.readthedocs.io/en/latest/?badge=latest)
[![Build Status](https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master)](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
[![PyPi downloads](https://img.shields.io/pypi/dm/pyTelegramBotAPI.svg)](https://pypi.org/project/pyTelegramBotAPI/)
[![PyPi status](https://img.shields.io/pypi/status/pytelegrambotapi.svg?style=flat-square)](https://pypi.python.org/pypi/pytelegrambotapi)
# <p align="center">pyTelegramBotAPI
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Supports both sync and async ways.</p>
<p align="center">Both synchronous and asynchronous.</p>
## <p align="center">Supporting Bot API version: <a href="https://core.telegram.org/bots/api#january-31-2022">5.7</a>!
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#january-31-2022">5.7</a>!
<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
@ -354,7 +356,9 @@ def start(message):
assert message.another_text == message.text + ':changed'
```
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
#### Class-based middlewares
There are class-based middlewares. Check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based)
#### Custom filters
Also, you can use built-in custom filters. Or, you can create your own filter.
@ -757,7 +761,7 @@ As you can see here, keywords are await and 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)
AsyncTeleBot is asynchronous. It uses aiohttp instead of requests module.
### Examples
See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder

View File

@ -1 +1,3 @@
pytelegrambotapi
pytelegrambotapi
furo
sphinx_copybutton

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -28,14 +28,6 @@ Asynchronous storage for states
:undoc-members:
:show-inheritance:
asyncio_helper file
-------------------
.. automodule:: telebot.asyncio_helper
:members:
:undoc-members:
:show-inheritance:
Asyncio handler backends
------------------------

View File

@ -22,7 +22,7 @@ copyright = '2022, coder2020official'
author = 'coder2020official'
# The full version, including alpha/beta/rc tags
release = '1.0'
release = '4.4.0'
# -- General configuration ---------------------------------------------------
@ -31,11 +31,11 @@ release = '1.0'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autosectionlabel',
'sphinx.ext.autodoc',
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
"sphinx_copybutton",
]
@ -53,14 +53,18 @@ exclude_patterns = []
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme = 'furo'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_logo = 'logo.png'
#html_logo = 'logo.png'
html_theme_options = {
'logo_only': True,
'display_version': False,
"light_css_variables": {
"color-brand-primary": "#7C4DFF",
"color-brand-content": "#7C4DFF",
},
"light_logo": "logo.png",
"dark_logo": "logo2.png",
}

View File

@ -25,7 +25,7 @@ Pypi: `Pypi <https://pypi.org/project/pyTelegramBotAPI/>`__
Source: `Github repository <https://github.com/eternnoir/pyTelegramBotAPI>`__
Some features:
-------------
--------------
Easy to learn and use.
Easy to understand.

View File

@ -3,8 +3,6 @@ Installation Guide
==================
:toctree
Using PIP
----------
.. code-block:: bash

View File

@ -33,11 +33,3 @@ handler_backends file
:undoc-members:
:show-inheritance:
apihelper
------------------------
.. automodule:: telebot.apihelper
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
# Just a little example of middleware handlers
from telebot.asyncio_handler_backends import BaseMiddleware
from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate
from telebot.async_telebot import AsyncTeleBot
from telebot.async_telebot import CancelUpdate
bot = AsyncTeleBot('TOKEN')
@ -36,4 +35,4 @@ async def start(message):
await bot.send_message(message.chat.id, 'Hello!')
import asyncio
asyncio.run(bot.polling())
asyncio.run(bot.polling())

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,8 +24,33 @@ class State:
class StatesGroup:
def __init_subclass__(cls) -> None:
# print all variables of a subclass
for name, value in cls.__dict__.items():
if not name.startswith('__') and not callable(value) and isinstance(value, State):
# change value of that variable
value.name = ':'.join((cls.__name__, name))
value.name = ':'.join((cls.__name__, name))
class SkipHandler:
"""
Class for skipping handlers.
Just return instance of this class
in middleware to skip handler.
Update will go to post_process,
but will skip execution of handler.
"""
def __init__(self) -> None:
pass
class CancelUpdate:
"""
Class for canceling updates.
Just return instance of this class
in middleware to skip update.
Update will skip handler and execution
of post_process in middlewares.
"""
def __init__(self) -> None:
pass

View File

@ -159,10 +159,48 @@ class State:
class StatesGroup:
def __init_subclass__(cls) -> None:
# print all variables of a subclass
for name, value in cls.__dict__.items():
if not name.startswith('__') and not callable(value) and isinstance(value, State):
# change value of that variable
value.name = ':'.join((cls.__name__, name))
class BaseMiddleware:
"""
Base class for middleware.
Your middlewares should be inherited from this class.
"""
def __init__(self):
pass
def pre_process(self, message, data):
raise NotImplementedError
def post_process(self, message, data, exception):
raise NotImplementedError
class SkipHandler:
"""
Class for skipping handlers.
Just return instance of this class
in middleware to skip handler.
Update will go to post_process,
but will skip execution of handler.
"""
def __init__(self) -> None:
pass
class CancelUpdate:
"""
Class for canceling updates.
Just return instance of this class
in middleware to skip update.
Update will skip handler and execution
of post_process in middlewares.
"""
def __init__(self) -> None:
pass

View File

@ -1041,9 +1041,9 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
def __init__(self, keyboard=None, row_width=3):
"""
This object represents an inline keyboard that appears
right next to the message it belongs to.
right next to the message it belongs to.
:return:
:return: None
"""
if row_width > self.max_row_keys:
# Todo: Will be replaced with Exception in future releases
@ -1058,10 +1058,10 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
This method adds buttons to the keyboard without exceeding row_width.
E.g. InlineKeyboardMarkup.add("A", "B", "C") yields the json result:
{keyboard: [["A"], ["B"], ["C"]]}
{keyboard: [["A"], ["B"], ["C"]]}
when row_width is set to 1.
When row_width is set to 2, the result:
{keyboard: [["A", "B"], ["C"]]}
{keyboard: [["A", "B"], ["C"]]}
See https://core.telegram.org/bots/api#inlinekeyboardmarkup
:param args: Array of InlineKeyboardButton to append to the keyboard
@ -1085,10 +1085,10 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
def row(self, *args):
"""
Adds a list of InlineKeyboardButton to the keyboard.
This method does not consider row_width.
This method does not consider row_width.
InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs:
'{keyboard: [["A"], ["B", "C"]]}'
'{keyboard: [["A"], ["B", "C"]]}'
See https://core.telegram.org/bots/api#inlinekeyboardmarkup
:param args: Array of InlineKeyboardButton to append to the keyboard
@ -1100,7 +1100,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
def to_json(self):
"""
Converts this object to its json representation
following the Telegram API guidelines described here:
following the Telegram API guidelines described here:
https://core.telegram.org/bots/api#inlinekeyboardmarkup
:return:
"""

View File

@ -283,7 +283,7 @@ def split_string(text: str, chars_per_string: int) -> List[str]:
def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]:
"""
r"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
If `chars_per_string` > 4096: `chars_per_string` = 4096.
@ -350,24 +350,27 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I
This is useful to avoid always typing 'btn1 = InlineKeyboardButton(...)' 'btn2 = InlineKeyboardButton(...)'
Example:
quick_markup({
'Twitter': {'url': 'https://twitter.com'},
'Facebook': {'url': 'https://facebook.com'},
'Back': {'callback_data': 'whatever'}
}, row_width=2):
returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
and a back button below
kwargs can be:
{
'url': None,
'callback_data': None,
'switch_inline_query': None,
'switch_inline_query_current_chat': None,
'callback_game': None,
'pay': None,
'login_url': None
}
.. code-block:: python
quick_markup({
'Twitter': {'url': 'https://twitter.com'},
'Facebook': {'url': 'https://facebook.com'},
'Back': {'callback_data': 'whatever'}
}, row_width=2):
# returns an InlineKeyboardMarkup with two buttons in a row, one leading to Twitter, the other to facebook
# and a back button below
# kwargs can be:
{
'url': None,
'callback_data': None,
'switch_inline_query': None,
'switch_inline_query_current_chat': None,
'callback_game': None,
'pay': None,
'login_url': None
}
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
:param row_width: int row width
@ -484,12 +487,17 @@ 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:
.. code-block:: python3
from telebot.util import antiflood
for chat_id in chat_id_list:
msg = antiflood(bot.send_message, chat_id, text)
You want get the
:param function:
:param args:
:param kwargs:
:return: None
"""
from telebot.apihelper import ApiTelegramException
from time import sleep