新增大量渠道适配器相关的协议、策略、工具类与基础设施代码,包括: 1. 多协议定义:认证、消息、配置、网关等核心接口 2. 策略模块:上下文、群聊、去重、防抖等业务策略 3. 工具集:重试、去重、文本分块、消息格式化等SDK工具 4. 基础设施:外部进程管理、事件广播、熔断机制等 5. 账户与管道系统:账户管理、消息处理管道实现 6. 运行时服务:状态收集、维护任务、日志等后台服务
102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from collections.abc import Awaitable, Callable
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from yuxi.channels.models import ChannelAccountSnapshot
|
|
|
|
|
|
@dataclass
|
|
class ChatAbortEntry:
|
|
task: Any = None
|
|
event: asyncio.Event = field(default_factory=asyncio.Event)
|
|
|
|
def abort(self) -> None:
|
|
self.event.set()
|
|
if self.task is not None and not self.task.done():
|
|
self.task.cancel()
|
|
|
|
def is_aborted(self) -> bool:
|
|
return self.event.is_set()
|
|
|
|
|
|
@dataclass
|
|
class ChatRunBuffer:
|
|
run_id: str = ""
|
|
_chunks: list[str] = field(default_factory=list)
|
|
_finished: bool = False
|
|
|
|
def append_chunk(self, chunk: str) -> None:
|
|
self._chunks.append(chunk)
|
|
|
|
def get_full_text(self) -> str:
|
|
return "".join(self._chunks)
|
|
|
|
def get_chunks(self) -> list[str]:
|
|
return list(self._chunks)
|
|
|
|
def is_finished(self) -> bool:
|
|
return self._finished
|
|
|
|
def mark_finished(self) -> None:
|
|
self._finished = True
|
|
|
|
def chunk_count(self) -> int:
|
|
return len(self._chunks)
|
|
|
|
|
|
@dataclass
|
|
class GatewayRequestContext:
|
|
runtime_config: dict = field(default_factory=dict)
|
|
runtime_snapshot: ChannelAccountSnapshot | None = None
|
|
|
|
start_channel: Callable[[str, dict], Awaitable[None]] | None = None
|
|
stop_channel: Callable[[str], Awaitable[None]] | None = None
|
|
mark_channel_logged_out: Callable[[str, str], Awaitable[None]] | None = None
|
|
|
|
broadcast_fn: Callable[[str, Any], Awaitable[None]] | None = None
|
|
node_send_to_session_fn: Callable[[str, str, Any], Awaitable[None]] | None = None
|
|
|
|
get_snapshot: Callable[[str], Awaitable[ChannelAccountSnapshot]] | None = None
|
|
|
|
dedupe: Any = None
|
|
|
|
chat_abort_controllers: dict[str, ChatAbortEntry] = field(default_factory=dict)
|
|
chat_run_buffers: dict[str, ChatRunBuffer] = field(default_factory=dict)
|
|
|
|
def get_runtime_config(self, key: str, default=None):
|
|
return self.runtime_config.get(key, default)
|
|
|
|
def set_runtime_config(self, key: str, value) -> None:
|
|
self.runtime_config[key] = value
|
|
|
|
def add_chat_run(self, run_id: str, entry: ChatAbortEntry, buffer: ChatRunBuffer | None = None) -> None:
|
|
self.chat_abort_controllers[run_id] = entry
|
|
if buffer is not None:
|
|
self.chat_run_buffers[run_id] = buffer
|
|
|
|
def remove_chat_run(self, run_id: str) -> None:
|
|
self.chat_abort_controllers.pop(run_id, None)
|
|
self.chat_run_buffers.pop(run_id, None)
|
|
|
|
def abort_chat_run(self, run_id: str) -> bool:
|
|
entry = self.chat_abort_controllers.get(run_id)
|
|
if entry is None:
|
|
return False
|
|
entry.abort()
|
|
return True
|
|
|
|
def get_chat_buffer(self, run_id: str) -> ChatRunBuffer | None:
|
|
return self.chat_run_buffers.get(run_id)
|
|
|
|
async def broadcast(self, event: str, payload: Any = None) -> None:
|
|
if self.broadcast_fn is not None:
|
|
await self.broadcast_fn(event, payload)
|
|
|
|
async def node_send_to_session(self, session_id: str, event: str, payload: Any = None) -> None:
|
|
if self.node_send_to_session_fn is not None:
|
|
await self.node_send_to_session_fn(session_id, event, payload)
|