新增 Tlon 渠道扩展,支持在 Yuxi 平台中集成 Tlon/Urbit 去中心化通讯平台。 包含以下功能模块: - tlon_api: Tlon API 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - sse_client: SSE 客户端 - outbound: 外发消息管理 - send: 消息发送 - security: 安全校验 - auth: 认证管理 - monitor: 渠道状态监控 - status: 会话状态管理 - session: 会话管理 - approval: 审批流程 - channel_mgmt: 频道管理 - channel_ops: 频道操作 - contacts: 联系人管理 - discovery: 服务发现 - doctor: 健康诊断 - expose: 服务暴露 - gallery: 图库管理 - history: 历史记录 - hooks: 钩子管理 - media: 媒体资源处理 - notebook: 笔记本功能 - settings_store: 设置存储 - setup: 初始化设置 - story: 故事功能 - targets: 目标管理 - cite_parser: 引用解析 - utils: 工具函数 - types: 类型定义
119 lines
3.7 KiB
Python
119 lines
3.7 KiB
Python
from pydantic import BaseModel
|
|
|
|
from yuxi.channel.extensions.twitch.types import TwitchRole
|
|
|
|
|
|
class TwitchAccountConfig(BaseModel):
|
|
username: str = ""
|
|
access_token: str = ""
|
|
client_id: str | None = None
|
|
channel: str = ""
|
|
enabled: bool = True
|
|
allow_from: list[str] = []
|
|
allowed_roles: list[TwitchRole] = []
|
|
require_mention: bool = True
|
|
response_prefix: str | None = None
|
|
client_secret: str | None = None
|
|
refresh_token: str | None = None
|
|
expires_in: int | None = None
|
|
obtainment_timestamp: float | None = None
|
|
|
|
|
|
class TwitchConfig(BaseModel):
|
|
enabled: bool = True
|
|
name: str | None = None
|
|
default_account: str = "default"
|
|
username: str | None = None
|
|
access_token: str | None = None
|
|
client_id: str | None = None
|
|
channel: str | None = None
|
|
require_mention: bool = True
|
|
allow_from: list[str] = []
|
|
allowed_roles: list[TwitchRole] = []
|
|
response_prefix: str | None = None
|
|
client_secret: str | None = None
|
|
refresh_token: str | None = None
|
|
accounts: dict[str, TwitchAccountConfig] = {}
|
|
block_streaming: bool = True
|
|
block_streaming_coalesce_max_delay_ms: int = 500
|
|
block_streaming_coalesce_min_chars: int = 100
|
|
send_chunk_interval_ms: int = 1500
|
|
eventsub_enabled: bool = True
|
|
|
|
|
|
_MERGE_FIELDS = (
|
|
"username",
|
|
"access_token",
|
|
"client_id",
|
|
"channel",
|
|
"require_mention",
|
|
"allow_from",
|
|
"allowed_roles",
|
|
)
|
|
|
|
|
|
def normalize_twitch_channel(channel: str) -> str:
|
|
return channel.strip().removeprefix("#").lower()
|
|
|
|
|
|
def is_account_configured(account: TwitchAccountConfig) -> bool:
|
|
return bool(account.username and account.access_token and account.channel)
|
|
|
|
|
|
def get_account_config(config: TwitchConfig, account_id: str = "default") -> TwitchAccountConfig:
|
|
account = config.accounts.get(account_id)
|
|
if account is None:
|
|
if account_id == "default":
|
|
return TwitchAccountConfig(
|
|
username=config.username or "",
|
|
access_token=config.access_token or "",
|
|
client_id=config.client_id,
|
|
channel=config.channel or "",
|
|
require_mention=config.require_mention,
|
|
allow_from=list(config.allow_from),
|
|
allowed_roles=list(config.allowed_roles),
|
|
response_prefix=config.response_prefix,
|
|
)
|
|
return TwitchAccountConfig()
|
|
|
|
if account_id == "default":
|
|
merged = account.model_copy()
|
|
for field_name in _MERGE_FIELDS:
|
|
base_val = getattr(config, field_name, None)
|
|
is_empty = False
|
|
if isinstance(base_val, str) and not base_val:
|
|
is_empty = True
|
|
elif isinstance(base_val, list) and not base_val:
|
|
is_empty = True
|
|
if base_val is None or is_empty:
|
|
continue
|
|
setattr(merged, field_name, base_val)
|
|
return merged
|
|
|
|
return account
|
|
|
|
|
|
def list_account_ids(config: TwitchConfig) -> list[str]:
|
|
if config.accounts:
|
|
return list(config.accounts.keys())
|
|
return ["default"]
|
|
|
|
|
|
def has_configured_state(config: TwitchConfig) -> bool:
|
|
if config.accounts:
|
|
return any(is_account_configured(acct) for acct in config.accounts.values())
|
|
default = TwitchAccountConfig(
|
|
username=config.username or "",
|
|
access_token=config.access_token or "",
|
|
channel=config.channel or "",
|
|
)
|
|
return is_account_configured(default)
|
|
|
|
|
|
def resolve_default_to(config: TwitchConfig, account_id: str | None = None) -> str | None:
|
|
aid = account_id or config.default_account
|
|
account = get_account_config(config, aid)
|
|
if is_account_configured(account):
|
|
return normalize_twitch_channel(account.channel)
|
|
return None
|