Parcourir la source

refactor(bot-middlewares): adapt to new ResponsesPorts API and add LanguageMiddleware to push user language through pipeline

Librellium il y a 1 semaine
Parent
commit
30e6a48642

+ 0 - 22
anonflow/bot/middleware/banned.py

@@ -1,22 +0,0 @@
-from aiogram import BaseMiddleware
-from aiogram.types import Message
-
-from anonflow.services import MessageRouter, ModeratorService
-from anonflow.services.transport.results import UserBannedResult
-
-
-class BannedMiddleware(BaseMiddleware):
-    def __init__(self, message_router: MessageRouter, moderator_service: ModeratorService):
-        super().__init__()
-
-        self.message_router = message_router
-        self.moderator_service = moderator_service
-
-    async def __call__(self, handler, event, data):
-        message = getattr(event, "message", None)
-        if isinstance(message, Message):
-            if await self.moderator_service.is_banned(message.chat.id):
-                await self.message_router.dispatch(UserBannedResult(), message)
-                return
-
-        return await handler(event, data)

+ 0 - 67
anonflow/bot/middleware/throttling.py

@@ -1,67 +0,0 @@
-import asyncio
-import time
-from typing import Dict, Iterable, Optional
-
-from aiogram import BaseMiddleware
-from aiogram.types import ChatIdUnion, Message
-
-from anonflow.services import MessageRouter
-from anonflow.services.transport.results import UserThrottledResult
-
-
-class ThrottlingMiddleware(BaseMiddleware):
-    def __init__(
-        self,
-        message_router: MessageRouter,
-        delay: float,
-        allowed_chat_ids: Optional[Iterable[ChatIdUnion]]
-    ):
-        super().__init__()
-
-        self.message_router = message_router
-        self.delay = delay
-        self.allowed_chat_ids = allowed_chat_ids
-
-        self.user_times: Dict[int, float] = {}
-        self.user_locks: Dict[int, asyncio.Lock] = {}
-
-        self.lock = asyncio.Lock()
-
-    async def __call__(self, handler, event, data):
-        message = getattr(event, "message", None)
-        if isinstance(message, Message) and message.chat.id not in self.allowed_chat_ids:
-            text = message.text or message.caption or ""
-            if not text.startswith("/"):
-                async with self.lock:
-                    user_lock = self.user_locks.setdefault(message.chat.id, asyncio.Lock())
-
-                if user_lock.locked():
-                    start_time = self.user_times.get(message.chat.id) or 0
-                    current_time = time.monotonic()
-
-                    await self.message_router.dispatch(
-                        UserThrottledResult(
-                            remaining_time=(
-                                round(self.delay - (current_time - start_time))
-                                if start_time else 0
-                            )
-                        ),
-                        message
-                    )
-                    return
-
-                async with user_lock:
-                    start_time = time.monotonic()
-                    self.user_times[message.chat.id] = start_time
-
-                    result = await handler(event, data)
-
-                    elapsed_time = time.monotonic() - start_time
-                    await asyncio.sleep(max(0, self.delay - elapsed_time))
-
-                async with self.lock:
-                    self.user_locks.pop(message.chat.id)
-
-                return result
-
-        return await handler(event, data)

+ 2 - 0
anonflow/bot/middleware/__init__.py → anonflow/bot/middlewares/__init__.py

@@ -1,10 +1,12 @@
 from .banned import BannedMiddleware
