From 686d6a90f25916c0827b2da6c01a45f70e9d592a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B2-=E5=90=B2=E5=93=9A=E5=9F=BA=E4=B8=99=E6=B0=A8?= =?UTF-8?q?=E9=85=B8awa?= <100545261+tryptophawa@users.noreply.github.com> Date: Sat, 23 May 2026 16:31:21 +0800 Subject: [PATCH] feat(onebot): Support Snowluma (#189) * Update onebot_adapter.py * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../platform/adapters/onebot_adapter.py | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/infrastructure/platform/adapters/onebot_adapter.py b/src/infrastructure/platform/adapters/onebot_adapter.py index 5377f46..5daa2d5 100644 --- a/src/infrastructure/platform/adapters/onebot_adapter.py +++ b/src/infrastructure/platform/adapters/onebot_adapter.py @@ -66,6 +66,10 @@ class OneBotAdapter(PlatformAdapter): self._is_llbot = False self._llbot_checked = False + # SnowLuma 探测标志 + self._is_snowluma = False + self._snowluma_checked = False + def _init_capabilities(self) -> PlatformCapabilities: """返回预定义的 OneBot v11 能力集。""" return ONEBOT_V11_CAPABILITIES @@ -86,6 +90,22 @@ class OneBotAdapter(PlatformAdapter): self._is_llbot = False self._llbot_checked = True + async def _detect_snowluma(self): + """探测是否为 SnowLuma""" + if self._snowluma_checked: + return + try: + result = await self.bot.call_action("get_version_info") + if isinstance(result, dict): + app_name = result.get("app_name", "") + self._is_snowluma = app_name.lower() == "snowluma" + if self._is_snowluma: + logger.info("[OneBot] 探测到当前协议端为 SnowLuma") + except Exception as exc: + logger.debug("[OneBot] 探测 SnowLuma 失败,将按非 SnowLuma 处理: %s", exc, exc_info=True) + self._is_snowluma = False + self._snowluma_checked = True + def _get_nearest_size(self, requested_size: int) -> int: """从支持的尺寸列表中找到最接近请求尺寸的一个。""" return min(self.AVAILABLE_SIZES, key=lambda x: abs(x - requested_size)) @@ -117,6 +137,8 @@ class OneBotAdapter(PlatformAdapter): if not hasattr(self.bot, "call_action"): return [] + await self._detect_snowluma() + try: chunk_size = 100 # 每次拉取 100 条,较为稳健 all_raw_messages = [] @@ -144,11 +166,14 @@ class OneBotAdapter(PlatformAdapter): params = { "group_id": int(group_id), "count": fetch_count, - "reverseOrder": True, # 关键:协助分页向上回退拉取历史 } - - if current_anchor_id: - params["message_seq"] = current_anchor_id + if self._is_snowluma: + if current_anchor_id: + params["message_id"] = current_anchor_id + else: + params["reverseOrder"] = True + if current_anchor_id: + params["message_seq"] = current_anchor_id result = await self.bot.call_action("get_group_msg_history", **params) @@ -199,22 +224,21 @@ class OneBotAdapter(PlatformAdapter): all_raw_messages.append(raw_msg) # 提取锚点。 - # 优先级: message_seq > real_id > seq > message_id + # SnowLuma 仅支持 message_id 作为分页锚点。 + # 其他 OneBot 实现优先级: message_seq > real_id > seq > message_id # 注意:为了兼容 NapCat (NTQQ) 这种 Message ID 非连续的情况, # 以及 LLBot 这种 Sequence 模式,我们统一不进行 -1 偏移。 # 分页产生的重叠消息将由上方的去重逻辑 (all_raw_messages 循环对比) 自动处理。 - seq_val = ( - chunk_earliest_msg.get("message_seq") - or chunk_earliest_msg.get("real_id") - or chunk_earliest_msg.get("seq") - ) - mid_val = chunk_earliest_msg.get("message_id") - - # 重要修复:取消对 ID 的 -1 位移手动操作。 - # 在 NapCat/NTQQ 中,ID 虽为数字但并不连续。-1 位移会导致“消息不存在”错误。 - # 即使 API 返回的消息包含锚点本身,上方的 deduplication 逻辑也会将其排除, - # 保证翻页能正常向前推进。 - new_anchor_id = seq_val if seq_val is not None else mid_val + if self._is_snowluma: + new_anchor_id = chunk_earliest_msg.get("message_id") + else: + seq_val = ( + chunk_earliest_msg.get("message_seq") + or chunk_earliest_msg.get("real_id") + or chunk_earliest_msg.get("seq") + ) + mid_val = chunk_earliest_msg.get("message_id") + new_anchor_id = seq_val if seq_val is not None else mid_val # 如果消息时间已到达起始点,或者锚点无法继续往前位移,则停止 if chunk_earliest_time <= start_timestamp: