From 8b36944fdf462805bcba3d0e71befd3161e7defa Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 28 Apr 2026 17:11:17 +0800 Subject: [PATCH] feat: add attachment saved event handling in chat and live chat routes --- astrbot/dashboard/routes/chat.py | 29 +++++++++++++++++++++++++++ astrbot/dashboard/routes/live_chat.py | 20 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/astrbot/dashboard/routes/chat.py b/astrbot/dashboard/routes/chat.py index dea3cca3df..5ff1913b9e 100644 --- a/astrbot/dashboard/routes/chat.py +++ b/astrbot/dashboard/routes/chat.py @@ -819,6 +819,19 @@ async def flush_pending_bot_message(): refs = {} return saved_record + def build_attachment_saved_event(part: dict | None) -> str | None: + if not part or not part.get("attachment_id") or not part.get("type"): + return None + + payload = { + "type": "attachment_saved", + "data": { + "id": part["attachment_id"], + "type": part["type"], + }, + } + return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n" + try: # Emit session_id first so clients can bind the stream immediately. session_info = { @@ -908,12 +921,20 @@ async def flush_pending_bot_message(): filename, "image" ) message_accumulator.add_attachment(part) + if attachment_saved_event := build_attachment_saved_event( + part + ): + yield attachment_saved_event elif msg_type == "record": filename = result_text.replace("[RECORD]", "") part = await self._create_attachment_from_file( filename, "record" ) message_accumulator.add_attachment(part) + if attachment_saved_event := build_attachment_saved_event( + part + ): + yield attachment_saved_event elif msg_type == "file": # 格式: [FILE]filename filename = result_text.replace("[FILE]", "") @@ -921,12 +942,20 @@ async def flush_pending_bot_message(): filename, "file" ) message_accumulator.add_attachment(part) + if attachment_saved_event := build_attachment_saved_event( + part + ): + yield attachment_saved_event elif msg_type == "video": filename = result_text.replace("[VIDEO]", "") part = await self._create_attachment_from_file( filename, "video" ) message_accumulator.add_attachment(part) + if attachment_saved_event := build_attachment_saved_event( + part + ): + yield attachment_saved_event should_save = False if msg_type == "end": diff --git a/astrbot/dashboard/routes/live_chat.py b/astrbot/dashboard/routes/live_chat.py index 8fe113fcbe..d7705882db 100644 --- a/astrbot/dashboard/routes/live_chat.py +++ b/astrbot/dashboard/routes/live_chat.py @@ -537,6 +537,22 @@ async def flush_pending_bot_message(): pending_bot_message_flusher = flush_pending_bot_message + async def send_attachment_saved_event(part: dict | None) -> None: + if not part or not part.get("attachment_id") or not part.get("type"): + return + + await self._send_chat_payload( + session, + { + "ct": "chat", + "type": "attachment_saved", + "data": { + "id": part["attachment_id"], + "type": part["type"], + }, + }, + ) + while True: if session.should_interrupt: session.should_interrupt = False @@ -586,18 +602,22 @@ async def flush_pending_bot_message(): filename = str(result_text).replace("[IMAGE]", "") part = await self._create_attachment_from_file(filename, "image") message_accumulator.add_attachment(part) + await send_attachment_saved_event(part) elif msg_type == "record": filename = str(result_text).replace("[RECORD]", "") part = await self._create_attachment_from_file(filename, "record") message_accumulator.add_attachment(part) + await send_attachment_saved_event(part) elif msg_type == "file": filename = str(result_text).replace("[FILE]", "").split("|", 1)[0] part = await self._create_attachment_from_file(filename, "file") message_accumulator.add_attachment(part) + await send_attachment_saved_event(part) elif msg_type == "video": filename = str(result_text).replace("[VIDEO]", "").split("|", 1)[0] part = await self._create_attachment_from_file(filename, "video") message_accumulator.add_attachment(part) + await send_attachment_saved_event(part) should_save = False if msg_type == "end":