import hashlib import json from collections.abc import Mapping from typing import Any from astrbot.api import logger from ..request import APIResponseFormatError from .base import EndpointAdapter class JX3BoxAdapter(EndpointAdapter): """适配 JX3BOX 响应结构,并集中实现其分页规则。""" def extract_payload(self, payload: Any, out_key: str | None) -> Any: if out_key in (None, ""): return payload if not isinstance(payload, Mapping): raise APIResponseFormatError( f"JX3BOX 响应不是对象,无法提取字段: {out_key}" ) if out_key not in payload: raise APIResponseFormatError(f"JX3BOX 响应缺少字段: {out_key}") return payload[out_key] async def request_all_pages( self, endpoint_name: str, *, params: Mapping[str, Any] | None = None, out_key: str | None = None, path_params: Mapping[str, Any] | None = None, page_key: str = "page", list_key: str = "list", max_pages: int = 10, ) -> list[Any]: if max_pages < 1: raise ValueError("max_pages 必须大于等于 1") all_items: list[Any] = [] request_params = dict(params or {}) previous_page_hash: str | None = None for current_page in range(1, max_pages + 1): request_params[page_key] = str(current_page) data = await self.request( endpoint_name, params=request_params, out_key=out_key, path_params=path_params, ) page_items = self._extract_page_items(data, list_key) if not page_items: break page_hash = hashlib.sha256( json.dumps( page_items, ensure_ascii=False, sort_keys=True, default=str, ).encode("utf-8") ).hexdigest() if page_hash == previous_page_hash: logger.warning( "JX3BOX 分页接口连续返回重复页面,已停止: " f"key={endpoint_name}, page={current_page}" ) break all_items.extend(page_items) previous_page_hash = page_hash return all_items @staticmethod def _extract_page_items(data: Any, list_key: str) -> list[Any]: if data is None: return [] if isinstance(data, bytes): raise APIResponseFormatError("JX3BOX 分页接口返回了二进制内容") if isinstance(data, list): return data if not isinstance(data, Mapping): raise APIResponseFormatError("JX3BOX 分页响应必须是列表或对象") if list_key not in data: raise APIResponseFormatError(f"JX3BOX 分页响应缺少字段: {list_key}") items = data[list_key] if not isinstance(items, list): raise APIResponseFormatError(f"JX3BOX 分页字段 {list_key} 不是列表") return items