Переход на Google Style Docstring
This commit is contained in:
parent
1400f72cd0
commit
af4c9dc487
884
maxapi/bot.py
884
maxapi/bot.py
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
from aiohttp import ClientTimeout
|
from aiohttp import ClientTimeout
|
||||||
|
|
||||||
|
|
||||||
class DefaultConnectionProperties:
|
class DefaultConnectionProperties:
|
||||||
|
'''
|
||||||
|
Класс для хранения параметров соединения по умолчанию для aiohttp-клиента.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
timeout (int): Таймаут всего соединения в секундах (по умолчанию 5 * 30).
|
||||||
|
sock_connect (int): Таймаут установки TCP-соединения в секундах (по умолчанию 30).
|
||||||
|
**kwargs: Дополнительные параметры, которые будут сохранены как есть.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
timeout (ClientTimeout): Экземпляр aiohttp.ClientTimeout с заданными параметрами.
|
||||||
|
kwargs (dict): Дополнительные параметры.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, timeout: int = 5 * 30, sock_connect: int = 30, **kwargs):
|
def __init__(self, timeout: int = 5 * 30, sock_connect: int = 30, **kwargs):
|
||||||
|
'''
|
||||||
|
Инициализация параметров соединения.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
timeout (int): Таймаут всего соединения в секундах.
|
||||||
|
sock_connect (int): Таймаут установки TCP-соединения в секундах.
|
||||||
|
**kwargs: Дополнительные параметры.
|
||||||
|
'''
|
||||||
self.timeout = ClientTimeout(total=timeout, sock_connect=sock_connect)
|
self.timeout = ClientTimeout(total=timeout, sock_connect=sock_connect)
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
@ -30,9 +30,7 @@ class BaseConnection:
|
|||||||
"""
|
"""
|
||||||
Базовый класс для всех методов API.
|
Базовый класс для всех методов API.
|
||||||
|
|
||||||
Содержит общую логику выполнения запроса (например, сериализацию, отправку HTTP-запроса, обработку ответа).
|
Содержит общую логику выполнения запроса (сериализация, отправка HTTP-запроса, обработка ответа).
|
||||||
|
|
||||||
Метод request() может быть переопределён в потомках при необходимости.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
API_URL = 'https://botapi.max.ru'
|
API_URL = 'https://botapi.max.ru'
|
||||||
@ -41,53 +39,67 @@ class BaseConnection:
|
|||||||
AFTER_MEDIA_INPUT_DELAY = 2.0
|
AFTER_MEDIA_INPUT_DELAY = 2.0
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Инициализация BaseConnection.
|
||||||
|
|
||||||
|
Атрибуты:
|
||||||
|
bot (Optional[Bot]): Экземпляр бота.
|
||||||
|
session (Optional[ClientSession]): aiohttp-сессия.
|
||||||
|
after_input_media_delay (float): Задержка после ввода медиа.
|
||||||
|
"""
|
||||||
|
|
||||||
self.bot: Optional[Bot] = None
|
self.bot: Optional[Bot] = None
|
||||||
self.session: Optional[ClientSession] = None
|
self.session: Optional[ClientSession] = None
|
||||||
self.after_input_media_delay: float = self.AFTER_MEDIA_INPUT_DELAY
|
self.after_input_media_delay: float = self.AFTER_MEDIA_INPUT_DELAY
|
||||||
|
|
||||||
async def request(
|
async def request(
|
||||||
self,
|
self,
|
||||||
method: HTTPMethod,
|
method: HTTPMethod,
|
||||||
path: ApiPath | str,
|
path: ApiPath | str,
|
||||||
model: BaseModel | Any = None,
|
model: BaseModel | Any = None,
|
||||||
is_return_raw: bool = False,
|
is_return_raw: bool = False,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Выполняет HTTP-запрос к API, используя указанные параметры.
|
Выполняет HTTP-запрос к API.
|
||||||
|
|
||||||
:param method: HTTP-метод запроса (GET, POST и т.д.)
|
Args:
|
||||||
:param path: Путь к конечной точке API
|
method (HTTPMethod): HTTP-метод (GET, POST и т.д.).
|
||||||
:param model: Pydantic-модель, в которую будет десериализован ответ (если is_return_raw=False)
|
path (ApiPath | str): Путь до конечной точки.
|
||||||
:param is_return_raw: Если True — вернуть "сырое" тело ответа, иначе — результат десериализации в model
|
model (BaseModel | Any, optional): Pydantic-модель для десериализации ответа, если is_return_raw=False.
|
||||||
:param kwargs: Дополнительные параметры (например, query, headers, json)
|
is_return_raw (bool, optional): Если True — вернуть сырой ответ, иначе — результат десериализации.
|
||||||
|
**kwargs: Дополнительные параметры (query, headers, json).
|
||||||
|
|
||||||
:return:
|
Returns:
|
||||||
- Объект model (если is_return_raw=False и model задан)
|
model | dict | Error: Объект модели, dict или ошибка.
|
||||||
|
|
||||||
- dict (если is_return_raw=True)
|
Raises:
|
||||||
|
RuntimeError: Если бот не инициализирован.
|
||||||
|
MaxConnection: Ошибка соединения.
|
||||||
|
InvalidToken: Ошибка авторизации (401).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.bot is None:
|
if self.bot is None:
|
||||||
raise RuntimeError('Bot не инициализирован')
|
raise RuntimeError('Bot не инициализирован')
|
||||||
|
|
||||||
if not self.bot.session:
|
if not self.bot.session:
|
||||||
self.bot.session = ClientSession(
|
self.bot.session = ClientSession(
|
||||||
base_url=self.bot.API_URL,
|
base_url=self.bot.API_URL,
|
||||||
timeout=self.bot.default_connection.timeout,
|
timeout=self.bot.default_connection.timeout,
|
||||||
**self.bot.default_connection.kwargs
|
**self.bot.default_connection.kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = await self.bot.session.request(
|
r = await self.bot.session.request(
|
||||||
method=method.value,
|
method=method.value,
|
||||||
url=path.value if isinstance(path, ApiPath) else path,
|
url=path.value if isinstance(path, ApiPath) else path,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
except ClientConnectionError as e:
|
except ClientConnectionError as e:
|
||||||
raise MaxConnection(f'Ошибка при отправке запроса: {e}')
|
raise MaxConnection(f'Ошибка при отправке запроса: {e}')
|
||||||
|
|
||||||
if r.status == 401:
|
if r.status == 401:
|
||||||
await self.bot.session.close()
|
await self.bot.session.close()
|
||||||
raise InvalidToken('Неверный токен!')
|
raise InvalidToken('Неверный токен!')
|
||||||
@ -97,38 +109,41 @@ class BaseConnection:
|
|||||||
error = Error(code=r.status, raw=raw)
|
error = Error(code=r.status, raw=raw)
|
||||||
logger_bot.error(error)
|
logger_bot.error(error)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
raw = await r.json()
|
raw = await r.json()
|
||||||
|
|
||||||
if is_return_raw:
|
if is_return_raw:
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
model = model(**raw) # type: ignore
|
model = model(**raw) # type: ignore
|
||||||
|
|
||||||
if hasattr(model, 'message'):
|
if hasattr(model, 'message'):
|
||||||
attr = getattr(model, 'message')
|
attr = getattr(model, 'message')
|
||||||
if hasattr(attr, 'bot'):
|
if hasattr(attr, 'bot'):
|
||||||
attr.bot = self.bot
|
attr.bot = self.bot
|
||||||
|
|
||||||
if hasattr(model, 'bot'):
|
if hasattr(model, 'bot'):
|
||||||
model.bot = self.bot
|
model.bot = self.bot
|
||||||
|
|
||||||
return model
|
return model
|
||||||
|
|
||||||
async def upload_file(
|
async def upload_file(
|
||||||
self,
|
self,
|
||||||
url: str,
|
url: str,
|
||||||
path: str,
|
path: str,
|
||||||
type: UploadType
|
type: UploadType
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Загружает файл на указанный URL.
|
Загружает файл на сервер.
|
||||||
|
|
||||||
:param url: Конечная точка загрузки файла
|
Args:
|
||||||
:param path: Путь к локальному файлу
|
url (str): URL загрузки.
|
||||||
:param type: Тип файла (video, image, audio, file)
|
path (str): Путь к файлу.
|
||||||
|
type (UploadType): Тип файла.
|
||||||
|
|
||||||
:return: Сырой .text() ответ от сервера после загрузки файла
|
Returns:
|
||||||
|
str: Сырой .text() ответ от сервера.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async with aiofiles.open(path, 'rb') as f:
|
async with aiofiles.open(path, 'rb') as f:
|
||||||
@ -147,12 +162,12 @@ class BaseConnection:
|
|||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
response = await session.post(
|
response = await session.post(
|
||||||
url=url,
|
url=url,
|
||||||
data=form
|
data=form
|
||||||
)
|
)
|
||||||
|
|
||||||
return await response.text()
|
return await response.text()
|
||||||
|
|
||||||
async def upload_file_buffer(
|
async def upload_file_buffer(
|
||||||
self,
|
self,
|
||||||
filename: str,
|
filename: str,
|
||||||
@ -160,16 +175,20 @@ class BaseConnection:
|
|||||||
buffer: bytes,
|
buffer: bytes,
|
||||||
type: UploadType
|
type: UploadType
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Загружает файл из буфера.
|
Загружает файл из буфера.
|
||||||
|
|
||||||
:param url: Конечная точка загрузки файла
|
Args:
|
||||||
:param buffer: Буфер (bytes)
|
filename (str): Имя файла.
|
||||||
:param type: Тип файла (video, image, audio, file)
|
url (str): URL загрузки.
|
||||||
|
buffer (bytes): Буфер данных.
|
||||||
|
type (UploadType): Тип файла.
|
||||||
|
|
||||||
:return: Сырой .text() ответ от сервера после загрузки файла
|
Returns:
|
||||||
|
str: Сырой .text() ответ от сервера.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
matches = puremagic.magic_string(buffer[:4096])
|
matches = puremagic.magic_string(buffer[:4096])
|
||||||
if matches:
|
if matches:
|
||||||
@ -194,36 +213,7 @@ class BaseConnection:
|
|||||||
|
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
response = await session.post(
|
response = await session.post(
|
||||||
url=url,
|
url=url,
|
||||||
data=form
|
data=form
|
||||||
)
|
)
|
||||||
return await response.text()
|
return await response.text()
|
||||||
|
|
||||||
async def download_file(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
url: str,
|
|
||||||
token: str,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Скачивает медиа с указанной ссылки по токену, сохраняя по определенному пути
|
|
||||||
|
|
||||||
:param path: Путь сохранения медиа
|
|
||||||
:param url: Ссылка на медиа
|
|
||||||
:param token: Токен медиа
|
|
||||||
|
|
||||||
:return: Числовой статус
|
|
||||||
"""
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'Authorization': f'Bearer {token}'
|
|
||||||
}
|
|
||||||
|
|
||||||
async with ClientSession() as session:
|
|
||||||
async with session.get(url, headers=headers) as response:
|
|
||||||
|
|
||||||
if response.status == 200:
|
|
||||||
async with aiofiles.open(path, 'wb') as f:
|
|
||||||
await f.write(await response.read())
|
|
||||||
|
|
||||||
return response.status
|
|
@ -1,4 +1,8 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Представляет отдельное состояние в FSM-группе.
|
Представляет отдельное состояние в FSM-группе.
|
||||||
|
|
||||||
@ -16,6 +20,7 @@ class State:
|
|||||||
|
|
||||||
|
|
||||||
class StatesGroup:
|
class StatesGroup:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Базовый класс для описания группы состояний FSM.
|
Базовый класс для описания группы состояний FSM.
|
||||||
|
|
||||||
@ -23,7 +28,8 @@ class StatesGroup:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def states(cls) -> list[str]:
|
def states(cls) -> List[str]:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Получить список всех состояний в формате 'ИмяКласса:имя_состояния'.
|
Получить список всех состояний в формате 'ИмяКласса:имя_состояния'.
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ from asyncio.exceptions import TimeoutError as AsyncioTimeoutError
|
|||||||
|
|
||||||
from aiohttp import ClientConnectorError
|
from aiohttp import ClientConnectorError
|
||||||
|
|
||||||
|
from maxapi.exceptions.dispatcher import HandlerException
|
||||||
|
|
||||||
from .filters.filter import BaseFilter
|
from .filters.filter import BaseFilter
|
||||||
from .filters.middleware import BaseMiddleware
|
from .filters.middleware import BaseMiddleware
|
||||||
from .filters.handler import Handler
|
from .filters.handler import Handler
|
||||||
@ -60,9 +62,9 @@ class Dispatcher:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Инициализация диспетчера.
|
Инициализация диспетчера.
|
||||||
|
|
||||||
:param router_id: Идентификатор роутера, используется при логгировании.
|
Args:
|
||||||
По умолчанию индекс зарегистированного роутера в списке
|
router_id (str | None): Идентификатор роутера для логов.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.router_id = router_id
|
self.router_id = router_id
|
||||||
@ -132,6 +134,17 @@ class Dispatcher:
|
|||||||
handler: Callable[[Any, dict[str, Any]], Awaitable[Any]]
|
handler: Callable[[Any, dict[str, Any]], Awaitable[Any]]
|
||||||
) -> Callable[[Any, dict[str, Any]], Awaitable[Any]]:
|
) -> Callable[[Any, dict[str, Any]], Awaitable[Any]]:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Формирует цепочку вызова middleware вокруг хендлера.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
middlewares (list[BaseMiddleware]): Список middleware.
|
||||||
|
handler (Callable): Финальный обработчик.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Callable: Обёрнутый обработчик.
|
||||||
|
"""
|
||||||
|
|
||||||
for mw in reversed(middlewares):
|
for mw in reversed(middlewares):
|
||||||
handler = functools.partial(mw, handler)
|
handler = functools.partial(mw, handler)
|
||||||
|
|
||||||
@ -142,7 +155,8 @@ class Dispatcher:
|
|||||||
"""
|
"""
|
||||||
Добавляет указанные роутеры в диспетчер.
|
Добавляет указанные роутеры в диспетчер.
|
||||||
|
|
||||||
:param routers: Роутеры для добавления.
|
Args:
|
||||||
|
*routers (Router): Роутеры для добавления.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.routers += [r for r in routers]
|
self.routers += [r for r in routers]
|
||||||
@ -150,9 +164,10 @@ class Dispatcher:
|
|||||||
def outer_middleware(self, middleware: BaseMiddleware) -> None:
|
def outer_middleware(self, middleware: BaseMiddleware) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Добавляет Middleware на первое место в списке
|
Добавляет Middleware на первое место в списке.
|
||||||
|
|
||||||
:param: middleware: Middleware
|
Args:
|
||||||
|
middleware (BaseMiddleware): Middleware.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.middlewares.insert(0, middleware)
|
self.middlewares.insert(0, middleware)
|
||||||
@ -160,9 +175,10 @@ class Dispatcher:
|
|||||||
def middleware(self, middleware: BaseMiddleware) -> None:
|
def middleware(self, middleware: BaseMiddleware) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Добавляет Middleware в список
|
Добавляет Middleware в конец списка.
|
||||||
|
|
||||||
:param middleware: Middleware
|
Args:
|
||||||
|
middleware (BaseMiddleware): Middleware.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.middlewares.append(middleware)
|
self.middlewares.append(middleware)
|
||||||
@ -170,9 +186,10 @@ class Dispatcher:
|
|||||||
def filter(self, base_filter: BaseFilter) -> None:
|
def filter(self, base_filter: BaseFilter) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Добавляет фильтр в список
|
Добавляет фильтр в список.
|
||||||
|
|
||||||
:param base_filter: Фильтр
|
Args:
|
||||||
|
base_filter (BaseFilter): Фильтр.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.base_filters.append(base_filter)
|
self.base_filters.append(base_filter)
|
||||||
@ -182,7 +199,8 @@ class Dispatcher:
|
|||||||
"""
|
"""
|
||||||
Подготавливает диспетчер: сохраняет бота, регистрирует обработчики, вызывает on_started.
|
Подготавливает диспетчер: сохраняет бота, регистрирует обработчики, вызывает on_started.
|
||||||
|
|
||||||
:param bot: Экземпляр бота.
|
Args:
|
||||||
|
bot (Bot): Экземпляр бота.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
@ -208,11 +226,14 @@ class Dispatcher:
|
|||||||
def __get_memory_context(self, chat_id: int, user_id: int):
|
def __get_memory_context(self, chat_id: int, user_id: int):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Возвращает существующий или создает новый контекст по chat_id и user_id.
|
Возвращает существующий или создаёт новый MemoryContext по chat_id и user_id.
|
||||||
|
|
||||||
:param chat_id: Идентификатор чата.
|
Args:
|
||||||
:param user_id: Идентификатор пользователя.
|
chat_id (int): Идентификатор чата.
|
||||||
:return: Объект MemoryContext.
|
user_id (int): Идентификатор пользователя.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MemoryContext: Контекст.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for ctx in self.contexts:
|
for ctx in self.contexts:
|
||||||
@ -223,10 +244,23 @@ class Dispatcher:
|
|||||||
self.contexts.append(new_ctx)
|
self.contexts.append(new_ctx)
|
||||||
return new_ctx
|
return new_ctx
|
||||||
|
|
||||||
async def call_handler(self, handler, event_object, data):
|
async def call_handler(
|
||||||
|
self,
|
||||||
|
handler: Callable[[Any, dict[str, Any]], Awaitable[Any]],
|
||||||
|
event_object: UpdateType,
|
||||||
|
data: Dict[str, Any]
|
||||||
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Правка аргументов конечной функции хендлера и ее вызов
|
Вызывает хендлер с нужными аргументами.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
handler: Handler.
|
||||||
|
event_object: Объект события.
|
||||||
|
data: Данные для хендлера.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
func_args = handler.func_event.__annotations__.keys()
|
func_args = handler.func_event.__annotations__.keys()
|
||||||
@ -243,6 +277,17 @@ class Dispatcher:
|
|||||||
filters: List[BaseFilter]
|
filters: List[BaseFilter]
|
||||||
) -> Optional[Dict[str, Any]] | Literal[False]:
|
) -> Optional[Dict[str, Any]] | Literal[False]:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Асинхронно применяет фильтры к событию.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (UpdateUnion): Событие.
|
||||||
|
filters (List[BaseFilter]): Список фильтров.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[Dict[str, Any]] | Literal[False]: Словарь с результатом или False.
|
||||||
|
"""
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
for _filter in filters:
|
for _filter in filters:
|
||||||
@ -259,9 +304,10 @@ class Dispatcher:
|
|||||||
async def handle(self, event_object: UpdateUnion):
|
async def handle(self, event_object: UpdateUnion):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Основной обработчик события. Применяет фильтры, middleware и вызывает подходящий handler.
|
Основной обработчик события. Применяет фильтры, middleware и вызывает нужный handler.
|
||||||
|
|
||||||
:param event_object: Событие, пришедшее в бот.
|
Args:
|
||||||
|
event_object (UpdateUnion): Событие.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -335,26 +381,36 @@ class Dispatcher:
|
|||||||
)
|
)
|
||||||
|
|
||||||
kwargs_filtered = {k: v for k, v in kwargs.items() if k in func_args}
|
kwargs_filtered = {k: v for k, v in kwargs.items() if k in func_args}
|
||||||
|
|
||||||
await handler_chain(event_object, kwargs_filtered)
|
try:
|
||||||
|
await handler_chain(event_object, kwargs_filtered)
|
||||||
|
except:
|
||||||
|
raise HandlerException(
|
||||||
|
handler_title=handler.func_event.__name__,
|
||||||
|
memory_context={
|
||||||
|
'data': await memory_context.get_data(),
|
||||||
|
'state': current_state
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
logger_dp.info(f'Обработано: {router_id} | {process_info}')
|
logger_dp.info(f'Обработано: router_id: {router_id} | {process_info}')
|
||||||
|
|
||||||
is_handled = True
|
is_handled = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if not is_handled:
|
if not is_handled:
|
||||||
logger_dp.info(f'Проигнорировано: {router_id} | {process_info}')
|
logger_dp.info(f'Проигнорировано: router_id: {router_id} | {process_info}')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger_dp.error(f"Ошибка при обработке события: {router_id} | {process_info} | {e} ")
|
logger_dp.error(f"Ошибка при обработке события: router_id: {router_id} | {process_info} | {e} ")
|
||||||
|
|
||||||
async def start_polling(self, bot: Bot):
|
async def start_polling(self, bot: Bot):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Запускает цикл получения обновлений с сервера (long polling).
|
Запускает цикл получения обновлений (long polling).
|
||||||
|
|
||||||
:param bot: Экземпляр бота.
|
Args:
|
||||||
|
bot (Bot): Экземпляр бота.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.polling = True
|
self.polling = True
|
||||||
@ -399,9 +455,10 @@ class Dispatcher:
|
|||||||
"""
|
"""
|
||||||
Запускает FastAPI-приложение для приёма обновлений через вебхук.
|
Запускает FastAPI-приложение для приёма обновлений через вебхук.
|
||||||
|
|
||||||
:param bot: Экземпляр бота.
|
Args:
|
||||||
:param host: Хост, на котором запускается сервер.
|
bot (Bot): Экземпляр бота.
|
||||||
:param port: Порт сервера.
|
host (str): Хост сервера.
|
||||||
|
port (int): Порт сервера.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not FASTAPI_INSTALLED:
|
if not FASTAPI_INSTALLED:
|
||||||
@ -422,19 +479,6 @@ class Dispatcher:
|
|||||||
'\n\t pip install maxapi[webhook]'
|
'\n\t pip install maxapi[webhook]'
|
||||||
)
|
)
|
||||||
|
|
||||||
# try:
|
|
||||||
# from fastapi import Request
|
|
||||||
# from fastapi.responses import JSONResponse
|
|
||||||
# except ImportError:
|
|
||||||
# raise ImportError(
|
|
||||||
# '\n\t Не установлен fastapi!'
|
|
||||||
# '\n\t Выполните команду для установки fastapi: '
|
|
||||||
# '\n\t pip install fastapi>=0.68.0'
|
|
||||||
# '\n\t Или сразу все зависимости для работы вебхука:'
|
|
||||||
# '\n\t pip install maxapi[webhook]'
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
@self.webhook_post('/')
|
@self.webhook_post('/')
|
||||||
async def _(request: Request):
|
async def _(request: Request):
|
||||||
event_json = await request.json()
|
event_json = await request.json()
|
||||||
@ -457,24 +501,14 @@ class Dispatcher:
|
|||||||
async def init_serve(self, bot: Bot, host: str = 'localhost', port: int = 8080, **kwargs):
|
async def init_serve(self, bot: Bot, host: str = 'localhost', port: int = 8080, **kwargs):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Запускает сервер для обработки входящих вебхуков.
|
Запускает сервер для обработки вебхуков.
|
||||||
|
|
||||||
:param bot: Экземпляр бота.
|
Args:
|
||||||
:param host: Хост, на котором запускается сервер.
|
bot (Bot): Экземпляр бота.
|
||||||
:param port: Порт сервера.
|
host (str): Хост.
|
||||||
|
port (int): Порт.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# try:
|
|
||||||
# from uvicorn import Config, Server
|
|
||||||
# except ImportError:
|
|
||||||
# raise ImportError(
|
|
||||||
# '\n\t Не установлен uvicorn!'
|
|
||||||
# '\n\t Выполните команду для установки uvicorn: '
|
|
||||||
# '\n\t pip install uvicorn>=0.15.0'
|
|
||||||
# '\n\t Или сразу все зависимости для работы вебхука:'
|
|
||||||
# '\n\t pip install maxapi[webhook]'
|
|
||||||
# )
|
|
||||||
|
|
||||||
if not UVICORN_INSTALLED:
|
if not UVICORN_INSTALLED:
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
'\n\t Не установлен uvicorn!'
|
'\n\t Не установлен uvicorn!'
|
||||||
@ -504,10 +538,10 @@ class Router(Dispatcher):
|
|||||||
def __init__(self, router_id: str | None = None):
|
def __init__(self, router_id: str | None = None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Инициализация диспетчера.
|
Инициализация роутера.
|
||||||
|
|
||||||
:param router_id: Идентификатор роутера, используется при логгировании.
|
Args:
|
||||||
По умолчанию индекс зарегистированного роутера в списке
|
router_id (str | None): Идентификатор роутера для логов.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(router_id)
|
super().__init__(router_id)
|
||||||
@ -524,8 +558,9 @@ class Event:
|
|||||||
"""
|
"""
|
||||||
Инициализирует событие-декоратор.
|
Инициализирует событие-декоратор.
|
||||||
|
|
||||||
:param update_type: Тип события (UpdateType).
|
Args:
|
||||||
:param router: Роутер или диспетчер, в который регистрируется обработчик.
|
update_type (UpdateType): Тип события.
|
||||||
|
router (Dispatcher | Router): Экземпляр роутера или диспетчера.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.update_type = update_type
|
self.update_type = update_type
|
||||||
@ -536,7 +571,8 @@ class Event:
|
|||||||
"""
|
"""
|
||||||
Регистрирует функцию как обработчик события.
|
Регистрирует функцию как обработчик события.
|
||||||
|
|
||||||
:return: Исходная функция.
|
Returns:
|
||||||
|
Callable: Исходная функция.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func_event: Callable):
|
def decorator(func_event: Callable):
|
||||||
|
@ -9,14 +9,19 @@ __all__ = [
|
|||||||
|
|
||||||
|
|
||||||
def filter_attrs(obj: object, *filters: MagicFilter) -> bool:
|
def filter_attrs(obj: object, *filters: MagicFilter) -> bool:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Применяет один или несколько фильтров MagicFilter к объекту.
|
Применяет один или несколько фильтров MagicFilter к объекту.
|
||||||
|
|
||||||
:param obj: Любой объект с атрибутами (например, event/message)
|
Args:
|
||||||
:param filters: Один или несколько MagicFilter выражений
|
obj (object): Объект, к которому применяются фильтры (например, event или message).
|
||||||
:return: True, если все фильтры возвращают True, иначе False
|
*filters (MagicFilter): Один или несколько выражений MagicFilter.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True, если все фильтры возвращают True, иначе False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return all(f.resolve(obj) for f in filters)
|
return all(f.resolve(obj) for f in filters)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
@ -12,12 +12,12 @@ class Command(BaseFilter):
|
|||||||
Фильтр сообщений на соответствие команде.
|
Фильтр сообщений на соответствие команде.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
commands (str | list[str]): Ожидаемая команда или список команд без префикса.
|
commands (str | List[str]): Ожидаемая команда или список команд без префикса.
|
||||||
prefix (str, optional): Префикс команды (по умолчанию '/').
|
prefix (str, optional): Префикс команды (по умолчанию '/').
|
||||||
check_case (bool, optional): Учитывать регистр при сравнении (по умолчанию False).
|
check_case (bool, optional): Учитывать регистр при сравнении (по умолчанию False).
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
commands (list[str]): Список команд без префикса.
|
commands (List[str]): Список команд без префикса.
|
||||||
prefix (str): Префикс команды.
|
prefix (str): Префикс команды.
|
||||||
check_case (bool): Флаг чувствительности к регистру.
|
check_case (bool): Флаг чувствительности к регистру.
|
||||||
"""
|
"""
|
||||||
|
@ -6,5 +6,16 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
class BaseFilter:
|
class BaseFilter:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Базовый класс для фильтров.
|
||||||
|
|
||||||
|
Определяет интерфейс фильтрации событий.
|
||||||
|
Потомки должны переопределять метод __call__.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
__call__(event): Асинхронная проверка события на соответствие фильтру.
|
||||||
|
"""
|
||||||
|
|
||||||
async def __call__(self, event: UpdateUnion) -> bool | dict:
|
async def __call__(self, event: UpdateUnion) -> bool | dict:
|
||||||
return True
|
return True
|
||||||
|
@ -17,27 +17,24 @@ class Handler:
|
|||||||
"""
|
"""
|
||||||
Обработчик события.
|
Обработчик события.
|
||||||
|
|
||||||
Позволяет связать функцию-обработчик с типом обновления, состоянием и набором фильтров.
|
Связывает функцию-обработчик с типом события, состояниями и фильтрами.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
func_event: Callable,
|
func_event: Callable,
|
||||||
update_type: UpdateType,
|
update_type: UpdateType,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Инициализация обработчика.
|
Создаёт обработчик события.
|
||||||
|
|
||||||
:param args: Список фильтров и состояний, в том числе:
|
Args:
|
||||||
- MagicFilter — фильтр события,
|
*args: Список фильтров (MagicFilter, State, Command, BaseFilter, BaseMiddleware).
|
||||||
- State — состояние FSM,
|
func_event (Callable): Функция-обработчик.
|
||||||
- Command — команда для фильтрации по началу текста сообщения.
|
update_type (UpdateType): Тип обновления.
|
||||||
:param func_event: Функция-обработчик события
|
|
||||||
:param update_type: Тип обновления (события), на которое подписан обработчик
|
|
||||||
:param kwargs: Дополнительные параметры (не используются)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.func_event: Callable = func_event
|
self.func_event: Callable = func_event
|
||||||
@ -57,5 +54,6 @@ class Handler:
|
|||||||
elif isinstance(arg, BaseFilter):
|
elif isinstance(arg, BaseFilter):
|
||||||
self.base_filters.append(arg)
|
self.base_filters.append(arg)
|
||||||
else:
|
else:
|
||||||
logger_dp.info(f'Обнаружен неизвестный фильтр `{arg}` при '
|
logger_dp.info(
|
||||||
f'регистрации функции `{func_event.__name__}`')
|
f'Неизвестный фильтр `{arg}` при регистрации `{func_event.__name__}`'
|
||||||
|
)
|
||||||
|
@ -1,10 +1,30 @@
|
|||||||
from typing import Any, Callable, Awaitable
|
from typing import Any, Callable, Awaitable
|
||||||
|
|
||||||
class BaseMiddleware:
|
class BaseMiddleware:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Базовый класс для мидлварей.
|
||||||
|
|
||||||
|
Используется для обработки события до и после вызова хендлера.
|
||||||
|
"""
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
handler: Callable[[Any, dict[str, Any]], Awaitable[Any]],
|
handler: Callable[[Any, dict[str, Any]], Awaitable[Any]],
|
||||||
event_object: Any,
|
event_object: Any,
|
||||||
data: dict[str, Any]
|
data: dict[str, Any]
|
||||||
) -> Any:
|
) -> Any:
|
||||||
return await handler(event_object, data)
|
|
||||||
|
"""
|
||||||
|
Вызывает хендлер с переданным событием и данными.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
handler (Callable): Хендлер события.
|
||||||
|
event_object (Any): Событие.
|
||||||
|
data (dict): Дополнительные данные.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any: Результат работы хендлера.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await handler(event_object, data)
|
||||||
|
@ -113,30 +113,4 @@ class Attachment(BaseModel):
|
|||||||
bot: Optional[Bot] # type: ignore
|
bot: Optional[Bot] # type: ignore
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
use_enum_values = True
|
use_enum_values = True
|
||||||
|
|
||||||
# async def download(
|
|
||||||
# self,
|
|
||||||
# path: str
|
|
||||||
# ):
|
|
||||||
|
|
||||||
# """
|
|
||||||
# Скачивает медиа, сохраняя по определенному пути
|
|
||||||
|
|
||||||
# :param path: Путь сохранения медиа
|
|
||||||
|
|
||||||
# :return: Числовой статус
|
|
||||||
# """
|
|
||||||
|
|
||||||
# if not hasattr(self.payload, 'token') or \
|
|
||||||
# not hasattr(self.payload, 'url'):
|
|
||||||
# raise NotAvailableForDownload()
|
|
||||||
|
|
||||||
# elif not self.payload.token or not self.payload.url:
|
|
||||||
# raise NotAvailableForDownload(f'Медиа типа `{self.type}` недоступно для скачивания')
|
|
||||||
|
|
||||||
# return await self.bot.download_file(
|
|
||||||
# path=path,
|
|
||||||
# url=self.payload.url,
|
|
||||||
# token=self.payload.token,
|
|
||||||
# )
|
|
@ -11,7 +11,6 @@ class Audio(Attachment):
|
|||||||
Вложение с типом аудио.
|
Вложение с типом аудио.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Literal['audio']): Тип вложения, всегда 'audio'.
|
|
||||||
transcription (Optional[str]): Транскрипция аудио (если есть).
|
transcription (Optional[str]): Транскрипция аудио (если есть).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ from .button import Button
|
|||||||
|
|
||||||
class RequestGeoLocationButton(Button):
|
class RequestGeoLocationButton(Button):
|
||||||
|
|
||||||
"""Кнопка запроса геолокации пользователя.
|
"""
|
||||||
|
Кнопка запроса геолокации пользователя.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
quick: Если True, запрашивает геолокацию без дополнительного
|
quick: Если True, запрашивает геолокацию без дополнительного
|
||||||
|
@ -8,9 +8,6 @@ class Contact(Attachment):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Вложение с типом контакта.
|
Вложение с типом контакта.
|
||||||
|
|
||||||
Attributes:
|
|
||||||
type (Literal['contact']): Тип вложения, всегда 'contact'.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: Literal[AttachmentType.CONTACT]
|
type: Literal[AttachmentType.CONTACT]
|
@ -11,7 +11,6 @@ class File(Attachment):
|
|||||||
Вложение с типом файла.
|
Вложение с типом файла.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Literal['file']): Тип вложения, всегда 'file'.
|
|
||||||
filename (Optional[str]): Имя файла.
|
filename (Optional[str]): Имя файла.
|
||||||
size (Optional[int]): Размер файла в байтах.
|
size (Optional[int]): Размер файла в байтах.
|
||||||
"""
|
"""
|
||||||
|
@ -11,7 +11,6 @@ class Location(Attachment):
|
|||||||
Вложение с типом геолокации.
|
Вложение с типом геолокации.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Literal['location']): Тип вложения, всегда 'location'.
|
|
||||||
latitude (Optional[float]): Широта.
|
latitude (Optional[float]): Широта.
|
||||||
longitude (Optional[float]): Долгота.
|
longitude (Optional[float]): Долгота.
|
||||||
"""
|
"""
|
||||||
|
@ -11,7 +11,6 @@ class Share(Attachment):
|
|||||||
Вложение с типом "share" (поделиться).
|
Вложение с типом "share" (поделиться).
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Literal['share']): Тип вложения, всегда 'share'.
|
|
||||||
title (Optional[str]): Заголовок для шаринга.
|
title (Optional[str]): Заголовок для шаринга.
|
||||||
description (Optional[str]): Описание.
|
description (Optional[str]): Описание.
|
||||||
image_url (Optional[str]): URL изображения для предпросмотра.
|
image_url (Optional[str]): URL изображения для предпросмотра.
|
||||||
|
@ -11,7 +11,6 @@ class Sticker(Attachment):
|
|||||||
Вложение с типом стикера.
|
Вложение с типом стикера.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Literal['sticker']): Тип вложения, всегда 'sticker'.
|
|
||||||
width (Optional[int]): Ширина стикера в пикселях.
|
width (Optional[int]): Ширина стикера в пикселях.
|
||||||
height (Optional[int]): Высота стикера в пикселях.
|
height (Optional[int]): Высота стикера в пикселях.
|
||||||
"""
|
"""
|
||||||
|
@ -51,7 +51,6 @@ class Video(Attachment):
|
|||||||
Вложение с типом видео.
|
Вложение с типом видео.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
type (Optional[Literal['video']]): Тип вложения, всегда 'video'.
|
|
||||||
token (Optional[str]): Токен видео.
|
token (Optional[str]): Токен видео.
|
||||||
urls (Optional[VideoUrl]): URLs видео разных разрешений.
|
urls (Optional[VideoUrl]): URLs видео разных разрешений.
|
||||||
thumbnail (VideoThumbnail): Миниатюра видео.
|
thumbnail (VideoThumbnail): Миниатюра видео.
|
||||||
|
@ -6,6 +6,7 @@ from ..enums.upload_type import UploadType
|
|||||||
|
|
||||||
|
|
||||||
class InputMedia:
|
class InputMedia:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Класс для представления медиафайла.
|
Класс для представления медиафайла.
|
||||||
|
|
||||||
@ -15,16 +16,19 @@ class InputMedia:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path: str):
|
def __init__(self, path: str):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Инициализирует объект медиафайла.
|
Инициализирует объект медиафайла.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): Путь к файлу.
|
path (str): Путь к файлу.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.path = path
|
self.path = path
|
||||||
self.type = self.__detect_file_type(path)
|
self.type = self.__detect_file_type(path)
|
||||||
|
|
||||||
def __detect_file_type(self, path: str) -> UploadType:
|
def __detect_file_type(self, path: str) -> UploadType:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Определяет тип файла на основе его содержимого (MIME-типа).
|
Определяет тип файла на основе его содержимого (MIME-типа).
|
||||||
|
|
||||||
@ -34,6 +38,7 @@ class InputMedia:
|
|||||||
Returns:
|
Returns:
|
||||||
UploadType: Тип файла (VIDEO, IMAGE, AUDIO или FILE).
|
UploadType: Тип файла (VIDEO, IMAGE, AUDIO или FILE).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
sample = f.read(4096)
|
sample = f.read(4096)
|
||||||
|
|
||||||
@ -60,6 +65,7 @@ class InputMedia:
|
|||||||
|
|
||||||
|
|
||||||
class InputMediaBuffer:
|
class InputMediaBuffer:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Класс для представления медиафайла из буфера.
|
Класс для представления медиафайла из буфера.
|
||||||
|
|
||||||
@ -69,6 +75,7 @@ class InputMediaBuffer:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, buffer: bytes, filename: str | None = None):
|
def __init__(self, buffer: bytes, filename: str | None = None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Инициализирует объект медиафайла из буфера.
|
Инициализирует объект медиафайла из буфера.
|
||||||
|
|
||||||
@ -76,6 +83,7 @@ class InputMediaBuffer:
|
|||||||
buffer (IO): Буфер с содержимым файла.
|
buffer (IO): Буфер с содержимым файла.
|
||||||
filename (str): Название файла (по умолчанию присваивается uuid4).
|
filename (str): Название файла (по умолчанию присваивается uuid4).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.buffer = buffer
|
self.buffer = buffer
|
||||||
self.type = self.__detect_file_type(buffer)
|
self.type = self.__detect_file_type(buffer)
|
||||||
|
@ -6,6 +6,17 @@ from .update import Update
|
|||||||
|
|
||||||
|
|
||||||
class MessageChatCreated(Update):
|
class MessageChatCreated(Update):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Событие создания чата.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
chat (Chat): Объект чата.
|
||||||
|
title (Optional[str]): Название чата.
|
||||||
|
message_id (Optional[str]): ID сообщения.
|
||||||
|
start_payload (Optional[str]): Payload для старта.
|
||||||
|
"""
|
||||||
|
|
||||||
chat: Chat
|
chat: Chat
|
||||||
title: Optional[str] = None
|
title: Optional[str] = None
|
||||||
message_id: Optional[str] = None
|
message_id: Optional[str] = None
|
||||||
|
@ -5,7 +5,8 @@ from ..types.attachments.attachment import Attachment, ButtonsPayload
|
|||||||
|
|
||||||
class InlineKeyboardBuilder:
|
class InlineKeyboardBuilder:
|
||||||
|
|
||||||
"""Конструктор инлайн-клавиатур.
|
"""
|
||||||
|
Конструктор инлайн-клавиатур.
|
||||||
|
|
||||||
Позволяет удобно собирать кнопки в ряды и формировать из них клавиатуру
|
Позволяет удобно собирать кнопки в ряды и формировать из них клавиатуру
|
||||||
для отправки в сообщениях.
|
для отправки в сообщениях.
|
||||||
@ -16,7 +17,8 @@ class InlineKeyboardBuilder:
|
|||||||
|
|
||||||
def row(self, *buttons: InlineButtonUnion):
|
def row(self, *buttons: InlineButtonUnion):
|
||||||
|
|
||||||
"""Добавить новый ряд кнопок в клавиатуру.
|
"""
|
||||||
|
Добавить новый ряд кнопок в клавиатуру.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*buttons: Произвольное количество кнопок для добавления в ряд.
|
*buttons: Произвольное количество кнопок для добавления в ряд.
|
||||||
@ -26,7 +28,8 @@ class InlineKeyboardBuilder:
|
|||||||
|
|
||||||
def add(self, button: InlineButtonUnion):
|
def add(self, button: InlineButtonUnion):
|
||||||
|
|
||||||
"""Добавить кнопку в последний ряд клавиатуры.
|
"""
|
||||||
|
Добавить кнопку в последний ряд клавиатуры.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
button: Кнопка для добавления.
|
button: Кнопка для добавления.
|
||||||
@ -36,7 +39,8 @@ class InlineKeyboardBuilder:
|
|||||||
|
|
||||||
def as_markup(self):
|
def as_markup(self):
|
||||||
|
|
||||||
"""Собрать клавиатуру в объект для отправки.
|
"""
|
||||||
|
Собрать клавиатуру в объект для отправки.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Объект вложения с типом INLINE_KEYBOARD.
|
Объект вложения с типом INLINE_KEYBOARD.
|
||||||
|
@ -21,9 +21,20 @@ from ..enums.chat_type import ChatType
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..bot import Bot
|
from ..bot import Bot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def enrich_event(event_object: Any, bot: Bot) -> Any:
|
async def enrich_event(event_object: Any, bot: Bot) -> Any:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Дополняет объект события данными чата, пользователя и ссылкой на бота.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event_object (Any): Событие, которое нужно дополнить.
|
||||||
|
bot (Bot): Экземпляр бота.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any: Обновлённый объект события.
|
||||||
|
"""
|
||||||
|
|
||||||
if not bot.auto_requests:
|
if not bot.auto_requests:
|
||||||
return event_object
|
return event_object
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user