ForcePilot/backend/package/yuxi/channels/adapters/nostr/setup.py
Kris 7002557cf4 refactor(nostr): 整理代码结构并新增多项功能优化
1.  整理多个文件的导入顺序,移除冗余空行和重复导入
2.  为Nostr订阅添加until参数防止重复拉取
3.  实现长消息分块发送并添加间隔等待
4.  新增Relay健康分数同步任务
5.  新增TLS强制检查配置项并支持从环境变量加载
6.  重构状态恢复逻辑适配异步存储
7.  修复反应发送的参数错误
8.  新增线程模拟自动补全消息上下文
9.  添加解密指标统计和更完善的错误日志
2026-05-13 16:13:03 +08:00

278 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import asyncio
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
class NostrSetupAdapter:
def __init__(self, adapter=None):
self._adapter = adapter
def get_setup_steps(self) -> list[dict]:
return [
{
"step": 1,
"title": "生成或输入私钥",
"description": "Nostr 私钥用于签名和加密。可以留空自动生成,或输入现有的 nsec/hex 格式私钥",
"field_key": "private_key",
"required": False,
"help": "支持 nsec1... 或 64 位 hex 格式",
},
{
"step": 2,
"title": "配置 Relay 服务器",
"description": "Relay 是中继服务器,用于收发 Nostr 消息。支持 ws:// 和 wss:// 协议",
"field_key": "relays",
"required": False,
"default": [
"wss://relay.damus.io",
"wss://relay.primal.net",
"wss://relay.nostr.info",
"wss://nos.lol",
],
"help": "多个 Relay URL 以逗号分隔",
},
{
"step": 3,
"title": "选择 DM 策略",
"description": "控制谁可以向你的 Nostr Bot 发送私信",
"field_key": "dm_policy",
"required": True,
"default": "pairing",
"options": ["pairing", "open", "whitelist", "disabled"],
"help": "pairing=配对认证 | open=所有人 | whitelist=白名单 | disabled=禁用",
},
{
"step": 4,
"title": "配置高级选项",
"description": "配置 NIP-17 加密、流式输出模式、Markdown 表格转换等高级选项",
"field_key": "advanced",
"required": False,
"default": {
"nip17_enabled": True,
"streaming_mode": "block",
"markdown_table_mode": "off",
"nip42_auth_enabled": False,
},
},
{
"step": 5,
"title": "探测 Relay 连接",
"description": "验证 Relay 服务器是否可达",
"action": "probe_relays",
"field_key": "relays",
},
{
"step": 6,
"title": "验证私钥",
"description": "验证私钥格式并获取对应的 npub",
"action": "verify_key",
"field_key": "private_key",
},
]
async def probe_relays(self, relays: list[str]) -> dict:
probe_tasks = [probe_relay(url, timeout=8.0) for url in relays]
results = await asyncio.gather(*probe_tasks, return_exceptions=True)
reachable = 0
total = len(relays)
details: list[dict] = []
for result in results:
if isinstance(result, Exception):
details.append({"error": str(result)})
continue
entry = {
"url": result.url,
"connected": result.connected,
"latency_ms": round(result.latency_ms, 1) if result.latency_ms else None,
}
if result.connected:
reachable += 1
if result.error:
entry["error"] = result.error[:120]
details.append(entry)
return {
"success": reachable > 0,
"reachable": reachable,
"total": total,
"details": details,
}
async def verify_key(self, private_key: str) -> dict:
if not private_key:
crypto = NostrCrypto(None)
return {
"success": True,
"auto_generated": True,
"npub": crypto.npub,
"hex": crypto.pubkey_hex(),
"nsec": crypto.nsec(),
}
try:
crypto = NostrCrypto(private_key)
return {
"success": True,
"npub": crypto.npub,
"hex": crypto.pubkey_hex(),
}
except NostrCryptoError as e:
return {"success": False, "error": str(e)}
async def run_interactive_setup(self) -> dict:
logger.info("[Nostr Setup] Starting interactive setup wizard")
steps = self.get_setup_steps()
return {
"status": "setup_required",
"channel": "nostr",
"steps": steps,
"help": "详细文档请参考: /docs/channels/nostr",
}
def generate_config_schema(self) -> dict:
return {
"private_key": {
"type": "str",
"required": False,
"label": "私钥 (nsec/hex)",
"description": "Nostr 私钥 (nsec/hex),留空自动生成",
"placeholder": "nsec1... 或 64 位 hex 格式",
"help": "建议使用 nsec1 格式。留空则自动生成一个新的临时密钥",
},
"relays": {
"type": "list[str]",
"required": False,
"label": "Relay 中继服务器",
"description": "Relay 中继服务器 URL 列表 (ws:// 或 wss://)",
"placeholder": "wss://relay.damus.io",
"help": "至少配置 1 个 Relay 用于收发消息。建议使用 wss:// 加密连接",
"default": [
"wss://relay.damus.io",
"wss://relay.primal.net",
"wss://relay.nostr.info",
"wss://nos.lol",
],
},
"dm_policy": {
"type": "str",
"required": False,
"default": "pairing",
"label": "DM 策略",
"description": "控制谁可以向您的 Nostr Bot 发送私信",
"help": "pairing=配对认证 | open=所有人 | whitelist=白名单 | disabled=禁用 DM",
"enum": ["pairing", "open", "whitelist", "disabled"],
},
"nip17_enabled": {
"type": "bool",
"required": False,
"default": True,
"label": "NIP-17 Gift Wrap 加密",
"description": "启用 NIP-17 Gift Wrap 加密(推荐)",
"help": "NIP-17 提供更好的隐私保护,建议开启",
},
"streaming_mode": {
"type": "str",
"required": False,
"default": "block",
"label": "流式输出模式",
"description": "控制消息的流式输出方式",
"help": "off=关闭流式 | block=分段输出 | progress=进度式输出",
"enum": ["off", "block", "progress"],
},
"markdown_table_mode": {
"type": "str",
"required": False,
"default": "off",
"label": "Markdown 表格转换",
"description": "将 Markdown 表格转换为适合 Nostr 的格式",
"help": "Nostr 客户端通常不支持原生的 Markdown 表格渲染",
"enum": ["off", "convert"],
},
"nip42_auth_enabled": {
"type": "bool",
"required": False,
"default": False,
"label": "NIP-42 AUTH 认证",
"description": "启用 Relay AUTH 认证 (NIP-42)",
"help": "需要 Relay 支持 NIP-42 才能生效",
},
"allow_from": {
"type": "list[str]",
"required": False,
"default": [],
"label": "DM 白名单",
"description": "DM 白名单 (npub/hex/pubkey)",
"placeholder": "npub1...",
"help": "当 DM 策略为 whitelist 时,只有此列表中的 pubkey 能发消息",
},
"profile": {
"type": "dict",
"required": False,
"label": "Bot Profile",
"description": "Nostr Kind 0 元数据 Profile",
"schema": {
"name": {
"type": "str",
"default": "",
"label": "用户名",
"description": "Nostr 用户名",
"placeholder": "my_nostr_bot",
},
"display_name": {
"type": "str",
"default": "",
"label": "显示名称",
"description": "Nostr 显示名称",
"placeholder": "My Nostr Bot",
},
"about": {
"type": "str",
"default": "",
"label": "简介",
"description": "Bot 简介",
"placeholder": "A Nostr bot powered by ForcePilot",
},
"picture": {
"type": "str",
"default": "",
"label": "头像 URL",
"description": "头像图片 URL需要 https",
"placeholder": "https://example.com/avatar.png",
},
"banner": {
"type": "str",
"default": "",
"label": "横幅图 URL",
"description": "Profile 横幅图 URL需要 https",
"placeholder": "https://example.com/banner.png",
},
"website": {
"type": "str",
"default": "",
"label": "个人网站",
"description": "个人/项目网站 URL",
"placeholder": "https://example.com",
},
"nip05": {
"type": "str",
"default": "",
"label": "NIP-05 标识",
"description": "NIP-05 验证标识 (name@domain)",
"placeholder": "bot@example.com",
},
"lud16": {
"type": "str",
"default": "",
"label": "Lightning Address",
"description": "Lightning 地址用于接收 Zap 打赏",
"placeholder": "bot@getalby.com",
},
},
},
}