Добавлен Middleware
This commit is contained in:
parent
8aa9c65fcc
commit
512eb9a4af
@ -66,12 +66,16 @@ if __name__ == '__main__':
|
||||
- [Обработчик доступных событий](https://github.com/love-apples/maxapi/blob/main/examples/events/main.py)
|
||||
- [Обработчики с MagicFilter](https://github.com/love-apples/maxapi/blob/main/examples/magic_filters/main.py)
|
||||
- [Демонстрация роутинга, InputMedia и механика контекста](https://github.com/love-apples/maxapi/tree/main/examples/router_with_input_media) (audio.mp3 для команды /media)
|
||||
- [Получение ID](https://github.com/love-apples/maxapi/tree/main/examples/get_ids/main.py)
|
||||
- [Миддлварь в хендлерах](https://github.com/love-apples/maxapi/tree/main/examples/middleware_in_handlers/main.py)
|
||||
- [Миддлварь в роутерах](https://github.com/love-apples/maxapi/tree/main/examples/middleware_for_router/main.py)
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 🧩 Возможности
|
||||
|
||||
- ✅ Middleware
|
||||
- ✅ Роутеры
|
||||
- ✅ Билдер инлайн клавиатур
|
||||
- ✅ Простая загрузка медиафайлов
|
||||
|
39
examples/get_ids/main.py
Normal file
39
examples/get_ids/main.py
Normal file
@ -0,0 +1,39 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from maxapi import Bot, Dispatcher, F
|
||||
from maxapi.enums.parse_mode import ParseMode
|
||||
from maxapi.types import MessageCreated
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
bot = Bot('тут_ваш_токен')
|
||||
dp = Dispatcher()
|
||||
|
||||
|
||||
@dp.message_created(F.message.link.type == 'forward')
|
||||
async def get_ids_from_forward(event: MessageCreated):
|
||||
text = (
|
||||
'Информация о пересланном сообщении:\n\n'
|
||||
|
||||
f'Из чата: <b>{event.message.link.chat_id}</b>\n'
|
||||
f'От пользователя: <b>{event.message.link.sender.user_id}</b>'
|
||||
)
|
||||
await event.message.reply(text)
|
||||
|
||||
|
||||
@dp.message_created()
|
||||
async def get_ids(event: MessageCreated):
|
||||
text = (
|
||||
f'Ваш ID: <b>{event.from_user.user_id}</b>\n'
|
||||
f'ID этого чата: <b>{event.chat.chat_id}</b>'
|
||||
)
|
||||
await event.message.answer(text, parse_mode=ParseMode.HTML)
|
||||
|
||||
|
||||
async def main():
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
41
examples/middleware_for_router/main.py
Normal file
41
examples/middleware_for_router/main.py
Normal file
@ -0,0 +1,41 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
from maxapi import Bot, Dispatcher
|
||||
from maxapi.types import MessageCreated, Command, UpdateUnion
|
||||
from maxapi.filters.middleware import BaseMiddleware
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
bot = Bot(token='тут_ваш_токен')
|
||||
dp = Dispatcher()
|
||||
|
||||
|
||||
class CustomDataForRouterMiddleware(BaseMiddleware):
|
||||
async def __call__(
|
||||
self,
|
||||
event: UpdateUnion,
|
||||
data: Dict[str, Any]
|
||||
):
|
||||
|
||||
data['custom_data'] = f'Это ID того кто вызвал команду: {event.from_user.user_id}'
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@dp.message_created(Command('custom_data'))
|
||||
async def custom_data(event: MessageCreated, custom_data: str):
|
||||
await event.message.answer(custom_data)
|
||||
|
||||
|
||||
async def main():
|
||||
dp.middlewares = [
|
||||
CustomDataForRouterMiddleware()
|
||||
]
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
59
examples/middleware_in_handlers/main.py
Normal file
59
examples/middleware_in_handlers/main.py
Normal file
@ -0,0 +1,59 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
from maxapi import Bot, Dispatcher
|
||||
from maxapi.filters.middleware import BaseMiddleware
|
||||
from maxapi.types import MessageCreated, Command, UpdateUnion
|
||||
from maxapi.types.command import Command
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
bot = Bot(token='тут_ваш_токен')
|
||||
dp = Dispatcher()
|
||||
|
||||
|
||||
class CheckChatTitleMiddleware(BaseMiddleware):
|
||||
async def __call__(
|
||||
self,
|
||||
event: UpdateUnion,
|
||||
):
|
||||
|
||||
return event.chat.title == 'MAXApi'
|
||||
|
||||
|
||||
@dp.message_created(Command('start'), CheckChatTitleMiddleware())
|
||||
async def start(event: MessageCreated):
|
||||
await event.message.answer('Это сообщение было отправлено, так как ваш чат называется "MAXApi"!')
|
||||
|
||||
|
||||
class CustomDataMiddleware(BaseMiddleware):
|
||||
async def __call__(
|
||||
self,
|
||||
event: UpdateUnion,
|
||||
data: Dict[str, Any]
|
||||
):
|
||||
|
||||
data['custom_data'] = f'Это ID того кто вызвал команду: {event.from_user.user_id}'
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@dp.message_created(Command('custom_data'), CustomDataMiddleware())
|
||||
async def custom_data(event: MessageCreated, custom_data: str):
|
||||
await event.message.answer(custom_data)
|
||||
|
||||
|
||||
@dp.message_created(Command('many_middlewares'), CheckChatTitleMiddleware(), CustomDataMiddleware())
|
||||
async def many_middlewares(event: MessageCreated, custom_data: str):
|
||||
await event.message.answer('Это сообщение было отправлено, так как ваш чат называется "MAXApi"!')
|
||||
await event.message.answer(custom_data)
|
||||
|
||||
|
||||
async def main():
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
@ -1,4 +1,4 @@
|
||||
from typing import Callable, List
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
@ -6,6 +6,8 @@ from magic_filter import MagicFilter
|
||||
from uvicorn import Config, Server
|
||||
from aiohttp import ClientConnectorError
|
||||
|
||||
from maxapi.filters.middleware import BaseMiddleware
|
||||
|
||||
from .filters.handler import Handler
|
||||
|
||||
from .context import MemoryContext
|
||||
@ -36,6 +38,8 @@ class Dispatcher:
|
||||
self.contexts: List[MemoryContext] = []
|
||||
self.routers: List[Router] = []
|
||||
self.filters: List[MagicFilter] = []
|
||||
self.middlewares: List[BaseMiddleware] = []
|
||||
|
||||
self.bot = None
|
||||
self.on_started_func = None
|
||||
|
||||
@ -78,7 +82,7 @@ class Dispatcher:
|
||||
|
||||
handlers_count = 0
|
||||
for router in self.routers:
|
||||
for handler in router.event_handlers:
|
||||
for _ in router.event_handlers:
|
||||
handlers_count += 1
|
||||
|
||||
logger_dp.info(f'{handlers_count} событий на обработку')
|
||||
@ -106,6 +110,30 @@ class Dispatcher:
|
||||
self.contexts.append(new_ctx)
|
||||
return new_ctx
|
||||
|
||||
async def process_middlewares(
|
||||
self,
|
||||
middlewares: List[BaseMiddleware],
|
||||
event_object: UpdateUnion,
|
||||
result_data_kwargs: Dict[str, Any]
|
||||
):
|
||||
|
||||
for middleware in middlewares:
|
||||
result = await middleware.process_middleware(
|
||||
event_object=event_object,
|
||||
result_data_kwargs=result_data_kwargs
|
||||
)
|
||||
|
||||
if result == None or result == False:
|
||||
return
|
||||
|
||||
elif result == True:
|
||||
result = {}
|
||||
|
||||
for key, value in result.items():
|
||||
result_data_kwargs[key] = value
|
||||
|
||||
return result_data_kwargs
|
||||
|
||||
async def handle(self, event_object: UpdateUnion):
|
||||
|
||||
"""Обрабатывает событие.
|
||||
@ -113,54 +141,68 @@ class Dispatcher:
|
||||
Args:
|
||||
event_object: Объект события для обработки
|
||||
"""
|
||||
ids = event_object.get_ids()
|
||||
try:
|
||||
ids = event_object.get_ids()
|
||||
memory_context = self.__get_memory_context(*ids)
|
||||
kwargs = {'context': memory_context}
|
||||
|
||||
is_handled = False
|
||||
is_handled = False
|
||||
|
||||
for router in self.routers:
|
||||
for router in self.routers:
|
||||
|
||||
if is_handled:
|
||||
break
|
||||
if is_handled:
|
||||
break
|
||||
|
||||
if router.filters:
|
||||
if not filter_attrs(event_object, *router.filters):
|
||||
continue
|
||||
|
||||
for handler in router.event_handlers:
|
||||
|
||||
if not handler.update_type == event_object.update_type:
|
||||
continue
|
||||
|
||||
if handler.filters:
|
||||
if not filter_attrs(event_object, *handler.filters):
|
||||
if router.filters:
|
||||
if not filter_attrs(event_object, *router.filters):
|
||||
continue
|
||||
|
||||
memory_context = self.__get_memory_context(*ids)
|
||||
kwargs = await self.process_middlewares(
|
||||
middlewares=router.middlewares,
|
||||
event_object=event_object,
|
||||
result_data_kwargs=kwargs
|
||||
)
|
||||
|
||||
if not handler.state == await memory_context.get_state() \
|
||||
and handler.state:
|
||||
continue
|
||||
for handler in router.event_handlers:
|
||||
|
||||
func_args = handler.func_event.__annotations__.keys()
|
||||
if not handler.update_type == event_object.update_type:
|
||||
continue
|
||||
|
||||
kwargs = {'context': memory_context}
|
||||
if handler.filters:
|
||||
if not filter_attrs(event_object, *handler.filters):
|
||||
continue
|
||||
|
||||
for key in kwargs.copy().keys():
|
||||
if not key in func_args:
|
||||
del kwargs[key]
|
||||
if not handler.state == await memory_context.get_state() \
|
||||
and handler.state:
|
||||
continue
|
||||
|
||||
if handler.middleware:
|
||||
await handler.middleware()
|
||||
func_args = handler.func_event.__annotations__.keys()
|
||||
|
||||
await handler.func_event(event_object, **kwargs)
|
||||
kwargs = await self.process_middlewares(
|
||||
middlewares=handler.middlewares,
|
||||
event_object=event_object,
|
||||
result_data_kwargs=kwargs
|
||||
)
|
||||
|
||||
logger_dp.info(f'Обработано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
|
||||
if not kwargs:
|
||||
continue
|
||||
|
||||
is_handled = True
|
||||
break
|
||||
for key in kwargs.copy().keys():
|
||||
if not key in func_args:
|
||||
del kwargs[key]
|
||||
|
||||
if not is_handled:
|
||||
logger_dp.info(f'Проигнорировано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
|
||||
await handler.func_event(event_object, **kwargs)
|
||||
|
||||
logger_dp.info(f'Обработано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
|
||||
|
||||
is_handled = True
|
||||
break
|
||||
|
||||
if not is_handled:
|
||||
logger_dp.info(f'Проигнорировано: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]}')
|
||||
|
||||
except Exception as e:
|
||||
logger_dp.error(f"Ошибка при обработке события: {event_object.update_type} | chat_id: {ids[0]}, user_id: {ids[1]} | {e} ")
|
||||
|
||||
async def start_polling(self, bot: Bot):
|
||||
|
||||
@ -187,10 +229,7 @@ class Dispatcher:
|
||||
)
|
||||
|
||||
for event in processed_events:
|
||||
try:
|
||||
await self.handle(event)
|
||||
except Exception as e:
|
||||
logger_dp.error(f"Ошибка при обработке события: {event.update_type}: {e}")
|
||||
await self.handle(event)
|
||||
except ClientConnectorError:
|
||||
logger_dp.error(f'Ошибка подключения: {e}')
|
||||
except Exception as e:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Callable
|
||||
from typing import Callable, List
|
||||
|
||||
from magic_filter import F, MagicFilter
|
||||
|
||||
@ -45,7 +45,7 @@ class Handler:
|
||||
self.update_type: UpdateType = update_type
|
||||
self.filters = []
|
||||
self.state: State = None
|
||||
self.middleware: BaseMiddleware = None
|
||||
self.middlewares: List[BaseMiddleware] = []
|
||||
|
||||
for arg in args:
|
||||
if isinstance(arg, MagicFilter):
|
||||
@ -55,7 +55,7 @@ class Handler:
|
||||
elif isinstance(arg, Command):
|
||||
self.filters.insert(0, F.message.body.text.startswith(arg.command))
|
||||
elif isinstance(arg, BaseMiddleware):
|
||||
self.middleware = arg
|
||||
self.middlewares.append(arg)
|
||||
else:
|
||||
logger_dp.info(f'Обнаружен неизвестный фильтр `{arg}` при '
|
||||
f'регистрации функции `{func_event.__name__}`')
|
@ -1,6 +1,23 @@
|
||||
from typing import Any, Dict
|
||||
from ..types.updates import UpdateUnion
|
||||
|
||||
|
||||
class BaseMiddleware:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
async def process_middleware(
|
||||
self,
|
||||
result_data_kwargs: Dict[str, Any],
|
||||
event_object: UpdateUnion
|
||||
):
|
||||
|
||||
kwargs_temp = {'data': result_data_kwargs.copy()}
|
||||
|
||||
for key in kwargs_temp.copy().keys():
|
||||
if not key in self.__call__.__annotations__.keys():
|
||||
del kwargs_temp[key]
|
||||
|
||||
result: Dict[str, Any] = await self(event_object, **kwargs_temp)
|
||||
|
||||
return result
|
@ -9,6 +9,7 @@ from ..types.updates.message_edited import MessageEdited
|
||||
from ..types.updates.message_removed import MessageRemoved
|
||||
from ..types.updates.user_added import UserAdded
|
||||
from ..types.updates.user_removed import UserRemoved
|
||||
from ..types.updates import UpdateUnion
|
||||
|
||||
from ..types.attachments.attachment import PhotoAttachmentPayload
|
||||
from ..types.attachments.attachment import OtherAttachmentPayload
|
||||
@ -20,12 +21,14 @@ from ..types.attachments.buttons.chat_button import ChatButton
|
||||
from ..types.attachments.buttons.link_button import LinkButton
|
||||
from ..types.attachments.buttons.request_contact import RequestContact
|
||||
from ..types.attachments.buttons.request_geo_location_button import RequestGeoLocationButton
|
||||
from ..types.message import Message
|
||||
|
||||
from ..types.command import Command, BotCommand
|
||||
|
||||
from .input_media import InputMedia
|
||||
|
||||
__all__ = [
|
||||
UpdateUnion,
|
||||
InputMedia,
|
||||
BotCommand,
|
||||
CallbackButton,
|
||||
|
@ -1,5 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from maxapi.enums.button_type import ButtonType
|
||||
|
||||
from .button import Button
|
||||
|
||||
|
||||
@ -7,7 +9,6 @@ class ChatButton(Button):
|
||||
|
||||
"""
|
||||
Attributes:
|
||||
type: Тип кнопки (наследуется от Button)
|
||||
text: Текст кнопки (наследуется от Button)
|
||||
chat_title: Название чата (до 128 символов)
|
||||
chat_description: Описание чата (до 256 символов)
|
||||
@ -15,6 +16,7 @@ class ChatButton(Button):
|
||||
uuid: Уникальный идентификатор чата
|
||||
"""
|
||||
|
||||
type: ButtonType = ButtonType.CHAT
|
||||
chat_title: Optional[str] = None
|
||||
chat_description: Optional[str] = None
|
||||
start_payload: Optional[str] = None
|
||||
|
Loading…
x
Reference in New Issue
Block a user