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": {
"description": "群文件/群相册上传设置",
"type": "object",
"hint": "图片报告生成后自动上传到群文件目录或群相册。群文件上传基于 OneBot upload_group_file API,群相册上传为 NapCat 扩展 API。仅对 QQ(OneBot)平台的图片格式报告生效",
"hint": "图片报告生成后自动上传到群文件目录或群相册。群文件上传基于 OneBot upload_group_file API。仅对 QQOneBot)平台的图片格式报告生效",
"items": {
"enable_group_file_upload": {
"type": "bool",
@@ -440,7 +440,7 @@
},
"enable_group_album_upload": {
"type": "bool",
"description": "启用群相册上传NapCat 通过测试)",
"description": "启用群相册上传",
"default": false,
"hint": "图片报告生成后,自动上传到群相册(确保 Bot 有权限上传群相册)。此功能为 NapCat 扩展 API,其他 OneBot 实现可能不支持"
},
@@ -62,10 +62,30 @@ class OneBotAdapter(PlatformAdapter):
if not self.bot_self_ids and config:
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:
"""返回预定义的 OneBot v11 能力集。"""
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:
"""从支持的尺寸列表中找到最接近请求尺寸的一个。"""
return min(self.AVAILABLE_SIZES, key=lambda x: abs(x - requested_size))
@@ -946,7 +966,7 @@ class OneBotAdapter(PlatformAdapter):
if not album_id:
albums = await self.get_group_album_list(group_id)
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:
@@ -959,6 +979,27 @@ class OneBotAdapter(PlatformAdapter):
return False
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 = {
"group_id": int(group_id),
"file": content,
@@ -994,28 +1035,29 @@ class OneBotAdapter(PlatformAdapter):
获取群分析相册列表(兼容多种 OneBot 扩展实现)。
"""
def extract_list(data: Any) -> list[dict]:
if not data:
return []
if isinstance(data, list):
return data
if isinstance(data, dict):
# 探测常见键名
lst = (
data.get("albums")
or data.get("album_list")
or data.get("albumList")
or data.get("list")
or []
def extract_list(payload: Any) -> list[dict]:
"""从不同结构的响应中提取相册列表:直接列表、嵌套在 data 中、或直接在根字段中。"""
if isinstance(payload, list):
return [item for item in payload if isinstance(item, dict)]
if not isinstance(payload, dict):
logger.debug(
f"[群分析相册] 提取相册列表失败: payload 非字典/列表类型 ({type(payload)})"
)
if isinstance(lst, list):
return lst
# 如果 'data' 键本身存在且为列表
inner_data = data.get("data")
if isinstance(inner_data, list):
return inner_data
if isinstance(inner_data, dict):
return extract_list(inner_data)
return []
data = payload.get("data")
if isinstance(data, dict):
album_list = data.get("album_list") or data.get("list")
if isinstance(album_list, list):
return [item for item in album_list if isinstance(item, dict)]
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 []
# 候选 API 名称
@@ -1033,13 +1075,14 @@ class OneBotAdapter(PlatformAdapter):
)
result = await self.bot.call_action(
action,
group_id=int(group_id), # 确保传整型,对齐 NapCat 等实现
group_id=int(group_id),
)
logger.debug(f"[群分析相册] 接口 {action} 原始响应内容: {result}")
if result:
albums = extract_list(result)
if albums:
logger.debug(
f"[群分析相册] {action} 成功获取到 {len(albums)} 个相册"
f"[群分析相册] {action} 成功获取并提取{len(albums)} 个相册对象"
)
return albums
except Exception as e:
@@ -1070,11 +1113,21 @@ class OneBotAdapter(PlatformAdapter):
)
albums = await self.get_group_album_list(group_id)
for album in albums:
name = album.get("name") or album.get("album_name", "")
aid = album.get("album_id") or album.get("id", "")
if name == album_name and aid:
logger.info(f"[群分析相册] 成功定位相册: '{album_name}' -> ID: {aid}")
return str(aid)
name = album.get("name") or album.get("album_name")
logger.debug(
f"[群分析相册] 正在匹配相册: 目标='{album_name}', 当前相册名称='{name}', 原始数据={album}"
)
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})")
return None