新增 Nostr 渠道扩展,支持在 Yuxi 平台中集成 Nostr 去中心化社交协议。 包含以下功能模块: - bus: 事件总线与中继通信 - account: 账户管理 - key_utils: 密钥工具 - gateway: SSE/WebSocket 网关接入 - outbound: 外发消息管理 - gift_wrap: Gift Wrap 加密 - nip44: NIP-44 加密协议 - config_schema: 配置模式 - defaults: 默认配置 - profile_core: 用户资料核心 - profile_publisher: 资料发布 - event_utils: 事件工具 - deletion: 事件删除 - reactions: 表情反应 - metrics: 指标监控 - seen_tracker: 已读追踪 - session_route: 会话路由 - state_store: 状态存储
59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
import asyncio
|
|
import hashlib
|
|
import json
|
|
import time
|
|
from dataclasses import dataclass, field
|
|
|
|
from .event_utils import sign_event
|
|
from .defaults import PROFILE_KIND, PROFILE_PUBLISH_TIMEOUT_S
|
|
from .profile_core import NostrProfile, profile_to_content
|
|
|
|
|
|
@dataclass
|
|
class ProfilePublishResult:
|
|
event_id: str
|
|
created_at: int
|
|
successes: list[str] = field(default_factory=list)
|
|
failures: list[dict] = field(default_factory=list)
|
|
persisted: bool = False
|
|
|
|
|
|
async def publish_profile(bus, profile: NostrProfile, last_published_at: int | None = None) -> ProfilePublishResult:
|
|
now = int(time.time())
|
|
created_at = max(now, (last_published_at or 0) + 1)
|
|
|
|
content = profile_to_content(profile)
|
|
event = sign_event(bus.sk, bus.pk, kind=PROFILE_KIND, content=content, tags=[])
|
|
|
|
serialized = json.dumps(
|
|
[0, event["pubkey"], created_at, PROFILE_KIND, [], content],
|
|
separators=(",", ":"),
|
|
ensure_ascii=False,
|
|
)
|
|
event["created_at"] = created_at
|
|
event["id"] = hashlib.sha256(serialized.encode()).hexdigest()
|
|
|
|
sig = bus._sign_event_hash(event["id"])
|
|
event["sig"] = sig.hex()
|
|
|
|
results = {}
|
|
for relay in bus.relays:
|
|
try:
|
|
success = await asyncio.wait_for(
|
|
bus.pool.publish(relay, event), timeout=PROFILE_PUBLISH_TIMEOUT_S
|
|
)
|
|
results[relay] = "ok" if success else "failed"
|
|
except TimeoutError:
|
|
results[relay] = "timeout"
|
|
except Exception:
|
|
results[relay] = "failed"
|
|
|
|
persisted = any(v == "ok" for v in results.values())
|
|
return ProfilePublishResult(
|
|
event_id=event["id"],
|
|
created_at=created_at,
|
|
successes=[r for r, v in results.items() if v == "ok"],
|
|
failures=[{"relay": r, "error": v} for r, v in results.items() if v != "ok"],
|
|
persisted=persisted,
|
|
)
|