ForcePilot/backend/package/yuxi/channels/adapters/nostr/setup_wizard.py

140 lines
5.4 KiB
Python
Raw Normal View History

from __future__ import annotations
import asyncio
import os
from typing import Any
from yuxi.channels.adapters.nostr.crypto import NostrCrypto, NostrCryptoError
from yuxi.channels.adapters.nostr.probe import probe_relay
from yuxi.utils.logging_config import logger
async def run_setup_wizard(config_dir: str | None = None) -> dict[str, Any]:
logger.info("[Nostr] Starting setup wizard")
accounts: dict[str, dict[str, Any]] = {}
account_counter = 0
while True:
if account_counter == 0:
account_name = "default"
else:
account_name = input(f"\n账户名称 (默认: account_{account_counter}): ").strip()
if not account_name:
account_name = f"account_{account_counter}"
if account_name in accounts:
logger.warning(f"账户 '{account_name}' 已存在,请使用不同名称")
continue
account_config = await _configure_account(account_name, account_counter)
if account_config is None:
if account_counter == 0:
return {"status": "cancelled", "message": "初始帐户配置已取消"}
continue
accounts[account_name] = account_config
account_counter += 1
if account_counter > 0:
more = input("\n添加其他账户? [y/n] (默认: n): ").strip().lower()
if more != "y":
break
config: dict[str, Any] = {"accounts": accounts}
primary = accounts.get("default", list(accounts.values())[0])
primary_crypto = NostrCrypto(primary.get("private_key", ""))
npub = primary_crypto.npub
logger.info("[Nostr] 设置向导完成: %s 个账户", len(accounts))
return {"status": "ok", "npub": npub, "config": config, "account_count": len(accounts)}
async def _configure_account(account_name: str, index: int) -> dict[str, Any] | None:
logger.info("[Nostr] 配置账户: %s", account_name)
env_key = os.environ.get("NOSTR_PRIVATE_KEY", "")
if env_key and index == 0:
use_env = input("\n检测到环境变量 NOSTR_PRIVATE_KEY。是否使用? [y/n] (默认: y): ").strip().lower()
if use_env != "n":
try:
crypto = NostrCrypto(env_key)
logger.info(f"[Nostr] 使用环境变量密钥: npub={crypto.npub}")
private_key = env_key
skip_key_input = True
except NostrCryptoError as e:
logger.warning(f"[Nostr] 环境变量密钥无效: {e},将跳转到手动输入")
skip_key_input = False
else:
skip_key_input = False
else:
skip_key_input = False
if not skip_key_input:
private_key = input("\nNostr 私钥 (nsec/hex留空自动生成): ").strip()
else:
private_key = env_key
try:
crypto = NostrCrypto(private_key if private_key else None)
private_key = crypto.nsec()
logger.info(f"[Nostr] 密钥就绪: npub={crypto.npub}")
except NostrCryptoError as e:
logger.error(f"[Nostr] 私钥无效: {e}")
return None
relays_input = input("Relay URL 列表 (逗号分隔,默认: damus.io, primal.net, nostr.info, nos.lol): ").strip()
if relays_input:
relays = [r.strip() for r in relays_input.split(",") if r.strip()]
else:
relays = [
"wss://relay.damus.io",
"wss://relay.primal.net",
"wss://relay.nostr.info",
"wss://nos.lol",
]
dm_policy = input("DM 策略 [pairing/open/whitelist/disabled] (默认: pairing): ").strip().lower()
dm_policy = dm_policy if dm_policy in ("pairing", "open", "whitelist", "disabled") else "pairing"
nip17_input = input("启用 NIP-17 加密 [y/n] (默认: y): ").strip().lower()
nip17_enabled = nip17_input != "n"
streaming_mode = input("流式模式 [off/block/progress] (默认: block): ").strip().lower()
streaming_mode = streaming_mode if streaming_mode in ("off", "block", "progress") else "block"
markdown_tables = input("启用 Markdown 表格转换 [y/n] (默认: n): ").strip().lower()
markdown_table_mode = "convert" if markdown_tables == "y" else "off"
probe_input = input("是否探测 Relay 连接? [y/n] (默认: y): ").strip().lower()
if probe_input != "n":
logger.info("[Nostr] 正在探测 Relay 连接...")
probe_tasks = [probe_relay(url, timeout=8.0) for url in relays]
results = await asyncio.gather(*probe_tasks, return_exceptions=True)
reachable = 0
for result in results:
if isinstance(result, Exception):
continue
if result.connected:
reachable += 1
logger.info(f"{result.url} (延迟: {result.latency_ms:.1f}ms)")
else:
logger.warning(f"{result.url} - {result.error or '不可达'}")
if reachable == 0:
logger.warning("[Nostr] 所有 Relay 不可达,请检查网络和 URL")
else:
logger.info(f"[Nostr] {reachable}/{len(relays)} 个 Relay 可连接")
account_config: dict[str, Any] = {
"private_key": private_key,
"relays": relays,
"dm_policy": dm_policy,
"nip17_enabled": nip17_enabled,
"streaming_mode": streaming_mode,
"markdown_table_mode": markdown_table_mode,
}
if index > 0:
account_config["enabled"] = True
return account_config