Merge branch 'main' into dev

This commit is contained in:
镀铬酸钾
2026-01-23 17:16:58 +08:00
committed by GitHub
9 changed files with 169 additions and 697 deletions

View File

@@ -5,11 +5,35 @@
状态设置等相关的 OneBot v11 API 封装。
"""
import orjson
from typing import Dict, Any
from typing import Dict, Any, Type, TypeVar
from dataclasses import is_dataclass, fields
from .base import BaseAPI
from models.objects import LoginInfo, VersionInfo, Status
from ..managers.redis_manager import redis_manager
T = TypeVar('T')
def _safe_dataclass_from_dict(cls: Type[T], data: Dict[str, Any]) -> T:
"""
安全地从字典创建 dataclass 实例,忽略多余的键。
"""
if not data:
try:
return cls()
except TypeError:
raise ValueError(f"无法在没有数据的情况下创建 {cls.__name__} 的实例")
# 使用官方的 is_dataclass 进行检查,对 MyPyC 更友好
if not is_dataclass(cls):
raise TypeError(f"{cls.__name__} 不是一个 dataclass")
# 获取 dataclass 的所有字段名
known_fields = {f.name for f in fields(cls)}
# 过滤出 dataclass 认识的键值对
filtered_data = {k: v for k, v in data.items() if k in known_fields}
return cls(**filtered_data)
class AccountAPI(BaseAPI):
"""
@@ -30,11 +54,11 @@ class AccountAPI(BaseAPI):
if not no_cache:
cached_data = await redis_manager.get(cache_key)
if cached_data:
return LoginInfo(**orjson.loads(cached_data))
return _safe_dataclass_from_dict(LoginInfo, orjson.loads(cached_data))
res = await self.call_api("get_login_info")
await redis_manager.set(cache_key, orjson.dumps(res), ex=3600) # 缓存 1 小时
return LoginInfo(**res)
return _safe_dataclass_from_dict(LoginInfo, res)
async def get_version_info(self) -> VersionInfo:
"""
@@ -43,8 +67,8 @@ class AccountAPI(BaseAPI):
Returns:
VersionInfo: 包含 OneBot 实现版本信息的 `VersionInfo` 数据对象。
"""
res = await self.call_api("get_friend_list")
return VersionInfo(**res)
res = await self.call_api("get_version_info")
return _safe_dataclass_from_dict(VersionInfo, res)
async def get_status(self) -> Status:
"""
@@ -54,7 +78,7 @@ class AccountAPI(BaseAPI):
Status: 包含 OneBot 状态信息的 `Status` 数据对象。
"""
res = await self.call_api("get_status")
return Status(**res)
return _safe_dataclass_from_dict(Status, res)
async def bot_exit(self) -> Dict[str, Any]:
"""
@@ -162,56 +186,25 @@ class AccountAPI(BaseAPI):
"""
return await self.call_api("clean_cache")
async def get_stranger_info(self, user_id: int, no_cache: bool = False) -> Any:
async def get_profile_like(self) -> Dict[str, Any]:
"""
获取陌生人信息。
获取个人资料的点赞信息。
Returns:
Dict[str, Any]: OneBot API 的响应数据。
"""
return await self.call_api("get_profile_like")
async def nc_get_user_status(self, user_id: int) -> Dict[str, Any]:
"""
获取用户的在线状态 (NapCat 特有 API)。
Args:
user_id (int): 目标用户的 QQ 号。
no_cache (bool, optional): 是否不使用缓存。Defaults to False.
Returns:
Any: 包含陌生人信息的字典或对象
Dict[str, Any]: OneBot API 的响应数据
"""
return await self.call_api("get_stranger_info", {"user_id": user_id, "no_cache": no_cache})
return await self.call_api("nc_get_user_status", {"user_id": user_id})
async def get_friend_list(self, no_cache: bool = False) -> list:
"""
获取好友列表。
Args:
no_cache (bool, optional): 是否不使用缓存。Defaults to False.
Returns:
list: 好友列表。
"""
cache_key = f"neobot:cache:get_friend_list:{self.self_id}"
if not no_cache:
cached_data = await redis_manager.get(cache_key)
if cached_data:
return orjson.loads(cached_data)
res = await self.call_api("get_friend_list")
await redis_manager.set(cache_key, orjson.dumps(res), ex=3600) # 缓存 1 小时
return res
async def get_group_list(self, no_cache: bool = False) -> list:
"""
获取群列表。
Args:
no_cache (bool, optional): 是否不使用缓存。Defaults to False.
Returns:
list: 群列表。
"""
cache_key = f"neobot:cache:get_group_list:{self.self_id}"
if not no_cache:
cached_data = await redis_manager.get(cache_key)
if cached_data:
return orjson.loads(cached_data)
res = await self.call_api("get_group_list")
await redis_manager.set(cache_key, orjson.dumps(res), ex=3600) # 缓存 1 小时
return res

View File

@@ -122,7 +122,13 @@ class ImageManager(Singleton):
content = f.read()
mime_type = "image/jpeg" if image_type == "jpeg" else "image/png"
return f"data:{mime_type};base64," + base64.b64encode(content).decode("utf-8")
base64_str = base64.b64encode(content).decode("utf-8")
# 记录摘要日志,避免刷屏
log_message = f"Base64 图片已生成 (MIME: {mime_type}, Size: {len(base64_str)/1024:.2f} KB, Preview: {base64_str[:30]}...{base64_str[-30:]})"
logger.debug(log_message)
return f"data:{mime_type};base64," + base64_str
except Exception as e:
logger.error(f"读取图片文件失败: {e}")
return None

View File

@@ -412,7 +412,7 @@ class PermissionManager(Singleton):
"""
try:
# 创建空的权限数据
empty_data = {"users": {}}
empty_data: Dict[str, Dict] = {"users": {}}
# 原子性写入文件
temp_file = self.data_file + ".tmp"