ForcePilot/backend/package/yuxi/channels/base.py
Kris 1f78c44b03 refactor: 整理并清理项目中的冗余代码与格式问题
这是一个批量整理提交,包含以下主要改动:
1.  删除多处冗余的空行和未使用的导入
2.  修复文件末尾缺少换行符的问题
3.  调整部分模块的导入顺序与代码排版
4.  修复部分配置默认值与策略逻辑
5.  新增多个功能模块与辅助工具
6.  完善异常处理与日志记录
7.  修复速率限制、消息缓存、权限校验等逻辑bug
8.  废弃部分旧有API与配置项并添加警告提示
2026-05-12 14:51:53 +08:00

112 lines
3.7 KiB
Python

from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import AsyncIterator, Awaitable, Callable
from typing import Any, ClassVar
from yuxi.channels.capabilities import CAPS_SIMPLE_TEXT, ChannelCapabilities
from yuxi.channels.meta import ChannelMeta
from yuxi.channels.models import (
ChannelMessage,
ChannelResponse,
ChannelType,
DeliveryResult,
HealthStatus,
)
class BaseChannelAdapter(ABC):
channel_id: ClassVar[str]
channel_type: ClassVar[ChannelType]
text_chunk_limit: ClassVar[int] = 4096
supports_markdown: ClassVar[bool] = False
supports_streaming: ClassVar[bool] = False
streaming_modes: ClassVar[list[str]] = ["off"]
max_media_size_mb: ClassVar[int] = 100
webhook_path: ClassVar[str | None] = None
capabilities: ClassVar[ChannelCapabilities] = CAPS_SIMPLE_TEXT
meta: ClassVar[ChannelMeta] = ChannelMeta(id="", label="")
def __init__(self, config: dict[str, Any] | None = None):
self.config = config or {}
self._message_handler: Callable[[ChannelMessage], Awaitable[None]] | None = None
self._stream_state: dict[str, int] = {}
def _get_stream_state(self, chat_id: str, msg_id: str) -> int:
return self._stream_state.get(f"{chat_id}:{msg_id}", 0)
def _set_stream_state(self, chat_id: str, msg_id: str, state: int) -> None:
self._stream_state[f"{chat_id}:{msg_id}"] = state
def _clear_stream_state(self, chat_id: str, msg_id: str) -> None:
self._stream_state.pop(f"{chat_id}:{msg_id}", None)
@abstractmethod
async def connect(self) -> None: ...
@abstractmethod
async def disconnect(self) -> None: ...
@abstractmethod
async def send(self, response: ChannelResponse) -> DeliveryResult: ...
async def receive(self) -> AsyncIterator[ChannelMessage]:
return
yield # type: ignore[misc]
@abstractmethod
def normalize_inbound(self, raw: Any) -> ChannelMessage: ...
@abstractmethod
def format_outbound(self, response: ChannelResponse) -> Any: ...
@abstractmethod
async def health_check(self) -> HealthStatus: ...
def on_message(self, handler: Callable[[ChannelMessage], Awaitable[None]]) -> None:
self._message_handler = handler
async def _handle_message(self, message: ChannelMessage) -> None:
if self._message_handler:
await self._message_handler(message)
async def send_media(self, chat_id: str, media_type: str, data: Any) -> DeliveryResult:
raise NotImplementedError
async def edit_message(self, chat_id: str, msg_id: str, content: str) -> DeliveryResult:
raise NotImplementedError
async def delete_message(self, chat_id: str, msg_id: str) -> DeliveryResult:
raise NotImplementedError
async def send_reaction(self, chat_id: str, msg_id: str, emoji: str) -> DeliveryResult:
raise NotImplementedError
async def send_stream_chunk(self, chat_id: str, msg_id: str, chunk: str, finished: bool) -> DeliveryResult:
identity = self._build_stream_identity(chat_id, msg_id)
response = ChannelResponse(identity=identity, content=chunk)
return await self.send(response)
def _build_stream_identity(self, chat_id: str, msg_id: str) -> Any:
from yuxi.channels.models import ChannelIdentity
return ChannelIdentity(
channel_id=self.channel_id,
channel_type=self.channel_type,
channel_user_id="",
channel_chat_id=chat_id,
channel_message_id=msg_id,
)
async def verify_webhook_signature(self, headers: dict, body: bytes) -> bool:
return True
async def _refresh_token_if_needed(self) -> bool:
return True
async def pre_connect(self) -> dict:
return {}