ForcePilot/backend/package/yuxi/channels/adapters/urbit/setup.py
Kris 49ecd949e8 feat(urbit): 实现完整的Urbit聊天适配器模块
新增了从错误定义、客户端实现到功能完整的Urbit适配器全套代码,包括认证、消息处理、目录查询、邀请管理、媒体处理、速率限制等核心功能模块
2026-05-12 00:50:27 +08:00

209 lines
7.6 KiB
Python

from __future__ import annotations
from typing import Any
from yuxi.utils.logging_config import logger
def run_setup_wizard(config: dict[str, Any], interactive: bool = True) -> dict[str, Any]:
result: dict[str, Any] = {}
ship_url = config.get("ship_url", "")
if not ship_url and interactive:
ship_url = input("Enter your Urbit ship URL (e.g. http://localhost:8080): ").strip()
from .probe import validate_urbit_url
valid, warnings = validate_urbit_url(ship_url)
if warnings:
for w in warnings:
logger.warning(f"[Urbit Setup] {w}")
if not valid and interactive:
allow_private = input("URL validation failed. Allow private network connections? (y/n): ").strip().lower()
if allow_private == "y":
config["network"] = config.get("network", {})
config["network"]["dangerously_allow_private_network"] = True
result["dangerously_allow_private_network"] = True
result["ship_url"] = ship_url
ship_name = config.get("ship_name", "")
if not ship_name and interactive:
ship_name = input("Enter your Urbit ship name (without ~): ").strip()
result["ship_name"] = ship_name
ship_code = config.get("ship_code", "")
if not ship_code and interactive:
ship_code = input("Enter your ship +code: ").strip()
result["ship_code"] = ship_code
group_channels = config.get("group_channels", [])
if not group_channels and interactive:
channels_input = input(
"Group channels to monitor (comma-separated, e.g. chat/some-group, diary/some-diary): "
).strip()
if channels_input:
group_channels = [c.strip() for c in channels_input.split(",") if c.strip()]
result["group_channels"] = group_channels
dm_allowlist = config.get("dm_allowlist", [])
if not dm_allowlist and interactive:
allowlist_input = input(
"DM whitelist ships (comma-separated with urbit: prefix, e.g. urbit:zod, urbit:bus): "
).strip()
if allowlist_input:
dm_allowlist = [s.strip() for s in allowlist_input.split(",") if s.strip()]
result["dm_allowlist"] = dm_allowlist
auto_discover = config.get("auto_discover_channels", True)
result["auto_discover_channels"] = auto_discover
logger.info("[Urbit Setup] Configuration complete")
return result
def quick_setup(ship_url: str, ship_name: str, ship_code: str, **kw) -> dict[str, Any]:
config: dict[str, Any] = {
"ship_url": ship_url,
"ship_name": ship_name,
"ship_code": ship_code,
"group_channels": kw.pop("group_channels", []),
"dm_allowlist": kw.pop("dm_allowlist", []),
"auto_discover_channels": kw.pop("auto_discover", True),
}
config.update(kw)
return run_setup_wizard(config, interactive=False)
def get_setup_schema() -> dict[str, Any]:
return {
"name": "urbit",
"display_name": "Urbit (Tlon)",
"description": "Connect to your Urbit ship for group chat and DM",
"required_fields": [
{"key": "ship_url", "label": "Ship URL", "type": "url", "placeholder": "http://localhost:8080"},
{"key": "ship_name", "label": "Ship Name", "type": "text", "placeholder": "zod"},
{"key": "ship_code", "label": "Ship +code", "type": "password", "placeholder": "XXXX-XXXX-XXXX-XXXX"},
],
"optional_fields": [
{"key": "dm_allowlist", "label": "DM Allowlist", "type": "list"},
{"key": "group_channels", "label": "Group Channels", "type": "list"},
{
"key": "autoDiscoverChannels",
"label": "Auto-discover Channels",
"type": "boolean",
"default": True,
},
{
"key": "dm_policy",
"label": "DM Policy",
"type": "select",
"options": ["open", "disabled", "pairing", "allowlist"],
"default": "pairing",
},
{
"key": "group_policy",
"label": "Group Policy",
"type": "select",
"options": ["open", "disabled", "allowlist"],
"default": "open",
"description": "Controls how the bot handles incoming group messages",
},
{
"key": "owner_ship",
"label": "Owner Ship",
"type": "text",
"description": "Ship that receives admin notifications",
},
{
"key": "autoAcceptDmInvites",
"label": "Auto Accept DM Invites",
"type": "boolean",
"default": False,
},
{
"key": "autoAcceptGroupInvites",
"label": "Auto Accept Group Invites",
"type": "boolean",
"default": False,
},
{
"key": "groupInviteAllowlist",
"label": "Group Invite Allowlist",
"type": "list",
"description": "Group paths allowed for auto-accept",
},
{
"key": "streaming",
"label": "Streaming Mode",
"type": "select",
"options": ["off", "partial"],
"default": "off",
"description": "Controls how streaming responses are delivered",
},
{
"key": "configWrites",
"label": "Config Writes",
"type": "object",
"description": "Custom config values pushed to Settings Store on connect",
},
{
"key": "authorization.channel_rules",
"label": "Channel Rules",
"type": "object",
"description": 'Per-channel authorization rules, e.g. {"chat/my-group": {"allow": ["zod"]}}',
},
{
"key": "authorization.default_authorized_ships",
"label": "Default Authorized Ships",
"type": "list",
"description": "Ships authorized across all channels by default",
},
{
"key": "network.dangerously_allow_private_network",
"label": "Allow Private Network",
"type": "boolean",
"default": False,
"description": "Allow connections to private/local network addresses",
},
{
"key": "trustedLocalFileRoots",
"label": "Trusted Local File Roots",
"type": "list",
"description": "Trusted directory paths for file operations",
},
{
"key": "timeoutSeconds",
"label": "Request Timeout (s)",
"type": "number",
"default": 30,
"min": 5,
"max": 300,
},
{
"key": "showModelSignature",
"label": "Show Model Signature",
"type": "boolean",
"default": False,
},
{
"key": "responsePrefix",
"label": "Response Prefix",
"type": "text",
"description": "Optional prefix added before bot responses",
},
{
"key": "commands.native",
"label": "Enable Native Commands",
"type": "boolean",
"default": False,
},
{
"key": "commands.nativeSkills",
"label": "Native Command Skills",
"type": "list",
},
],
}