ForcePilot/backend/package/yuxi/channel/extensions/twitch/status.py
Kris 0babb1ca8e feat(channel): 添加 Tlon 渠道扩展
新增 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: 类型定义
2026-05-21 11:55:25 +08:00

125 lines
4.6 KiB
Python

import time
from yuxi.channel.extensions.twitch.config import (
TwitchConfig,
get_account_config,
is_account_configured,
list_account_ids,
)
from yuxi.channel.extensions.twitch.types import TwitchRole
from yuxi.channel.protocols import ChannelStatusIssue
def collect_twitch_status_issues(
config: TwitchConfig,
runtime_snapshots: dict[str, object],
) -> list[ChannelStatusIssue]:
issues = []
account_ids = list_account_ids(config)
if not account_ids:
account_ids = ["default"]
for aid in account_ids:
account = get_account_config(config, aid)
if not is_account_configured(account):
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="unconfigured",
message=f"Twitch account '{aid}' is not configured",
fix="Provide username, access_token, and channel in config",
)
)
continue
if not account.enabled:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="disabled",
message=f"Twitch account '{aid}' is disabled",
)
)
continue
has_client_id = bool(account.client_id)
if not has_client_id:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="missing_client_id",
message=f"Twitch account '{aid}' is missing client_id",
fix="Add a Twitch Client ID for Helix API access",
)
)
if account.allow_from is not None and len(account.allow_from) == 0:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="empty_allowlist",
message=f"Twitch account '{aid}' has empty allow_from, which rejects all users",
fix="Add user IDs to allow_from or remove it to use open/role mode",
)
)
if TwitchRole.ALL in account.allowed_roles and account.allow_from:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="conflicting_access_control",
message=f"Twitch account '{aid}' has both allowed_roles='all' and non-empty allow_from",
fix="Choose one access control mode: either allow_from or allowed_roles, not both",
)
)
snapshot = runtime_snapshots.get(f"twitch:{aid}")
if snapshot is not None:
last_error = getattr(snapshot, "last_error", None)
if last_error:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="runtime_error",
message=f"Twitch account '{aid}' runtime error: {last_error}",
fix="Check logs and restart the channel",
)
)
running = getattr(snapshot, "running", False) or getattr(snapshot, "state", None) == "running"
last_start_at = getattr(snapshot, "last_start_at", None)
last_inbound_at = getattr(snapshot, "last_inbound_at", None)
if not running and not last_start_at and not last_inbound_at:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="never_connected",
message=f"Twitch account '{aid}' has never successfully connected",
)
)
if running and last_start_at:
uptime_seconds = time.time() - last_start_at
if uptime_seconds > 7 * 24 * 3600:
issues.append(
ChannelStatusIssue(
channel="twitch",
account_id=aid,
kind="long_uptime",
message=f"Twitch account '{aid}' has been running for over 7 days",
fix="Consider restarting the channel to refresh the IRC connection",
)
)
return issues