mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 13:38:43 +00:00
fix: 尝试分页拉取
This commit is contained in:
@@ -4,6 +4,7 @@ OneBot v11 平台适配器
|
|||||||
支持 NapCat、go-cqhttp、Lagrange 及其他 OneBot 实现。
|
支持 NapCat、go-cqhttp、Lagrange 及其他 OneBot 实现。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -80,12 +81,13 @@ class OneBotAdapter(PlatformAdapter):
|
|||||||
) -> list[UnifiedMessage]:
|
) -> list[UnifiedMessage]:
|
||||||
"""
|
"""
|
||||||
从 OneBot 后端拉取群组历史消息。
|
从 OneBot 后端拉取群组历史消息。
|
||||||
|
采用分页拉取策略(参考 portrayal 插件),减少 NapCat/go-cqhttp 单次请求的 CPU 和内存负担。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
group_id (str): 群号
|
group_id (str): 群号
|
||||||
days (int): 拉取过去几天的消息
|
days (int): 拉取过去几天的消息
|
||||||
max_count (int): 最大拉取条数
|
max_count (int): 最大拉取条数
|
||||||
before_id (str, optional): 锚点消息 ID(部分后端支持)
|
before_id (str, optional): 锚点消息 ID,用于分页回溯
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[UnifiedMessage]: 统一格式的消息列表
|
list[UnifiedMessage]: 统一格式的消息列表
|
||||||
@@ -94,41 +96,92 @@ class OneBotAdapter(PlatformAdapter):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 调用 OneBot 标准 API: get_group_msg_history
|
chunk_size = 100 # 每次拉取 100 条,较为稳健
|
||||||
result = await self.bot.call_action(
|
all_raw_messages = []
|
||||||
"get_group_msg_history",
|
|
||||||
group_id=int(group_id),
|
|
||||||
count=max_count,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not result or "messages" not in result:
|
|
||||||
return []
|
|
||||||
|
|
||||||
end_time = datetime.now()
|
end_time = datetime.now()
|
||||||
start_time = end_time - timedelta(days=days)
|
start_time = end_time - timedelta(days=days)
|
||||||
|
start_timestamp = int(start_time.timestamp())
|
||||||
|
|
||||||
messages = []
|
# 使用 message_seq (在 NapCat 中通常可用 message_id 作为 seq 参数)
|
||||||
for raw_msg in result.get("messages", []):
|
# 进行分页回溯拉取
|
||||||
msg_time = datetime.fromtimestamp(raw_msg.get("time", 0))
|
current_anchor_id = before_id
|
||||||
# 时间范围过滤
|
|
||||||
if not (start_time <= msg_time <= end_time):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 身份过滤(排除机器人自己)
|
logger.info(
|
||||||
sender_id = str(raw_msg.get("sender", {}).get("user_id", ""))
|
f"OneBot 开始分页回溯拉取消息: 群 {group_id}, 时间限制 {days}天, 数量限制 {max_count}"
|
||||||
if sender_id in self.bot_self_ids:
|
)
|
||||||
|
|
||||||
|
while len(all_raw_messages) < max_count:
|
||||||
|
fetch_count = min(chunk_size, max_count - len(all_raw_messages))
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"group_id": int(group_id),
|
||||||
|
"count": fetch_count,
|
||||||
|
"reverseOrder": True, # 关键:协助分页向上回退拉取历史
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_anchor_id:
|
||||||
|
params["message_seq"] = current_anchor_id
|
||||||
|
|
||||||
|
result = await self.bot.call_action("get_group_msg_history", **params)
|
||||||
|
|
||||||
|
if not result or "messages" not in result:
|
||||||
|
break
|
||||||
|
|
||||||
|
messages = result.get("messages", [])
|
||||||
|
if not messages:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 时间过滤与列表合并
|
||||||
|
# 消息通常是按时间正序排列的,即 messages[0] 最旧,messages[-1] 最新
|
||||||
|
chunk_earliest_msg = messages[0]
|
||||||
|
chunk_earliest_time = chunk_earliest_msg.get("time", 0)
|
||||||
|
|
||||||
|
for raw_msg in messages:
|
||||||
|
msg_time = raw_msg.get("time", 0)
|
||||||
|
|
||||||
|
# 身份过滤(排除机器人自己)
|
||||||
|
sender_id = str(raw_msg.get("sender", {}).get("user_id", ""))
|
||||||
|
if sender_id in self.bot_self_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 时间范围判定
|
||||||
|
if start_timestamp <= msg_time <= int(end_time.timestamp()):
|
||||||
|
all_raw_messages.append(raw_msg)
|
||||||
|
|
||||||
|
# 更新锚点为该批次最旧的消息 ID,用于下一次回溯请求
|
||||||
|
current_anchor_id = chunk_earliest_msg.get("message_id")
|
||||||
|
|
||||||
|
# 如果该批次最旧的消息已经早于 start_time,或者拉取的数量不足,说明已到达回溯终点
|
||||||
|
if chunk_earliest_time < start_timestamp or len(messages) < fetch_count:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 稍微延迟,减缓服务端压力(避免 NapCat 瞬间 CPU 飙升)
|
||||||
|
await asyncio.sleep(0.05)
|
||||||
|
|
||||||
|
# 统一转换为 UnifiedMessage 并在返回前去重排序
|
||||||
|
unified_messages = []
|
||||||
|
seen_ids = set()
|
||||||
|
for raw_msg in all_raw_messages:
|
||||||
|
mid = str(raw_msg.get("message_id", ""))
|
||||||
|
if not mid or mid in seen_ids:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
unified = self._convert_message(raw_msg, group_id)
|
unified = self._convert_message(raw_msg, group_id)
|
||||||
if unified:
|
if unified:
|
||||||
messages.append(unified)
|
unified_messages.append(unified)
|
||||||
|
seen_ids.add(mid)
|
||||||
|
|
||||||
# 确保按时间顺序排列
|
# 确保最终结果符合时间顺序
|
||||||
messages.sort(key=lambda m: m.timestamp)
|
unified_messages.sort(key=lambda m: m.timestamp)
|
||||||
return messages
|
|
||||||
|
logger.info(
|
||||||
|
f"OneBot 分页拉取完成: 共处理 {len(all_raw_messages)} 条原始消息, 最终有效 {len(unified_messages)} 条"
|
||||||
|
)
|
||||||
|
return unified_messages
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"OneBot 获取消息失败: {e}")
|
logger.warning(f"OneBot 分页获取消息失败: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _convert_message(self, raw_msg: dict, group_id: str) -> UnifiedMessage | None:
|
def _convert_message(self, raw_msg: dict, group_id: str) -> UnifiedMessage | None:
|
||||||
|
|||||||
Reference in New Issue
Block a user