feat(nostr adapter): 新增Nostr会话处理和线程模拟工具
新增两个文件实现Nostr适配器的基础工具能力: 1. session.py:实现聊天ID、线程键、代理路由的解析与目标标准化 2. thread_simulator.py:实现Nostr事件线程链构建、标签解析和事件缓存获取功能
This commit is contained in:
parent
7002557cf4
commit
b44aa607e0
31
backend/package/yuxi/channels/adapters/nostr/session.py
Normal file
31
backend/package/yuxi/channels/adapters/nostr/session.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from yuxi.channels.models import ChannelMessage
|
||||
|
||||
|
||||
def resolve_thread_key(message: ChannelMessage) -> str:
|
||||
identity = message.identity
|
||||
chat_id = identity.channel_chat_id
|
||||
|
||||
if chat_id.startswith("dm:"):
|
||||
pubkey = chat_id.removeprefix("dm:")
|
||||
return f"nostr:dm:{pubkey}"
|
||||
if chat_id.startswith("channel:"):
|
||||
channel = chat_id.removeprefix("channel:")
|
||||
return f"nostr:channel:{channel}"
|
||||
return f"nostr:unknown:{chat_id}"
|
||||
|
||||
|
||||
def resolve_chat_id(message: ChannelMessage) -> str:
|
||||
return message.identity.channel_chat_id
|
||||
|
||||
|
||||
def resolve_agent_route(message: ChannelMessage, default_agent_id: str = "default") -> str:
|
||||
thread_key = resolve_thread_key(message)
|
||||
return f"agent:{default_agent_id}:{thread_key}"
|
||||
|
||||
|
||||
def normalize_target(target: str) -> str:
|
||||
if target.startswith("nostr:"):
|
||||
return target
|
||||
return f"nostr:{target}"
|
||||
@ -0,0 +1,69 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class NostrThreadSimulator:
|
||||
def __init__(self, relay_pool: Any = None, max_depth: int = 10):
|
||||
self._relay_pool = relay_pool
|
||||
self._event_cache: dict[str, dict] = {}
|
||||
self._reply_chains: dict[str, list[str]] = {}
|
||||
self._max_depth = max_depth
|
||||
|
||||
async def build_thread_from_event(self, event: dict) -> list[dict]:
|
||||
chain: list[dict] = []
|
||||
current = event
|
||||
|
||||
for _ in range(self._max_depth):
|
||||
chain.append(current)
|
||||
parent_id = self._get_parent_event_id(current)
|
||||
if not parent_id:
|
||||
break
|
||||
parent = await self._fetch_event(parent_id)
|
||||
if not parent:
|
||||
break
|
||||
current = parent
|
||||
|
||||
return list(reversed(chain))
|
||||
|
||||
@staticmethod
|
||||
def _get_parent_event_id(event: dict) -> str | None:
|
||||
tags: list[list[str]] = event.get("tags", [])
|
||||
for tag in tags:
|
||||
if tag[0] == "e":
|
||||
if len(tag) >= 4 and tag[3] == "reply":
|
||||
return tag[1]
|
||||
if len(tag) >= 2:
|
||||
return tag[1]
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def resolve_e_tags(event: dict) -> list[str]:
|
||||
tags: list[list[str]] = event.get("tags", [])
|
||||
return [tag[1] for tag in tags if tag[0] == "e" and len(tag) > 1]
|
||||
|
||||
@staticmethod
|
||||
def resolve_p_tags(event: dict) -> list[str]:
|
||||
tags: list[list[str]] = event.get("tags", [])
|
||||
return [tag[1] for tag in tags if tag[0] == "p" and len(tag) > 1]
|
||||
|
||||
def _is_root_event(self, event: dict) -> bool:
|
||||
tags: list[list[str]] = event.get("tags", [])
|
||||
for tag in tags:
|
||||
if tag[0] == "e" and len(tag) >= 4 and tag[3] == "root":
|
||||
return False
|
||||
return True
|
||||
|
||||
async def _fetch_event(self, event_id: str) -> dict | None:
|
||||
if event_id in self._event_cache:
|
||||
return self._event_cache[event_id]
|
||||
|
||||
if self._relay_pool:
|
||||
try:
|
||||
event = await self._relay_pool.query_event(event_id)
|
||||
if event:
|
||||
self._event_cache[event_id] = event
|
||||
return event
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
Loading…
Reference in New Issue
Block a user