from __future__ import annotations import logging from enum import Enum, auto from typing import Any from yuxi.channels.adapters.qqbot.send import render_reply_payload from yuxi.channels.pipeline.context import PipelineContext logger = logging.getLogger(__name__) class ReplyMode(Enum): DIRECT = auto() STREAMING = auto() MARKDOWN = auto() FALLBACK = auto() class ReplyDispatcher: def __init__(self, adapter: Any) -> None: self._adapter = adapter async def reply(self, ctx: PipelineContext, text: str) -> Any: if ctx.chat_type == "interaction": return await self._reply_interaction(ctx, text) streaming_ready = self._check_streaming_ready(ctx) if streaming_ready: return await self._reply_streaming(ctx) if len(text) > 2000: return await self._reply_markdown(ctx, text) return await self._reply_direct(ctx, text) def _check_streaming_ready(self, ctx: PipelineContext) -> bool: if ctx.chat_type != "dm": return False adapter = self._adapter c2c_ctrl = getattr(adapter, "_c2c_streaming", None) return c2c_ctrl is not None async def _reply_direct(self, ctx: PipelineContext, text: str) -> Any: if ctx.chat_type == "group": return await self._adapter.send_group_message(ctx.chat_id, text, msg_id=ctx.msg_id) return await self._adapter.send_dm_message(ctx.chat_id, text, msg_id=ctx.msg_id) async def _reply_streaming(self, ctx: PipelineContext) -> Any: c2c_ctrl = self._adapter._c2c_streaming if c2c_ctrl is None: return None msg_id = ctx.metadata.get("stream_msg_id", "") if not msg_id: msg_id = ctx.msg_id return await c2c_ctrl.stream( chat_id=ctx.chat_id, msg_id=msg_id, content_generator=self._adapter._stream_content(ctx), event_id=ctx.metadata.get("event_id", ""), ) async def _reply_markdown(self, ctx: PipelineContext, text: str) -> Any: chunks = self._adapter._markdown_chunker.chunk(text) results = [] for i, chunk in enumerate(chunks): payload = render_reply_payload( chunk, msg_type=2, msg_id=ctx.msg_id, chunk_index=i, total_chunks=len(chunks) ) if ctx.chat_type == "group": result = await self._adapter.send_group_message( ctx.chat_id, content="", payload=payload, msg_id=ctx.msg_id ) else: result = await self._adapter.send_dm_message( ctx.chat_id, content="", payload=payload, msg_id=ctx.msg_id ) results.append(result) return results async def _reply_interaction(self, ctx: PipelineContext, text: str) -> Any: return await self._adapter._put_interaction(ctx.metadata.get("interaction_id", ""), ctx.content)