新增元宝(Yuanbao)渠道的完整适配器实现,包含以下核心模块: - 基础适配器与导出入口 - 协议编解码与WebSocket帧处理 - 会话管理与路由逻辑 - 事件队列与出站消息队列 - 消息格式转换与发送重试 - 安全审计与权限校验 - 配置映射与账户管理 - 视觉分析与工具函数 - 文档生成与设置向导
170 lines
5.6 KiB
Python
170 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import aiohttp
|
|
from dataclasses import dataclass, field
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
@dataclass
|
|
class PeerInfo:
|
|
open_id: str
|
|
username: str = ""
|
|
avatar_url: str = ""
|
|
display_name: str = ""
|
|
|
|
|
|
@dataclass
|
|
class GroupInfo:
|
|
group_open_id: str
|
|
name: str = ""
|
|
avatar_url: str = ""
|
|
member_count: int = 0
|
|
bot_is_member: bool = False
|
|
|
|
|
|
@dataclass
|
|
class DirectoryResult:
|
|
peers: list[PeerInfo] = field(default_factory=list)
|
|
groups: list[GroupInfo] = field(default_factory=list)
|
|
|
|
|
|
async def get_user_info(
|
|
api_base: str,
|
|
token: str,
|
|
http_client: aiohttp.ClientSession,
|
|
user_id: str,
|
|
) -> PeerInfo | None:
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
url = f"{api_base}/api/v1/user/{user_id}"
|
|
|
|
try:
|
|
async with http_client.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
return PeerInfo(
|
|
open_id=data.get("open_id", user_id),
|
|
username=data.get("username", ""),
|
|
avatar_url=data.get("avatar_url", ""),
|
|
display_name=data.get("display_name", ""),
|
|
)
|
|
else:
|
|
logger.debug(f"[Yuanbao] get_user_info returned {resp.status} for {user_id}")
|
|
return None
|
|
except Exception as e:
|
|
logger.warning(f"[Yuanbao] Failed to get user info for {user_id}: {e}")
|
|
return None
|
|
|
|
|
|
async def get_group_list(
|
|
api_base: str,
|
|
token: str,
|
|
http_client: aiohttp.ClientSession,
|
|
) -> list[GroupInfo]:
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
url = f"{api_base}/api/v1/bot/groups"
|
|
|
|
try:
|
|
async with http_client.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
groups = []
|
|
for g in data.get("groups", []):
|
|
groups.append(
|
|
GroupInfo(
|
|
group_open_id=g.get("group_open_id", ""),
|
|
name=g.get("group_name", ""),
|
|
avatar_url=g.get("avatar_url", ""),
|
|
member_count=g.get("member_count", 0),
|
|
bot_is_member=g.get("bot_is_member", False),
|
|
)
|
|
)
|
|
logger.info(f"[Yuanbao] Fetched {len(groups)} groups")
|
|
return groups
|
|
else:
|
|
logger.warning(f"[Yuanbao] get_group_list returned {resp.status}")
|
|
return []
|
|
except Exception as e:
|
|
logger.warning(f"[Yuanbao] Failed to get group list: {e}")
|
|
return []
|
|
|
|
|
|
async def get_group_detail(
|
|
api_base: str,
|
|
token: str,
|
|
http_client: aiohttp.ClientSession,
|
|
group_open_id: str,
|
|
) -> GroupInfo | None:
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
url = f"{api_base}/api/v1/bot/groups/{group_open_id}"
|
|
|
|
try:
|
|
async with http_client.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
return GroupInfo(
|
|
group_open_id=data.get("group_open_id", group_open_id),
|
|
name=data.get("group_name", ""),
|
|
avatar_url=data.get("avatar_url", ""),
|
|
member_count=data.get("member_count", 0),
|
|
bot_is_member=data.get("bot_is_member", False),
|
|
)
|
|
else:
|
|
logger.debug(f"[Yuanbao] get_group_detail returned {resp.status} for {group_open_id}")
|
|
return None
|
|
except Exception as e:
|
|
logger.warning(f"[Yuanbao] Failed to get group detail for {group_open_id}: {e}")
|
|
return None
|
|
|
|
|
|
async def get_group_members(
|
|
api_base: str,
|
|
token: str,
|
|
http_client: aiohttp.ClientSession,
|
|
group_open_id: str,
|
|
) -> list[PeerInfo]:
|
|
headers = {"Authorization": f"Bearer {token}"}
|
|
url = f"{api_base}/api/v1/bot/groups/{group_open_id}/members"
|
|
|
|
try:
|
|
async with http_client.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
members = []
|
|
for m in data.get("members", []):
|
|
members.append(
|
|
PeerInfo(
|
|
open_id=m.get("open_id", ""),
|
|
username=m.get("username", ""),
|
|
avatar_url=m.get("avatar_url", ""),
|
|
display_name=m.get("display_name", ""),
|
|
)
|
|
)
|
|
logger.info(f"[Yuanbao] Fetched {len(members)} members from group {group_open_id}")
|
|
return members
|
|
else:
|
|
logger.warning(f"[Yuanbao] get_group_members returned {resp.status} for {group_open_id}")
|
|
return []
|
|
except Exception as e:
|
|
logger.warning(f"[Yuanbao] Failed to get group members for {group_open_id}: {e}")
|
|
return []
|
|
|
|
|
|
async def list_directory(
|
|
api_base: str,
|
|
token: str,
|
|
http_client: aiohttp.ClientSession,
|
|
user_ids: list[str] | None = None,
|
|
) -> DirectoryResult:
|
|
result = DirectoryResult()
|
|
|
|
result.groups = await get_group_list(api_base, token, http_client)
|
|
|
|
if user_ids:
|
|
for user_id in user_ids:
|
|
peer = await get_user_info(api_base, token, http_client, user_id)
|
|
if peer:
|
|
result.peers.append(peer)
|
|
|
|
return result
|