这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
112 lines
3.7 KiB
Python
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 {}
|