from __future__ import annotations from typing import Any from yuxi.channels.adapters.nostr.setup import NostrSetupAdapter from yuxi.channels.adapters.nostr.crypto import NostrCrypto, NostrCryptoError from yuxi.channels.adapters.nostr.config import NostrConfig class NostrSetupPlugin: channel_id: str = "nostr" channel_label: str = "Nostr (Decentralized)" channel_description: str = "Nostr 去中心化社交网络 DM 协议配置向导" def __init__(self, config: dict[str, Any] | None = None): self._config = config or {} self._setup_adapter = NostrSetupAdapter() @property def config(self) -> dict[str, Any]: return self._config def get_setup_wizard(self) -> dict[str, Any]: return { "channel_id": self.channel_id, "label": self.channel_label, "description": self.channel_description, "steps": self._setup_adapter.get_setup_steps(), "help": "详细文档请参考: /docs/channels/nostr", } async def validate_credentials(self, private_key: str) -> dict[str, Any]: result = await self._setup_adapter.verify_key(private_key) if result.get("success"): return {"valid": True, "npub": result.get("npub"), "hex": result.get("hex")} return {"valid": False, "error": result.get("error", "unknown")} async def probe_connectivity(self, relays: list[str], timeout_ms: int = 8000) -> dict[str, Any]: result = await self._setup_adapter.probe_relays(relays) return { "connected": result.get("success", False), "reachable": result.get("reachable", 0), "total": result.get("total", 0), "details": result.get("details", []), } def get_recommendations(self) -> dict[str, Any]: return { "relays": [ "wss://relay.damus.io", "wss://relay.primal.net", "wss://relay.nostr.info", "wss://nos.lol", ], "dm_policy": "pairing", "nip17_enabled": True, "streaming_mode": "block", } async def validate_full_config(self, full_config: dict[str, Any]) -> dict[str, Any]: errors: list[str] = [] warnings: list[str] = [] try: NostrConfig.from_dict(full_config) except Exception as e: errors.append(f"配置验证失败: {e}") return {"valid": False, "errors": errors, "warnings": warnings} private_key = full_config.get("private_key") or full_config.get("accounts", {}).get("default", {}).get( "private_key", "" ) if private_key: try: NostrCrypto(private_key) except NostrCryptoError as e: errors.append(f"私钥无效: {e}") else: warnings.append("未配置私钥,将自动生成临时密钥") relays = full_config.get("relays") or full_config.get("accounts", {}).get("default", {}).get("relays") if not relays: warnings.append("未配置 Relay,将使用默认 Relay 列表") else: invalid_relays = [r for r in relays if not r.startswith(("ws://", "wss://"))] if invalid_relays: warnings.append(f"以下 Relay URL 格式可能无效: {invalid_relays}") return {"valid": len(errors) == 0, "errors": errors, "warnings": warnings} def get_setup_plugin(config: dict[str, Any] | None = None) -> NostrSetupPlugin: return NostrSetupPlugin(config) async def verify_channel_setup(config: dict[str, Any]) -> dict[str, Any]: plugin = NostrSetupPlugin(config) return await plugin.validate_full_config(config)