这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
120 lines
4.5 KiB
Python
120 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from typing import TYPE_CHECKING
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
from .scry import scry_groups_init
|
|
|
|
if TYPE_CHECKING:
|
|
from .client import UrbitClient
|
|
from .settings import SettingsStore
|
|
|
|
_DISCOVERY_INTERVAL = 120.0
|
|
|
|
|
|
class ChannelDiscovery:
|
|
def __init__(
|
|
self,
|
|
client: UrbitClient,
|
|
auto_discover: bool = False,
|
|
group_channels: list[str] | None = None,
|
|
settings_store: SettingsStore | None = None,
|
|
):
|
|
self._client = client
|
|
self._auto_discover = auto_discover
|
|
self._group_channels = set(group_channels or [])
|
|
self._discovered_channels: set[str] = set()
|
|
self._refresh_task: asyncio.Task | None = None
|
|
self._on_new_channel: asyncio.Event | None = None
|
|
self._settings_store = settings_store
|
|
|
|
@property
|
|
def channels(self) -> set[str]:
|
|
return self._discovered_channels | self._group_channels
|
|
|
|
async def start(self) -> set[str]:
|
|
if self._group_channels:
|
|
self._discovered_channels = set(self._group_channels)
|
|
|
|
if self._settings_store:
|
|
store_group_channels = self._settings_store.get("groupChannels", [])
|
|
if isinstance(store_group_channels, list) and store_group_channels:
|
|
self._discovered_channels |= set(store_group_channels)
|
|
logger.info(f"[Urbit] Merged {len(store_group_channels)} channels from Settings Store")
|
|
|
|
if self._auto_discover:
|
|
discovered = await self.discover_channels()
|
|
self._discovered_channels |= discovered
|
|
self._refresh_task = asyncio.create_task(self._refresh_loop())
|
|
|
|
channels = self.channels
|
|
logger.info(f"[Urbit] Channel discovery started: {len(channels)} channels")
|
|
return channels
|
|
|
|
async def stop(self) -> None:
|
|
if self._refresh_task and not self._refresh_task.done():
|
|
self._refresh_task.cancel()
|
|
try:
|
|
await self._refresh_task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
self._refresh_task = None
|
|
logger.info("[Urbit] Channel discovery stopped")
|
|
|
|
async def discover_channels(self) -> set[str]:
|
|
discovered: set[str] = set()
|
|
try:
|
|
init_data = await scry_groups_init(self._client)
|
|
if init_data and isinstance(init_data, dict):
|
|
groups = init_data.get("groups", {})
|
|
for group_id, group_data in groups.items():
|
|
channels = group_data.get("channels", {})
|
|
for channel_nest in channels:
|
|
if channel_nest.startswith("chat/"):
|
|
discovered.add(channel_nest)
|
|
except Exception as e:
|
|
logger.warning(f"[Urbit] Channel discovery failed: {e}")
|
|
|
|
if discovered:
|
|
logger.info(f"[Urbit] Discovered {len(discovered)} chat channels")
|
|
return discovered
|
|
|
|
async def _refresh_loop(self) -> None:
|
|
while True:
|
|
try:
|
|
await asyncio.sleep(_DISCOVERY_INTERVAL)
|
|
new_channels = await self.discover_channels()
|
|
added = new_channels - self._discovered_channels
|
|
if added:
|
|
self._discovered_channels |= added
|
|
logger.info(f"[Urbit] Discovered {len(added)} new channels: {added}")
|
|
except asyncio.CancelledError:
|
|
break
|
|
except Exception as e:
|
|
logger.warning(f"[Urbit] Channel refresh error: {e}")
|
|
|
|
async def persist_group_channels(self) -> None:
|
|
if not self._settings_store:
|
|
return
|
|
try:
|
|
chat_channels = [ch for ch in self.channels if ch.startswith("chat/")]
|
|
self._settings_store.update_key("groupChannels", chat_channels)
|
|
logger.info(f"[Urbit] Persisted {len(chat_channels)} group channels to Settings Store")
|
|
except Exception as e:
|
|
logger.warning(f"[Urbit] Failed to persist group channels: {e}")
|
|
|
|
def add_channel(self, channel_nest: str) -> None:
|
|
if channel_nest not in self._discovered_channels:
|
|
self._discovered_channels.add(channel_nest)
|
|
logger.info(f"[Urbit] Manually added channel: {channel_nest}")
|
|
|
|
def get_chat_nests(self, ship_name: str) -> list[str]:
|
|
nests: list[str] = []
|
|
for channel in self.channels:
|
|
if channel.startswith("chat/"):
|
|
group_name = channel[len("chat/") :]
|
|
nests.append(f"~{ship_name}/{group_name}/chat")
|
|
return nests
|