feat(album): 支持 LLBot 群相册接口并优化 OneBot 适配器

This commit is contained in:
SXP-Simon
2026-04-22 15:15:59 +08:00
parent d36bf4941a
commit b119ea741e
2 changed files with 84 additions and 31 deletions
+2 -2
View File
@@ -424,7 +424,7 @@
"qq_group_upload": { "qq_group_upload": {
"description": "群文件/群相册上传设置", "description": "群文件/群相册上传设置",
"type": "object", "type": "object",
"hint": "图片报告生成后自动上传到群文件目录或群相册。群文件上传基于 OneBot upload_group_file API,群相册上传为 NapCat 扩展 API。仅对 QQ(OneBot)平台的图片格式报告生效", "hint": "图片报告生成后自动上传到群文件目录或群相册。群文件上传基于 OneBot upload_group_file API。仅对 QQOneBot)平台的图片格式报告生效",
"items": { "items": {
"enable_group_file_upload": { "enable_group_file_upload": {
"type": "bool", "type": "bool",
@@ -440,7 +440,7 @@
}, },
"enable_group_album_upload": { "enable_group_album_upload": {
"type": "bool", "type": "bool",
"description": "启用群相册上传NapCat 通过测试)", "description": "启用群相册上传",
"default": false, "default": false,
"hint": "图片报告生成后,自动上传到群相册(确保 Bot 有权限上传群相册)。此功能为 NapCat 扩展 API,其他 OneBot 实现可能不支持" "hint": "图片报告生成后,自动上传到群相册(确保 Bot 有权限上传群相册)。此功能为 NapCat 扩展 API,其他 OneBot 实现可能不支持"
}, },
@@ -62,10 +62,30 @@ class OneBotAdapter(PlatformAdapter):
if not self.bot_self_ids and config: if not self.bot_self_ids and config:
self.bot_self_ids = [str(id) for id in config.get("bot_qq_ids", [])] self.bot_self_ids = [str(id) for id in config.get("bot_qq_ids", [])]
# LLBot 探测标志
self._is_llbot = False
self._llbot_checked = False
def _init_capabilities(self) -> PlatformCapabilities: def _init_capabilities(self) -> PlatformCapabilities:
"""返回预定义的 OneBot v11 能力集。""" """返回预定义的 OneBot v11 能力集。"""
return ONEBOT_V11_CAPABILITIES return ONEBOT_V11_CAPABILITIES
async def _detect_llbot(self):
"""探测是否为 LLBot"""
if self._llbot_checked:
return
try:
# 避免在一些不支持 get_version_info 的老版本上卡死
result = await self.bot.call_action("get_version_info")
if isinstance(result, dict):
app_name = result.get("app_name", "")
self._is_llbot = app_name == "LLOneBot"
if self._is_llbot:
logger.info("[OneBot] 探测到当前协议端为 LLBot")
except Exception:
self._is_llbot = False
self._llbot_checked = True
def _get_nearest_size(self, requested_size: int) -> int: def _get_nearest_size(self, requested_size: int) -> int:
"""从支持的尺寸列表中找到最接近请求尺寸的一个。""" """从支持的尺寸列表中找到最接近请求尺寸的一个。"""
return min(self.AVAILABLE_SIZES, key=lambda x: abs(x - requested_size)) return min(self.AVAILABLE_SIZES, key=lambda x: abs(x - requested_size))
@@ -946,7 +966,7 @@ class OneBotAdapter(PlatformAdapter):
if not album_id: if not album_id:
albums = await self.get_group_album_list(group_id) albums = await self.get_group_album_list(group_id)
album_id = self._find_item_in_list( album_id = self._find_item_in_list(
albums, album_name, ["album_id", "id"], ["name", "album_name"] albums, album_name, ["album_id"], ["name", "album_name"]
) )
# 如果仍没指定且没搜到特定相册,取第一个 # 如果仍没指定且没搜到特定相册,取第一个
if not album_id and not album_name and albums: if not album_id and not album_name and albums:
@@ -959,6 +979,27 @@ class OneBotAdapter(PlatformAdapter):
return False return False
async def do_upload(content: str, label: str): async def do_upload(content: str, label: str):
await self._detect_llbot()
if self._is_llbot:
# LLBot 模式:使用 files 参数 (列表)
# LLBot 的 upload_group_album 接收 files 作为数组
llbot_params = {
"group_id": int(group_id),
"album_id": str(album_id),
"files": [content],
}
try:
await self.bot.call_action("upload_group_album", **llbot_params)
logger.debug(
f"[群分析相册] 上传成功 (LLBot, {label}): 群 {group_id}"
)
return
except Exception as e:
logger.warning(
f"[群分析相册] LLBot 上传接口调用失败: {e},尝试 NapCat 模式..."
)
params = { params = {
"group_id": int(group_id), "group_id": int(group_id),
"file": content, "file": content,
@@ -994,28 +1035,29 @@ class OneBotAdapter(PlatformAdapter):
获取群分析相册列表(兼容多种 OneBot 扩展实现)。 获取群分析相册列表(兼容多种 OneBot 扩展实现)。
""" """
def extract_list(data: Any) -> list[dict]: def extract_list(payload: Any) -> list[dict]:
if not data: """从不同结构的响应中提取相册列表:直接列表、嵌套在 data 中、或直接在根字段中。"""
return [] if isinstance(payload, list):
if isinstance(data, list): return [item for item in payload if isinstance(item, dict)]
return data if not isinstance(payload, dict):
if isinstance(data, dict): logger.debug(
# 探测常见键名 f"[群分析相册] 提取相册列表失败: payload 非字典/列表类型 ({type(payload)})"
lst = (
data.get("albums")
or data.get("album_list")
or data.get("albumList")
or data.get("list")
or []
) )
if isinstance(lst, list): return []
return lst
# 如果 'data' 键本身存在且为列表 data = payload.get("data")
inner_data = data.get("data") if isinstance(data, dict):
if isinstance(inner_data, list): album_list = data.get("album_list") or data.get("list")
return inner_data if isinstance(album_list, list):
if isinstance(inner_data, dict): return [item for item in album_list if isinstance(item, dict)]
return extract_list(inner_data) else:
logger.debug(f"[群分析相册] 在 data 字段中未找到列表: data={data}")
album_list = payload.get("album_list") or payload.get("list")
if isinstance(album_list, list):
return [item for item in album_list if isinstance(item, dict)]
logger.debug(f"[群分析相册] 无法从响应中提取相册列表: payload={payload}")
return [] return []
# 候选 API 名称 # 候选 API 名称
@@ -1033,13 +1075,14 @@ class OneBotAdapter(PlatformAdapter):
) )
result = await self.bot.call_action( result = await self.bot.call_action(
action, action,
group_id=int(group_id), # 确保传整型,对齐 NapCat 等实现 group_id=int(group_id),
) )
logger.debug(f"[群分析相册] 接口 {action} 原始响应内容: {result}")
if result: if result:
albums = extract_list(result) albums = extract_list(result)
if albums: if albums:
logger.debug( logger.debug(
f"[群分析相册] {action} 成功获取到 {len(albums)} 个相册" f"[群分析相册] {action} 成功获取并提取{len(albums)} 个相册对象"
) )
return albums return albums
except Exception as e: except Exception as e:
@@ -1070,11 +1113,21 @@ class OneBotAdapter(PlatformAdapter):
) )
albums = await self.get_group_album_list(group_id) albums = await self.get_group_album_list(group_id)
for album in albums: for album in albums:
name = album.get("name") or album.get("album_name", "") name = album.get("name") or album.get("album_name")
aid = album.get("album_id") or album.get("id", "") logger.debug(
if name == album_name and aid: f"[群分析相册] 正在匹配相册: 目标='{album_name}', 当前相册名称='{name}', 原始数据={album}"
logger.info(f"[群分析相册] 成功定位相册: '{album_name}' -> ID: {aid}") )
return str(aid) if name == album_name:
aid = album.get("album_id")
if aid:
logger.info(
f"[群分析相册] 成功定位相册: '{album_name}' -> ID: {aid}"
)
return str(aid)
else:
logger.debug(
f"[群分析相册] 相册 '{name}' 名称匹配,但未找到有效的 album_id"
)
logger.info(f"[群分析相册] 未能找到名为 '{album_name}' 的相册 (群 {group_id})") logger.info(f"[群分析相册] 未能找到名为 '{album_name}' 的相册 (群 {group_id})")
return None return None