+from .language import LanguageMiddleware
 from .not_registered import NotRegisteredMiddleware
 from .subscription import SubscriptionMiddleware
 from .throttling import ThrottlingMiddleware
 
 __all__ = [
     "BannedMiddleware",
+    "LanguageMiddleware",
     "NotRegisteredMiddleware",
     "SubscriptionMiddleware",
     "ThrottlingMiddleware"

+ 23 - 0
anonflow/bot/middlewares/banned.py

@@ -0,0 +1,23 @@
+from aiogram import BaseMiddleware
+from aiogram.types import Message
+
+from anonflow.interfaces import UserResponsesPort
+from anonflow.services import ModeratorService
+from anonflow.services.transport.types import RequestContext
+
+
+class BannedMiddleware(BaseMiddleware):
+    def __init__(self, responses_port: UserResponsesPort, moderator_service: ModeratorService):
+        super().__init__()
+
+        self._responses_port = responses_port
+        self._moderator_service = moderator_service
+
+    async def __call__(self, handler, event, data):
+        message = getattr(event, "message", None)
+        if isinstance(message, Message):
+            if await self._moderator_service.is_banned(message.chat.id):
+                await self._responses_port.user_banned(RequestContext(message.chat.id, data["user_language"]))
+                return
+
+        return await handler(event, data)

+ 24 - 0
anonflow/bot/middlewares/language.py

@@ -0,0 +1,24 @@
+from aiogram import BaseMiddleware
+from aiogram.types import Message
+
+from anonflow.services import UserService
+
+
+class LanguageMiddleware(BaseMiddleware):
+    def __init__(self, user_service: UserService):
+        super().__init__()
+
+        self._user_service = user_service
+
+    async def __call__(self, handler, event, data):
+        data["user_language"] = None
+
+        message = getattr(event, "message", None)
+        if isinstance(message, Message) and message.from_user:
+            user = await self._user_service.get(message.from_user.id)
+            data["user_language"] = (
+                user.language
+                if user else message.from_user.language_code
+            )
+
+        return await handler(event, data)

+ 8 - 7
anonflow/bot/middleware/not_registered.py → anonflow/bot/middlewares/not_registered.py

@@ -2,25 +2,26 @@ from aiogram import BaseMiddleware
 from aiogram.enums import ChatType
 from aiogram.types import Message
 
-from anonflow.services import MessageRouter, UserService
-from anonflow.services.transport.results import UserNotRegisteredResult
+from anonflow.interfaces import UserResponsesPort
+from anonflow.services import UserService
+from anonflow.services.transport.types import RequestContext
 
 
 class NotRegisteredMiddleware(BaseMiddleware):
-    def __init__(self, message_router: MessageRouter, user_service: UserService):
+    def __init__(self, responses_port: UserResponsesPort, user_service: UserService):
         super().__init__()
 
-        self.message_router = message_router
-        self.user_service = user_service
+        self._responses_port = responses_port
+        self._user_service = user_service
 
     async def __call__(self, handler, event, data):
         message = getattr(event, "message", None)
         if isinstance(message, Message) and message.chat.type == ChatType.PRIVATE:
             text = message.text or message.caption or ""
 
-            is_user_exists = await self.user_service.has(message.chat.id)
+            is_user_exists = await self._user_service.has(message.chat.id)
             if not is_user_exists and not text.startswith("/start"):
-                await self.message_router.dispatch(UserNotRegisteredResult(), message)
+                await self._responses_port.user_not_registered(RequestContext(message.chat.id, data["user_language"]))
                 return
 
         return await handler(event, data)

+ 7 - 7
anonflow/bot/middleware/subscription.py → anonflow/bot/middlewares/subscription.py

@@ -4,25 +4,25 @@ from aiogram import BaseMiddleware
 from aiogram.enums import ChatMemberStatus, ChatType
 from aiogram.types import ChatIdUnion, Message
 
-from anonflow.services import MessageRouter
-from anonflow.services.transport.results import UserSubscriptionRequiredResult
+from anonflow.interfaces import UserResponsesPort
+from anonflow.services.transport.types import RequestContext
 
 
 class SubscriptionMiddleware(BaseMiddleware):
-    def __init__(self, channel_ids: Tuple[ChatIdUnion], message_router: MessageRouter):
+    def __init__(self, responses_port: UserResponsesPort, channel_ids: Tuple[ChatIdUnion]):
         super().__init__()
 
-        self.channel_ids = channel_ids
-        self.message_router = message_router
+        self._responses_port = responses_port
+        self._channel_ids = channel_ids
 
     async def __call__(self, handler, event, data):
         message = getattr(event, "message", None)
         if isinstance(message, Message) and message.chat.type == ChatType.PRIVATE:
             user_id = message.from_user.id # type: ignore
-            for channel_id in self.channel_ids:
+            for channel_id in self._channel_ids:
                 member = await message.bot.get_chat_member(channel_id, user_id) # type: ignore
                 if member.status in (ChatMemberStatus.KICKED, ChatMemberStatus.LEFT):
-                    await self.message_router.dispatch(UserSubscriptionRequiredResult(), message)
+                    await self._responses_port.user_subscription_required(RequestContext(message.chat.id, data["user_language"]))
                     return
 
         return await handler(event, data)

+ 71 - 0
anonflow/bot/middlewares/throttling.py

@@ -0,0 +1,71 @@
+import asyncio
+import time
+from typing import Dict, Iterable, Optional
+
+from aiogram import BaseMiddleware
+from aiogram.types import ChatIdUnion, Message
+
+from anonflow.interfaces import UserResponsesPort
+from anonflow.services.transport.types import RequestContext
+
+
+class ThrottlingMiddleware(BaseMiddleware):
+    def __init__(
+        self,
+        responses_port: UserResponsesPort,
+        delay: float,
+        allowed_chat_ids: Optional[Iterable[ChatIdUnion]]
+    ):
+        super().__init__()
+
+        self._responses_port = responses_port
+        self._delay = delay
+        self._allowed_chat_ids = allowed_chat_ids
+
+        self._user_times: Dict[int, float] = {}
+        self._user_locks: Dict[int, asyncio.Lock] = {}
+
+        self._lock = asyncio.Lock()
+
+    async def __call__(self, handler, event, data):
+        message = getattr(event, "message", None)
+        if (
+            isinstance(message, Message)
+            and (
+                self._allowed_chat_ids is not None
+                and message.chat.id not in self._allowed_chat_ids
+            )
+        ):
+            text = message.text or message.caption or ""
+            if not text.startswith("/"):
+                async with self._lock:
+                    user_lock = self._user_locks.setdefault(message.chat.id, asyncio.Lock())
+
+                if user_lock.locked():
+                    start_time = self._user_times.get(message.chat.id) or 0
+                    current_time = time.monotonic()
+
+                    await self._responses_port.user_throttled(
+                        RequestContext(message.chat.id, data["user_language"]),
+                        remaining_time=(
+                            round(self._delay - (current_time - start_time))
+                            if start_time else 0
+                        )
+                    )
+                    return
+
+                async with user_lock:
+                    start_time = time.monotonic()
+                    self._user_times[message.chat.id] = start_time
+
+                    result = await handler(event, data)
+
+                    elapsed_time = time.monotonic() - start_time
+                    await asyncio.sleep(max(0, self._delay - elapsed_time))
+
+                async with self._lock:
+                    self._user_locks.pop(message.chat.id)
+
+                return result
+
+        return await handler(event, data)