mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 00:32:06 +08:00
* ✨ feat(hook): 增强认证钩子和运行时缓存管理 ``` ♻ refactor(hook): 移除未使用的配置项并优化缓存设置 移除 AUTH_HOOKS_CONCURRENCY_LIMIT 配置项,该配置项不再使用 ✨feat(auth_ban): 简化缓存配置并添加实体参数支持 将 BAN_CACHE_TTL 相关配置从动态配置改为常量定义, 移除复杂的 TTL 值转换逻辑,并为 auth_ban 函数添加可选的 entity 参数以支持外部传入实体信息 ♻ refactor(auth_limit): 移除未使用的配置依赖 移除 AUTH_LIMIT_NOTICE_CD 配置项,直接使用常量值 2 作为限制通知冷却时间 📦 依赖更新: update playwright dependency to version 1.57.0 in pyproject.toml and requirements.txt ``` * ✨ feat(auth_checker): 增强插件模块处理和预过滤逻辑,支持用户插件兼容性 * ✨ feat(http_utils): 添加内容缓存机制以优化HTTP响应处理 * ✨ feat(cache): 添加群组插件设置视图缓存类型并更新相关逻辑 * ✨ feat(renderer): 优化渲染引擎,增加内存缓存管理和HTML文档处理逻辑 * ✨ feat(sign_in): 添加好感度排行和好感度总排行命令 * ✨ feat(renderer): 增强浏览器实例管理和模板预处理,支持历史 include 语法兼容 * ✨ feat(renderer): 优化 Playwright 环境检查逻辑,增加结果缓存以提高性能 * ✨ feat(renderer): 增强模板文件渲染策略,优化资产加载路径处理 * 🚨 auto fix by pre-commit hooks * ✨ feat(log): 增加日志内容安全序列化,避免超长 base64 等污染日志 ✨ feat(log_sanitizer): 添加对嵌入超长 base64/data URI 的清理功能 ✨ feat(auth_checker): 添加 Alconna 快捷方式缓存检查,优化路由匹配逻辑 * feat(renderer): 添加渲染结果内存缓存功能以优化性能 feat(help): 实现帮助菜单图像缓存机制 feat(sign_in): 更新HTML卡片生成以支持动画禁用和剪裁 feat(superuser): 在启动时预热超级用户帮助缓存 feat(theme): 优化主题管理器的资源解析缓存 * 🚨 auto fix by pre-commit hooks * feat(renderer): 增加全页面视口最大宽度限制并优化内容尺寸计算 --------- Co-authored-by: ATTomatoo <1126160939@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com>
236 lines
8.7 KiB
Python
236 lines
8.7 KiB
Python
from typing import Any, TypeVar, overload
|
|
|
|
from pydantic import BaseModel, ValidationError
|
|
import ujson as json
|
|
|
|
from zhenxun.configs.config import Config
|
|
from zhenxun.models.group_plugin_setting import GroupPluginSetting
|
|
from zhenxun.services.cache import Cache
|
|
from zhenxun.services.data_access import DataAccess
|
|
from zhenxun.services.log import logger
|
|
from zhenxun.utils.enum import CacheType
|
|
from zhenxun.utils.pydantic_compat import model_dump, model_validate, parse_as
|
|
|
|
T = TypeVar("T", bound=BaseModel)
|
|
|
|
|
|
class GroupSettingsService:
|
|
"""
|
|
一个用于管理插件分群配置的服务。
|
|
集成了聚合缓存、批量操作和版本迁移功能。
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.dao = DataAccess(GroupPluginSetting)
|
|
self._cache = Cache[dict[str, Any]](CacheType.GROUP_PLUGIN_SETTINGS_VIEW)
|
|
|
|
@staticmethod
|
|
def _build_cache_key(group_id: str, plugin_name: str) -> str:
|
|
return f"{group_id}:{plugin_name}"
|
|
|
|
async def _clear_merged_cache(self, group_id: str, plugin_name: str) -> None:
|
|
await self._cache.delete(self._build_cache_key(group_id, plugin_name))
|
|
|
|
async def set(
|
|
self, group_id: str, plugin_name: str, settings_model: BaseModel
|
|
) -> None:
|
|
"""
|
|
为一个插件在指定群组中设置完整的配置模型。
|
|
|
|
参数:
|
|
group_id: 目标群组ID。
|
|
plugin_name: 插件的模块名。
|
|
settings_model: 包含完整配置的Pydantic模型实例。
|
|
"""
|
|
settings_dict = model_dump(settings_model)
|
|
json_value = json.dumps(settings_dict, ensure_ascii=False)
|
|
|
|
await self.dao.update_or_create(
|
|
defaults={"settings": json_value}, # type: ignore
|
|
group_id=group_id,
|
|
plugin_name=plugin_name,
|
|
)
|
|
|
|
await self.dao.clear_cache(group_id=group_id, plugin_name=plugin_name)
|
|
await self._clear_merged_cache(group_id, plugin_name)
|
|
|
|
async def set_key_value(
|
|
self, group_id: str, plugin_name: str, key: str, value: Any
|
|
) -> None:
|
|
"""为一个插件在指定群组中设置单个配置项的值。"""
|
|
setting_entry, _ = await GroupPluginSetting.get_or_create(
|
|
defaults={"settings": {}},
|
|
group_id=group_id,
|
|
plugin_name=plugin_name,
|
|
)
|
|
|
|
if not isinstance(setting_entry.settings, dict):
|
|
setting_entry.settings = {}
|
|
|
|
setting_entry.settings[key] = value
|
|
await setting_entry.save(update_fields=["settings"])
|
|
await self.dao.clear_cache(group_id=group_id, plugin_name=plugin_name)
|
|
await self._clear_merged_cache(group_id, plugin_name)
|
|
|
|
async def reset_key(self, group_id: str, plugin_name: str, key: str) -> bool:
|
|
"""重置单个配置项"""
|
|
setting = await self.dao.get_or_none(group_id=group_id, plugin_name=plugin_name)
|
|
if setting and isinstance(setting.settings, dict) and key in setting.settings:
|
|
del setting.settings[key]
|
|
if not setting.settings:
|
|
await setting.delete()
|
|
else:
|
|
await setting.save(update_fields=["settings"])
|
|
await self.dao.clear_cache(group_id=group_id, plugin_name=plugin_name)
|
|
await self._clear_merged_cache(group_id, plugin_name)
|
|
return True
|
|
return False
|
|
|
|
async def get(
|
|
self, group_id: str, plugin_name: str, key: str, default: Any = None
|
|
) -> Any:
|
|
"""
|
|
获取一个分群配置项的值,如果群组未单独设置,则回退到全局默认值。
|
|
|
|
参数:
|
|
group_id: 目标群组ID。
|
|
plugin_name: 插件的模块名。
|
|
key: 配置项的键。
|
|
default: 如果找不到配置项,返回的默认值。
|
|
|
|
返回:
|
|
配置项的值。
|
|
"""
|
|
full_settings = await self.get_all_for_plugin(group_id, plugin_name)
|
|
return full_settings.get(key, default)
|
|
|
|
async def reset_all_for_plugin(self, group_id: str, plugin_name: str) -> bool:
|
|
"""
|
|
重置一个插件在指定群组的配置,使其回退到全局默认值。
|
|
这通过删除数据库中的对应记录来实现。
|
|
|
|
参数:
|
|
group_id: 目标群组ID。
|
|
plugin_name: 插件的模块名。
|
|
|
|
返回:
|
|
bool: 如果成功删除了一个条目,则返回 True,否则返回 False。
|
|
"""
|
|
deleted_count = await self.dao.delete(
|
|
group_id=group_id, plugin_name=plugin_name
|
|
)
|
|
|
|
if deleted_count > 0:
|
|
await self.dao.clear_cache(group_id=group_id, plugin_name=plugin_name)
|
|
await self._clear_merged_cache(group_id, plugin_name)
|
|
logger.debug(f"已重置插件 '{plugin_name}' 在群组 '{group_id}' 的配置。")
|
|
return True
|
|
|
|
return False
|
|
|
|
@overload
|
|
async def get_all_for_plugin(
|
|
self, group_id: str, plugin_name: str, *, parse_model: type[T]
|
|
) -> T: ...
|
|
|
|
@overload
|
|
async def get_all_for_plugin(
|
|
self, group_id: str, plugin_name: str, *, parse_model: None = None
|
|
) -> dict[str, Any]: ...
|
|
|
|
async def get_all_for_plugin(
|
|
self, group_id: str, plugin_name: str, *, parse_model: type[T] | None = None
|
|
) -> T | dict[str, Any]:
|
|
"""
|
|
获取一个插件在指定群组中的完整配置,应用了“继承与覆盖”逻辑。
|
|
它首先获取全局默认配置,然后用数据库中存储的群组特定配置覆盖它。
|
|
|
|
参数:
|
|
group_id: 目标群组ID。
|
|
plugin_name: 插件的模块名。
|
|
parse_model: (可选) Pydantic模型,用于解析和验证配置。
|
|
"""
|
|
cache_key = self._build_cache_key(group_id, plugin_name)
|
|
cached_settings = await self._cache.get(cache_key)
|
|
if cached_settings is not None:
|
|
logger.debug(f"缓存命中: {cache_key}")
|
|
if parse_model:
|
|
try:
|
|
return parse_as(parse_model, cached_settings)
|
|
except (ValidationError, TypeError) as e:
|
|
logger.warning(
|
|
f"缓存数据 '{cache_key}' 与模型 '{parse_model.__name__}' "
|
|
f"不匹配: {e}。将从数据库重新加载。"
|
|
)
|
|
else:
|
|
return cached_settings
|
|
|
|
logger.debug(f"缓存未命中: {cache_key},从数据库加载。")
|
|
|
|
global_config_group = Config.get(plugin_name)
|
|
final_settings_dict = {
|
|
key: global_config_group.get(key, build_model=False)
|
|
for key in global_config_group.configs.keys()
|
|
}
|
|
|
|
group_setting_entry = await self.dao.get_or_none(
|
|
group_id=group_id, plugin_name=plugin_name
|
|
)
|
|
if group_setting_entry:
|
|
try:
|
|
group_specific_settings = group_setting_entry.settings
|
|
if isinstance(group_specific_settings, dict):
|
|
final_settings_dict.update(group_specific_settings)
|
|
else:
|
|
logger.warning(
|
|
f"群组 {group_id} 插件 '{plugin_name}' 的配置格式不正确"
|
|
f"(不是字典),已忽略。"
|
|
)
|
|
except Exception as e:
|
|
logger.warning(
|
|
f"加载群组 {group_id} 插件 '{plugin_name}' 的特定配置时出错: {e}"
|
|
)
|
|
|
|
await self._cache.set(cache_key, final_settings_dict)
|
|
|
|
if parse_model:
|
|
try:
|
|
return parse_as(parse_model, final_settings_dict)
|
|
except (ValidationError, TypeError) as e:
|
|
logger.warning(
|
|
f"插件 '{plugin_name}' 的配置无法解析为 '{parse_model.__name__}'。"
|
|
f"值: {final_settings_dict}, 错误: {e}。将返回一个默认模型实例。"
|
|
)
|
|
return parse_as(parse_model, {})
|
|
|
|
return final_settings_dict
|
|
|
|
async def set_bulk(
|
|
self, group_ids: list[str], plugin_name: str, key: str, value: Any
|
|
) -> tuple[int, int]:
|
|
"""
|
|
为多个群组批量设置同一个配置项。
|
|
|
|
参数:
|
|
group_ids: 目标群组ID列表。
|
|
plugin_name: 插件模块名。
|
|
key: 配置项的键。
|
|
value: 要设置的值。
|
|
|
|
返回:
|
|
一个元组 (updated_count, created_count)。
|
|
"""
|
|
if not group_ids:
|
|
return 0, 0
|
|
|
|
for group_id in group_ids:
|
|
current_settings = await self.get_all_for_plugin(group_id, plugin_name)
|
|
current_settings[key] = value
|
|
await self.set(
|
|
group_id, plugin_name, model_validate(BaseModel, current_settings)
|
|
)
|
|
return len(group_ids), 0
|
|
|
|
|
|
group_settings_service = GroupSettingsService()
|