93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
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
|