From d5ab527f5d51ba47b664faeb35de8a02dbefe435 Mon Sep 17 00:00:00 2001 From: Mikhail Putilovskij Date: Thu, 2 Apr 2026 14:14:19 +0300 Subject: [PATCH] =?UTF-8?q?fix(tg):=20QA=20fixes=20=E2=80=94=20stream=5Fme?= =?UTF-8?q?ssage,=20topic=5Fcreated,=20archive=20reply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sdk/mock.py: stream_message was async def (coroutine), must be async generator with yield — caused TypeError on every user message - topic_events.py: on_topic_created now skips bot-created topics (from_user.id == bot.id); cmd_new already registers them under the correct human user_id - commands.py: cmd_archive now sends "Чат архивирован." confirmation - test_topic_events.py: add bot=SimpleNamespace(id=BOT_ID) to fixture --- adapter/telegram/handlers/commands.py | 1 + adapter/telegram/handlers/topic_events.py | 8 +++++++- sdk/mock.py | 20 ++++++++------------ tests/adapter/telegram/test_topic_events.py | 4 ++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/adapter/telegram/handlers/commands.py b/adapter/telegram/handlers/commands.py index 65efead..3933826 100644 --- a/adapter/telegram/handlers/commands.py +++ b/adapter/telegram/handlers/commands.py @@ -54,6 +54,7 @@ async def cmd_archive(message: Message) -> None: except TelegramBadRequest as e: logger.warning("cmd_archive_bot_error", error=str(e)) db.archive_chat(user_id=user_id, thread_id=thread_id) + await message.answer("Чат архивирован.") logger.info("cmd_archive", user_id=user_id, thread_id=thread_id) diff --git a/adapter/telegram/handlers/topic_events.py b/adapter/telegram/handlers/topic_events.py index 4f899d1..3ad8750 100644 --- a/adapter/telegram/handlers/topic_events.py +++ b/adapter/telegram/handlers/topic_events.py @@ -13,7 +13,13 @@ router = Router(name="topic_events") @router.message(F.forum_topic_created) async def on_topic_created(message: Message) -> None: - """User created a topic via Telegram UI — register it as a new chat.""" + """User created a topic via Telegram UI — register it as a new chat. + + Skip topics created by the bot itself — those are already registered + by cmd_new at the time create_forum_topic() is called. + """ + if message.from_user is None or message.from_user.id == message.bot.id: + return user_id = message.from_user.id thread_id = message.message_thread_id name = message.forum_topic_created.name diff --git a/sdk/mock.py b/sdk/mock.py index 353a774..105b715 100644 --- a/sdk/mock.py +++ b/sdk/mock.py @@ -99,23 +99,19 @@ class MockPlatformClient: attachments: list[Attachment] | None = None, ) -> AsyncIterator[MessageChunk]: """ - Сейчас: один чанк с полным ответом (sync под капотом). - При реальном SDK: заменить на SSE/WebSocket итератор в platform/mock.py. + Сейчас: один чанк с полным ответом. + При реальном SDK: заменить на SSE/WebSocket итератор. Адаптеры переписывать не нужно. """ await self._latency(200, 600) message_id, response, tokens = self._build_response(user_id, chat_id, text, attachments) logger.info("stream_message", user_id=user_id, chat_id=chat_id, message_id=message_id) - - async def _gen() -> AsyncIterator[MessageChunk]: - yield MessageChunk( - message_id=message_id, - delta=response, - finished=True, - tokens_used=tokens, - ) - - return _gen() + yield MessageChunk( + message_id=message_id, + delta=response, + finished=True, + tokens_used=tokens, + ) # --------------------------------------------------------------- settings diff --git a/tests/adapter/telegram/test_topic_events.py b/tests/adapter/telegram/test_topic_events.py index 3de9a78..fb490af 100644 --- a/tests/adapter/telegram/test_topic_events.py +++ b/tests/adapter/telegram/test_topic_events.py @@ -16,6 +16,9 @@ def fresh_db(tmp_path, monkeypatch): return db_mod +BOT_ID = 9999 # distinct from any test user_id + + def make_service_message(*, user_id=1, thread_id=42, topic_name="Мой чат"): m = SimpleNamespace() m.message_thread_id = thread_id @@ -25,6 +28,7 @@ def make_service_message(*, user_id=1, thread_id=42, topic_name="Мой чат") m.forum_topic_edited = SimpleNamespace(name="Новое имя") m.forum_topic_closed = SimpleNamespace() m.answer = AsyncMock() + m.bot = SimpleNamespace(id=BOT_ID) return m