新增QQ Bot适配器完整代码栈,包含: 1. 基础适配器入口与工具类封装 2. 会话管理、重试队列与流量控制 3. 命令系统与内置指令(ping/help/status等) 4. 富媒体消息处理与格式转换 5. 引用存储与审批管理 6. 凭证备份与会话持久化 7. 健康检查与交互回调系统
85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
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)
|