✨ 引入缓存机制 (#1889)

* 添加全局cache

* ✨ 构建缓存,hook使用缓存

* ✨ 新增数据库Model方法监控

* ✨ 数据库添加semaphore锁

* 🩹 优化webapi返回数据

* ✨ 添加增量缓存与缓存过期

* 🎨 优化检测代码结构

* ⚡ 优化hook权限检测性能

* 🐛 添加新异常判断跳过权限检测

* ✨ 添加插件limit缓存

* 🎨 代码格式优化

* 🐛  修复代码导入

* 🐛 修复刷新时检查

* 👽 Rename exception for missing database URL in initialization

* ♿ Update default database URL to SQLite in configuration

* 🔧 Update tortoise-orm and aiocache dependencies restrictions; add optional redis and asyncpg support

* 🐛 修复ban检测

* 🐛 修复所有插件关闭时缓存更新

* 🐛 尝试迁移至aiocache

* 🐛 完善aiocache缓存

* ⚡ 代码性能优化

* 🐛 移除获取封禁缓存时的日志记录

* 🐛 修复缓存类型声明,优化封禁用户处理逻辑

* 🐛 优化LevelUser权限更新逻辑及数据库迁移

* ✨ cache支持redis连接

* 🚨 auto fix by pre-commit hooks

* ⚡ :增强获取群组的安全性和准确性。同时,优化了缓存管理中的相关逻辑,确保缓存操作的一致性。

* ✨ feat(auth_limit): 将插件初始化逻辑的启动装饰器更改为优先级管理器

* 🔧 修复日志记录级别

* 🔧 更新数据库连接字符串

* 🔧 更新数据库连接字符串为内存数据库,并优化权限检查逻辑

* ✨ feat(cache): 增加缓存功能配置项,并新增数据访问层以支持缓存逻辑

* ♻️ 重构cache

* ✨ feat(cache): 增强缓存管理,新增缓存字典和缓存列表功能,支持过期时间管理

* 🔧 修复Notebook类中的viewport高度设置,将其从1000调整为10

* ✨ 更新插件管理逻辑,替换缓存服务为CacheRoot并优化缓存失效处理

* ✨ 更新RegisterConfig类中的type字段

* ✨ 修复清理重复记录逻辑,确保检查记录的id属性有效性

* ⚡ 超级无敌大优化,解决延迟与卡死问题

* ✨ 更新封禁功能,增加封禁时长参数和描述,优化插件信息返回结构

* ✨ 更新zhenxun_help.py中的viewport高度,将其从453调整为10,以优化页面显示效果

* ✨ 优化插件分类逻辑,增加插件ID排序,并更新插件信息返回结构

---------

Co-authored-by: BalconyJH <balconyjh@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
HibiKier
2025-07-14 22:35:29 +08:00
committed by GitHub
co-authored by BalconyJH pre-commit-ci[bot]
parent 6283c3d13d
commit 8649aaaa54
57 changed files with 5179 additions and 1479 deletions
+3 -1
View File
@@ -45,11 +45,13 @@ async def classify_plugin(
"""
sort_data = await sort_type()
classify: dict[str, list] = {}
group = await GroupConsole.get_or_none(group_id=group_id) if group_id else None
group = await GroupConsole.get_group(group_id=group_id) if group_id else None
bot = await BotConsole.get_or_none(bot_id=session.self_id)
for menu, value in sort_data.items():
for plugin in value:
if not classify.get(menu):
classify[menu] = []
classify[menu].append(handle(bot, plugin, group, is_detail))
for value in classify.values():
value.sort(key=lambda x: x.id)
return classify
+6 -5
View File
@@ -21,6 +21,8 @@ class Item(BaseModel):
"""插件名称"""
sta: int
"""插件状态"""
id: int
"""插件id"""
class PluginList(BaseModel):
@@ -80,10 +82,9 @@ def __handle_item(
sta = 2
if f"{plugin.module}," in group.block_plugin:
sta = 1
if bot:
if f"{plugin.module}," in bot.block_plugins:
sta = 2
return Item(plugin_name=plugin.name, sta=sta)
if bot and f"{plugin.module}," in bot.block_plugins:
sta = 2
return Item(plugin_name=plugin.name, sta=sta, id=plugin.id)
def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
@@ -142,7 +143,7 @@ async def build_html_image(
template_name="zhenxun_menu.html",
templates={"plugin_list": plugin_list},
pages={
"viewport": {"width": 1903, "height": 975},
"viewport": {"width": 1903, "height": 10},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,
+2 -2
View File
@@ -45,7 +45,7 @@ async def build_normal_image(group_id: str | None, is_detail: bool) -> BuildImag
color="black" if idx % 2 else "white",
)
curr_h = 10
group = await GroupConsole.get_or_none(group_id=group_id)
group = await GroupConsole.get_group(group_id=group_id) if group_id else None
for _, plugin in enumerate(plugin_list):
text_color = (255, 255, 255) if idx % 2 else (0, 0, 0)
if group and f"{plugin.module}," in group.block_plugin:
@@ -80,7 +80,7 @@ async def build_normal_image(group_id: str | None, is_detail: bool) -> BuildImag
width, height = 10, 10
for s in [
"目前支持的功能列表:",
"可以通过 ‘帮助 [功能名称或功能Id]’ 来获取对应功能的使用方法",
"可以通过 '帮助 [功能名称或功能Id]' 来获取对应功能的使用方法",
]:
text = await BuildImage.build_text_image(s, "HYWenHei-85W.ttf", 24)
await result.paste(text, (width, height))
+31 -68
View File
@@ -20,6 +20,12 @@ class Item(BaseModel):
"""插件名称"""
commands: list[str]
"""插件命令"""
id: str
"""插件id"""
status: bool
"""插件状态"""
has_superuser_help: bool
"""插件是否拥有超级用户帮助"""
def __handle_item(
@@ -39,23 +45,36 @@ def __handle_item(
返回:
Item: Item
"""
status = True
has_superuser_help = False
nb_plugin = nonebot.get_plugin_by_module_name(plugin.module_path)
if nb_plugin and nb_plugin.metadata and nb_plugin.metadata.extra:
extra_data = PluginExtraData(**nb_plugin.metadata.extra)
if extra_data.superuser_help:
has_superuser_help = True
if not plugin.status:
if plugin.block_type == BlockType.ALL:
plugin.name = f"{plugin.name}(不可用)"
status = False
elif group and plugin.block_type == BlockType.GROUP:
plugin.name = f"{plugin.name}(不可用)"
status = False
elif not group and plugin.block_type == BlockType.PRIVATE:
plugin.name = f"{plugin.name}(不可用)"
status = False
elif group and f"{plugin.module}," in group.block_plugin:
plugin.name = f"{plugin.name}(不可用)"
status = False
elif bot and f"{plugin.module}," in bot.block_plugins:
plugin.name = f"{plugin.name}(不可用)"
status = False
commands = []
nb_plugin = nonebot.get_plugin_by_module_name(plugin.module_path)
if is_detail and nb_plugin and nb_plugin.metadata and nb_plugin.metadata.extra:
extra_data = PluginExtraData(**nb_plugin.metadata.extra)
commands = [cmd.command for cmd in extra_data.commands]
return Item(plugin_name=f"{plugin.id}-{plugin.name}", commands=commands)
return Item(
plugin_name=plugin.name,
commands=commands,
id=str(plugin.id),
status=status,
has_superuser_help=has_superuser_help,
)
def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
@@ -78,68 +97,10 @@ def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
}
for menu, value in classify.items()
]
plugin_list = build_line_data(plugin_list)
plugin_list.insert(
0,
build_plugin_line(
menu_key if menu_key not in ["normal", "功能"] else "主要功能",
max_data,
30,
100,
True,
),
)
return plugin_list
def build_plugin_line(
name: str, items: list, left: int, width: int | None = None, is_max: bool = False
) -> dict:
"""构造插件行数据
参数:
name: 菜单名称
items: 插件名称列表
left: 左边距
width: 总插件长度.
is_max: 是否为最大长度的插件菜单
返回:
dict: 插件数据
"""
_plugins = []
width = width or 50
if len(items) // 2 > 6 or is_max:
width = 100
plugin_list1 = []
plugin_list2 = []
for i in range(len(items)):
if i % 2:
plugin_list1.append(items[i])
else:
plugin_list2.append(items[i])
_plugins = [(30, 50, plugin_list1), (0, 50, plugin_list2)]
else:
_plugins = [(left, 100, items)]
return {"name": name, "items": _plugins, "width": width}
def build_line_data(plugin_list: list[dict]) -> list[dict]:
"""构造插件数据
参数:
plugin_list: 插件列表
返回:
list[dict]: 插件数据
"""
left = 30
data = []
plugin_list.insert(0, {"name": menu_key, "items": max_data})
for plugin in plugin_list:
data.append(build_plugin_line(plugin["name"], plugin["items"], left))
if len(plugin["items"]) // 2 <= 6:
left = 15 if left == 30 else 30
return data
plugin["items"].sort(key=lambda x: x.id)
return plugin_list
async def build_zhenxun_image(
@@ -160,6 +121,7 @@ async def build_zhenxun_image(
width = int(637 * 1.5) if is_detail else 637
title_font = int(53 * 1.5) if is_detail else 53
tip_font = int(19 * 1.5) if is_detail else 19
plugin_count = sum(len(plugin["items"]) for plugin in plugin_list)
return await template_to_pic(
template_path=str((TEMPLATE_PATH / "ss_menu").absolute()),
template_name="main.html",
@@ -170,10 +132,11 @@ async def build_zhenxun_image(
"width": width,
"font_size": (title_font, tip_font),
"is_detail": is_detail,
"plugin_count": plugin_count,
}
},
pages={
"viewport": {"width": width, "height": 453},
"viewport": {"width": width, "height": 10},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,