mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-14 21:52:56 +08:00
✨ 重构帮助,限制普通用户查询管理插件 (#1626)
This commit is contained in:
parent
d97e437e80
commit
029a731fb9
2
.gitignore
vendored
2
.gitignore
vendored
@ -174,8 +174,6 @@ data/
|
||||
/resources/image/prts/
|
||||
/configs/config.py
|
||||
configs/config.yaml
|
||||
./.env
|
||||
./.env.dev
|
||||
plugins/csgo_server/
|
||||
plugins/activity/
|
||||
!/resources/image/genshin/alc/back.png
|
||||
|
||||
@ -16,10 +16,9 @@ from nonebot_plugin_alconna import (
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.enum import PluginType
|
||||
from zhenxun.utils.message import MessageUtils
|
||||
from zhenxun.configs.path_config import IMAGE_PATH
|
||||
from zhenxun.configs.utils import RegisterConfig, PluginExtraData
|
||||
from zhenxun.builtin_plugins.help._config import GROUP_HELP_PATH, SIMPLE_HELP_IMAGE
|
||||
|
||||
from ._utils import GROUP_HELP_PATH
|
||||
from ._data_source import create_help_img, get_plugin_help
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
@ -42,10 +41,6 @@ __plugin_meta__ = PluginMetadata(
|
||||
)
|
||||
|
||||
|
||||
SIMPLE_HELP_IMAGE = IMAGE_PATH / "SIMPLE_HELP.png"
|
||||
if SIMPLE_HELP_IMAGE.exists():
|
||||
SIMPLE_HELP_IMAGE.unlink()
|
||||
|
||||
_matcher = on_alconna(
|
||||
Alconna(
|
||||
"功能",
|
||||
@ -66,11 +61,13 @@ async def _(
|
||||
session: EventSession,
|
||||
is_superuser: Query[bool] = AlconnaQuery("superuser.value", False),
|
||||
):
|
||||
if not session.id1:
|
||||
await MessageUtils.build_message("用户id为空...").finish()
|
||||
_is_superuser = is_superuser.result if is_superuser.available else False
|
||||
if name.available:
|
||||
if _is_superuser and session.id1 not in bot.config.superusers:
|
||||
_is_superuser = False
|
||||
if result := await get_plugin_help(name.result, _is_superuser):
|
||||
if result := await get_plugin_help(session.id1, name.result, _is_superuser):
|
||||
await MessageUtils.build_message(result).send(reply_to=True)
|
||||
else:
|
||||
await MessageUtils.build_message("没有此功能的帮助信息...").send(
|
||||
@ -80,11 +77,9 @@ async def _(
|
||||
elif gid := session.id3 or session.id2:
|
||||
_image_path = GROUP_HELP_PATH / f"{gid}.png"
|
||||
if not _image_path.exists():
|
||||
await create_help_img(bot.self_id, gid)
|
||||
await create_help_img(bot.self_id, gid, session.platform)
|
||||
await MessageUtils.build_message(_image_path).finish()
|
||||
else:
|
||||
if not SIMPLE_HELP_IMAGE.exists():
|
||||
if SIMPLE_HELP_IMAGE.exists():
|
||||
SIMPLE_HELP_IMAGE.unlink()
|
||||
await create_help_img(bot.self_id, None)
|
||||
await create_help_img(bot.self_id, None, session.platform)
|
||||
await MessageUtils.build_message(SIMPLE_HELP_IMAGE).finish()
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
from pydantic import BaseModel
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH
|
||||
|
||||
GROUP_HELP_PATH = DATA_PATH / "group_help"
|
||||
GROUP_HELP_PATH.mkdir(exist_ok=True, parents=True)
|
||||
for f in GROUP_HELP_PATH.iterdir():
|
||||
f.unlink()
|
||||
|
||||
class Item(BaseModel):
|
||||
plugin_name: str
|
||||
sta: int
|
||||
SIMPLE_HELP_IMAGE = IMAGE_PATH / "SIMPLE_HELP.png"
|
||||
if SIMPLE_HELP_IMAGE.exists():
|
||||
SIMPLE_HELP_IMAGE.unlink()
|
||||
|
||||
|
||||
class PluginList(BaseModel):
|
||||
plugin_type: str
|
||||
icon: str
|
||||
logo: str
|
||||
items: list[Item]
|
||||
base_config = Config.get("help")
|
||||
|
||||
@ -1,39 +1,82 @@
|
||||
import nonebot
|
||||
|
||||
from zhenxun.utils.enum import PluginType
|
||||
from zhenxun.models.level_user import LevelUser
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.configs.path_config import IMAGE_PATH
|
||||
from zhenxun.utils.image_utils import BuildImage, ImageTemplate
|
||||
|
||||
from ._utils import HelpImageBuild
|
||||
from .html_help import build_html_image
|
||||
from .normal_help import build_normal_image
|
||||
from .zhenxun_help import build_zhenxun_image
|
||||
from ._config import GROUP_HELP_PATH, SIMPLE_HELP_IMAGE, base_config
|
||||
|
||||
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
background = IMAGE_PATH / "background" / "0.png"
|
||||
|
||||
|
||||
async def create_help_img(bot_id: str, group_id: str | None):
|
||||
driver = nonebot.get_driver()
|
||||
|
||||
|
||||
async def create_help_img(bot_id: str, group_id: str | None, platform: str):
|
||||
"""生成帮助图片
|
||||
|
||||
参数:
|
||||
bot_id: bot id
|
||||
group_id: 群号
|
||||
platform: 平台
|
||||
"""
|
||||
await HelpImageBuild().build_image(bot_id, group_id)
|
||||
help_type: str = base_config.get("type")
|
||||
if help_type.lower() == "html":
|
||||
result = BuildImage.open(await build_html_image(group_id))
|
||||
elif help_type.lower() == "zhenxun":
|
||||
result = BuildImage.open(await build_zhenxun_image(bot_id, group_id, platform))
|
||||
else:
|
||||
result = await build_normal_image(group_id)
|
||||
if group_id:
|
||||
await result.save(GROUP_HELP_PATH / f"{group_id}.png")
|
||||
else:
|
||||
await result.save(SIMPLE_HELP_IMAGE)
|
||||
|
||||
|
||||
async def get_plugin_help(name: str, is_superuser: bool) -> str | BuildImage:
|
||||
async def get_user_allow_help(user_id: str) -> list[PluginType]:
|
||||
"""获取用户可访问插件类型列表
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
|
||||
返回:
|
||||
list[PluginType]: 插件类型列表
|
||||
"""
|
||||
type_list = [PluginType.NORMAL, PluginType.DEPENDANT]
|
||||
for level in await LevelUser.filter(user_id=user_id).values_list(
|
||||
"user_level", flat=True
|
||||
):
|
||||
if level > 0: # type: ignore
|
||||
type_list.extend((PluginType.ADMIN, PluginType.SUPER_AND_ADMIN))
|
||||
break
|
||||
if user_id in driver.config.superusers:
|
||||
type_list.append(PluginType.SUPERUSER)
|
||||
return type_list
|
||||
|
||||
|
||||
async def get_plugin_help(
|
||||
user_id: str, name: str, is_superuser: bool
|
||||
) -> str | BuildImage:
|
||||
"""获取功能的帮助信息
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
name: 插件名称或id
|
||||
is_superuser: 是否为超级用户
|
||||
"""
|
||||
type_list = await get_user_allow_help(user_id)
|
||||
if name.isdigit():
|
||||
plugin = await PluginInfo.get_or_none(id=int(name))
|
||||
plugin = await PluginInfo.get_or_none(id=int(name), plugin_type__in=type_list)
|
||||
else:
|
||||
plugin = await PluginInfo.get_or_none(
|
||||
name__iexact=name, load_status=True, plugin_type__not=PluginType.PARENT
|
||||
name__iexact=name, load_status=True, plugin_type__in=type_list
|
||||
)
|
||||
if plugin:
|
||||
_plugin = nonebot.get_plugin_by_module_name(plugin.module_path)
|
||||
|
||||
@ -1,347 +1,45 @@
|
||||
import os
|
||||
import random
|
||||
from collections.abc import Callable
|
||||
|
||||
import aiofiles
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
|
||||
from zhenxun.configs.config import Config
|
||||
from zhenxun.utils.enum import PluginType
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.utils.enum import BlockType, PluginType
|
||||
from zhenxun.models.group_console import GroupConsole
|
||||
from zhenxun.builtin_plugins.sign_in.utils import AVA_URL
|
||||
from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH, TEMPLATE_PATH
|
||||
from zhenxun.utils.image_utils import BuildImage, group_image, build_sort_image
|
||||
|
||||
from ._config import Item
|
||||
|
||||
GROUP_HELP_PATH = DATA_PATH / "group_help"
|
||||
GROUP_HELP_PATH.mkdir(exist_ok=True, parents=True)
|
||||
for f in os.listdir(GROUP_HELP_PATH):
|
||||
group_help_image = GROUP_HELP_PATH / f
|
||||
group_help_image.unlink()
|
||||
|
||||
BACKGROUND_PATH = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
LOGO_PATH = TEMPLATE_PATH / "menu" / "res" / "logo"
|
||||
|
||||
|
||||
class HelpImageBuild:
|
||||
def __init__(self):
|
||||
self._data: list[PluginInfo] = []
|
||||
self._sort_data: dict[str, list[PluginInfo]] = {}
|
||||
self._image_list = []
|
||||
self.icon2str = {
|
||||
"normal": "fa fa-cog",
|
||||
"原神相关": "fa fa-circle-o",
|
||||
"常规插件": "fa fa-cubes",
|
||||
"联系管理员": "fa fa-envelope-o",
|
||||
"抽卡相关": "fa fa-credit-card-alt",
|
||||
"来点好康的": "fa fa-picture-o",
|
||||
"数据统计": "fa fa-bar-chart",
|
||||
"一些工具": "fa fa-shopping-cart",
|
||||
"商店": "fa fa-shopping-cart",
|
||||
"其它": "fa fa-tags",
|
||||
"群内小游戏": "fa fa-gamepad",
|
||||
}
|
||||
async def sort_type() -> dict[str, list[PluginInfo]]:
|
||||
"""
|
||||
对插件按照菜单类型分类
|
||||
"""
|
||||
data = await PluginInfo.filter(
|
||||
menu_type__not="",
|
||||
load_status=True,
|
||||
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT],
|
||||
)
|
||||
sort_data = {}
|
||||
for plugin in data:
|
||||
menu_type = plugin.menu_type or "normal"
|
||||
if menu_type == "normal":
|
||||
menu_type = "功能"
|
||||
if not sort_data.get(menu_type):
|
||||
sort_data[menu_type] = []
|
||||
sort_data[menu_type].append(plugin)
|
||||
return sort_data
|
||||
|
||||
async def sort_type(self):
|
||||
"""
|
||||
对插件按照菜单类型分类
|
||||
"""
|
||||
if not self._data:
|
||||
self._data = await PluginInfo.filter(
|
||||
menu_type__not="",
|
||||
load_status=True,
|
||||
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT],
|
||||
)
|
||||
if not self._sort_data:
|
||||
for plugin in self._data:
|
||||
menu_type = plugin.menu_type or "normal"
|
||||
if menu_type == "normal":
|
||||
menu_type = "功能"
|
||||
if not self._sort_data.get(menu_type):
|
||||
self._sort_data[menu_type] = []
|
||||
self._sort_data[menu_type].append(plugin)
|
||||
|
||||
async def build_image(self, bot_id: str, group_id: str | None):
|
||||
if group_id:
|
||||
help_image = GROUP_HELP_PATH / f"{group_id}.png"
|
||||
else:
|
||||
help_image = IMAGE_PATH / "SIMPLE_HELP.png"
|
||||
build_type = Config.get_config("help", "TYPE")
|
||||
if build_type == "HTML":
|
||||
byt = await self.build_html_image(group_id)
|
||||
async with aiofiles.open(help_image, "wb") as f:
|
||||
await f.write(byt)
|
||||
elif build_type == "zhenxun":
|
||||
byt = await self.build_ss_image(bot_id, group_id)
|
||||
async with aiofiles.open(help_image, "wb") as f:
|
||||
await f.write(byt)
|
||||
else:
|
||||
img = await self.build_pil_image(group_id)
|
||||
await img.save(help_image)
|
||||
async def classify_plugin(group_id: str | None, handle: Callable) -> dict[str, list]:
|
||||
"""对插件进行分类并判断状态
|
||||
|
||||
async def build_ss_image(self, bot_id: str, group_id: str | None) -> bytes:
|
||||
"""构造ss帮助图片
|
||||
参数:
|
||||
group_id: 群组id
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
await self.sort_type()
|
||||
classify = {}
|
||||
for menu in self._sort_data:
|
||||
self._sort_data[menu].sort(key=lambda k: len(k.name))
|
||||
for menu in self._sort_data:
|
||||
for plugin in self._sort_data[menu]:
|
||||
if not plugin.status:
|
||||
if group_id and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.GROUP,
|
||||
]:
|
||||
plugin.name = f"{plugin.name}(不可用)"
|
||||
if not group_id and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.PRIVATE,
|
||||
]:
|
||||
plugin.name = f"{plugin.name}(不可用)"
|
||||
if not classify.get(menu):
|
||||
classify[menu] = []
|
||||
classify[menu].append(
|
||||
Item(plugin_name=f"{plugin.id}-{plugin.name}", sta=0)
|
||||
)
|
||||
max_len = 0
|
||||
flag_index = -1
|
||||
max_data = {}
|
||||
plugin_list = []
|
||||
for index, plu in enumerate(classify.keys()):
|
||||
data = {
|
||||
"name": "主要功能" if plu in ["normal", "功能"] else plu,
|
||||
"items": classify[plu],
|
||||
}
|
||||
if len(classify[plu]) > max_len:
|
||||
max_len = len(classify[plu])
|
||||
flag_index = index
|
||||
max_data = data
|
||||
plugin_list.append(data)
|
||||
del plugin_list[flag_index]
|
||||
# plugin_list.insert(0, max_data)
|
||||
_data = []
|
||||
_left = 30
|
||||
_pu1 = []
|
||||
_pu2 = []
|
||||
for i in range(len(max_data["items"])):
|
||||
if i % 2:
|
||||
_pu1.append(max_data["items"][i])
|
||||
else:
|
||||
_pu2.append(max_data["items"][i])
|
||||
_plugins = [(30, 50, _pu1), (0, 50, _pu2)]
|
||||
_data.append(
|
||||
{
|
||||
"name": max_data["name"],
|
||||
"items": [(30, 50, _pu1), (0, 50, _pu2)],
|
||||
"width": 100,
|
||||
}
|
||||
)
|
||||
for plugin in plugin_list:
|
||||
_plugins = []
|
||||
width = 50
|
||||
if len(plugin["items"]) // 2 > 6:
|
||||
width = 100
|
||||
_pu1 = []
|
||||
_pu2 = []
|
||||
for i in range(len(plugin["items"])):
|
||||
if i % 2:
|
||||
_pu1.append(plugin["items"][i])
|
||||
else:
|
||||
_pu2.append(plugin["items"][i])
|
||||
_plugins = [(30, 50, _pu1), (0, 50, _pu2)]
|
||||
else:
|
||||
_plugins = [(_left, 100, plugin["items"])]
|
||||
_left = 15 if _left == 30 else 30
|
||||
_data.append({"name": plugin["name"], "items": _plugins, "width": width})
|
||||
return await template_to_pic(
|
||||
template_path=str((TEMPLATE_PATH / "ss_menu").absolute()),
|
||||
template_name="main.html",
|
||||
templates={"data": {"plugin_list": _data, "ava": AVA_URL.format(bot_id)}},
|
||||
pages={
|
||||
"viewport": {"width": 637, "height": 453},
|
||||
"base_url": f"file://{TEMPLATE_PATH}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
|
||||
async def build_html_image(self, group_id: str | None) -> bytes:
|
||||
"""构造HTML帮助图片
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
await self.sort_type()
|
||||
classify = {}
|
||||
for menu in self._sort_data:
|
||||
for plugin in self._sort_data[menu]:
|
||||
sta = 0
|
||||
if not plugin.status:
|
||||
if group_id and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.GROUP,
|
||||
]:
|
||||
sta = 2
|
||||
if not group_id and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.PRIVATE,
|
||||
]:
|
||||
sta = 2
|
||||
if group_id and (
|
||||
group := await GroupConsole.get_or_none(group_id=group_id)
|
||||
):
|
||||
if f"{plugin.module}:super," in group.block_plugin:
|
||||
sta = 2
|
||||
if f"{plugin.module}," in group.block_plugin:
|
||||
sta = 1
|
||||
if classify.get(menu):
|
||||
classify[menu].append(Item(plugin_name=plugin.name, sta=sta))
|
||||
else:
|
||||
classify[menu] = [Item(plugin_name=plugin.name, sta=sta)]
|
||||
max_len = 0
|
||||
flag_index = -1
|
||||
max_data = None
|
||||
plugin_list = []
|
||||
for index, plu in enumerate(classify.keys()):
|
||||
if plu in self.icon2str.keys():
|
||||
icon = self.icon2str[plu]
|
||||
else:
|
||||
icon = "fa fa-pencil-square-o"
|
||||
logo = LOGO_PATH / random.choice(os.listdir(LOGO_PATH))
|
||||
data = {
|
||||
"name": plu if plu != "normal" else "功能",
|
||||
"items": classify[plu],
|
||||
"icon": icon,
|
||||
"logo": str(logo.absolute()),
|
||||
}
|
||||
if len(classify[plu]) > max_len:
|
||||
max_len = len(classify[plu])
|
||||
flag_index = index
|
||||
max_data = data
|
||||
plugin_list.append(data)
|
||||
del plugin_list[flag_index]
|
||||
plugin_list.insert(0, max_data)
|
||||
return await template_to_pic(
|
||||
template_path=str((TEMPLATE_PATH / "menu").absolute()),
|
||||
template_name="zhenxun_menu.html",
|
||||
templates={"plugin_list": plugin_list},
|
||||
pages={
|
||||
"viewport": {"width": 1903, "height": 975},
|
||||
"base_url": f"file://{TEMPLATE_PATH}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
|
||||
async def build_pil_image(self, group_id: str | None) -> BuildImage:
|
||||
"""构造PIL帮助图片
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
self._image_list = []
|
||||
await self.sort_type()
|
||||
font_size = 24
|
||||
build_type = Config.get_config("help", "TYPE")
|
||||
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
|
||||
for idx, menu_type in enumerate(self._sort_data.keys()):
|
||||
plugin_list = self._sort_data[menu_type]
|
||||
wh_list = [
|
||||
BuildImage.get_text_size(f"{x.id}.{x.name}", font) for x in plugin_list
|
||||
]
|
||||
wh_list.append(BuildImage.get_text_size(menu_type, font))
|
||||
# sum_height = sum([x[1] for x in wh_list])
|
||||
if build_type == "VV":
|
||||
sum_height = 50 * len(plugin_list) + 10
|
||||
else:
|
||||
sum_height = (font_size + 6) * len(plugin_list) + 10
|
||||
max_width = max(x[0] for x in wh_list) + 30
|
||||
bk = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height + 50,
|
||||
font_size=30,
|
||||
color="#a7d1fc",
|
||||
font="CJGaoDeGuo.otf",
|
||||
)
|
||||
title_size = bk.getsize(menu_type)
|
||||
max_width = max_width if max_width > title_size[0] else title_size[0]
|
||||
B = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height,
|
||||
font_size=font_size,
|
||||
color="black" if idx % 2 else "white",
|
||||
)
|
||||
curr_h = 10
|
||||
group = await GroupConsole.get_or_none(group_id=group_id)
|
||||
for i, 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:
|
||||
text_color = (252, 75, 13)
|
||||
pos = None
|
||||
# 禁用状态划线
|
||||
if plugin.block_type in [BlockType.ALL, BlockType.GROUP] or (
|
||||
group and f"super:{plugin.module}," in group.block_plugin
|
||||
):
|
||||
w = curr_h + int(B.getsize(plugin.name)[1] / 2) + 2
|
||||
pos = (
|
||||
7,
|
||||
w,
|
||||
B.getsize(plugin.name)[0] + 35,
|
||||
w,
|
||||
)
|
||||
if build_type == "VV":
|
||||
name_image = await self.build_name_image( # type: ignore
|
||||
max_width,
|
||||
plugin.name,
|
||||
"white" if idx % 2 else "black",
|
||||
text_color,
|
||||
pos,
|
||||
)
|
||||
await B.paste(name_image, (0, curr_h), center_type="width")
|
||||
curr_h += name_image.h + 5
|
||||
else:
|
||||
await B.text((10, curr_h), f"{plugin.id}.{plugin.name}", text_color)
|
||||
if pos:
|
||||
await B.line(pos, (236, 66, 7), 3)
|
||||
curr_h += font_size + 5
|
||||
await bk.text((0, 14), menu_type, center_type="width")
|
||||
await bk.paste(B, (0, 50))
|
||||
await bk.transparent(2)
|
||||
self._image_list.append(bk)
|
||||
image_group, h = group_image(self._image_list)
|
||||
|
||||
async def _a(image: BuildImage):
|
||||
await image.filter("GaussianBlur", 5)
|
||||
|
||||
B = await build_sort_image(
|
||||
image_group,
|
||||
h,
|
||||
background_path=BACKGROUND_PATH,
|
||||
background_handle=_a,
|
||||
)
|
||||
w = 10
|
||||
h = 10
|
||||
for msg in [
|
||||
"目前支持的功能列表:",
|
||||
"可以通过 ‘帮助 [功能名称或功能Id]’ 来获取对应功能的使用方法",
|
||||
]:
|
||||
text = await BuildImage.build_text_image(msg, "HYWenHei-85W.ttf", 24)
|
||||
await B.paste(text, (w, h))
|
||||
h += 50
|
||||
if msg == "目前支持的功能列表:":
|
||||
w += 50
|
||||
text = await BuildImage.build_text_image(
|
||||
"注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
"HYWenHei-85W.ttf",
|
||||
24,
|
||||
(231, 74, 57),
|
||||
)
|
||||
await B.paste(
|
||||
text,
|
||||
(300, 10),
|
||||
)
|
||||
return B
|
||||
返回:
|
||||
dict[str, list[Item]]: 分类插件数据
|
||||
"""
|
||||
sort_data = await sort_type()
|
||||
classify: dict[str, list] = {}
|
||||
group = await GroupConsole.get_or_none(group_id=group_id) if group_id else None
|
||||
for menu, value in sort_data.items():
|
||||
for plugin in value:
|
||||
if not classify.get(menu):
|
||||
classify[menu] = []
|
||||
classify[menu].append(handle(plugin, group))
|
||||
return classify
|
||||
|
||||
136
zhenxun/builtin_plugins/help/html_help.py
Normal file
136
zhenxun/builtin_plugins/help/html_help.py
Normal file
@ -0,0 +1,136 @@
|
||||
import os
|
||||
import random
|
||||
|
||||
from pydantic import BaseModel
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
|
||||
from zhenxun.utils.enum import BlockType
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.configs.path_config import TEMPLATE_PATH
|
||||
from zhenxun.models.group_console import GroupConsole
|
||||
|
||||
from ._utils import classify_plugin
|
||||
|
||||
LOGO_PATH = TEMPLATE_PATH / "menu" / "res" / "logo"
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
plugin_name: str
|
||||
"""插件名称"""
|
||||
sta: int
|
||||
"""插件状态"""
|
||||
|
||||
|
||||
class PluginList(BaseModel):
|
||||
plugin_type: str
|
||||
"""菜单名称"""
|
||||
icon: str
|
||||
"""图标"""
|
||||
logo: str
|
||||
"""logo"""
|
||||
items: list[Item]
|
||||
"""插件列表"""
|
||||
|
||||
|
||||
ICON2STR = {
|
||||
"normal": "fa fa-cog",
|
||||
"原神相关": "fa fa-circle-o",
|
||||
"常规插件": "fa fa-cubes",
|
||||
"联系管理员": "fa fa-envelope-o",
|
||||
"抽卡相关": "fa fa-credit-card-alt",
|
||||
"来点好康的": "fa fa-picture-o",
|
||||
"数据统计": "fa fa-bar-chart",
|
||||
"一些工具": "fa fa-shopping-cart",
|
||||
"商店": "fa fa-shopping-cart",
|
||||
"其它": "fa fa-tags",
|
||||
"群内小游戏": "fa fa-gamepad",
|
||||
}
|
||||
|
||||
|
||||
def __handle_item(plugin: PluginInfo, group: GroupConsole | None) -> Item:
|
||||
"""构造Item
|
||||
|
||||
参数:
|
||||
plugin: PluginInfo
|
||||
group: 群组
|
||||
|
||||
返回:
|
||||
Item: Item
|
||||
"""
|
||||
sta = 0
|
||||
if not plugin.status:
|
||||
if group and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.GROUP,
|
||||
]:
|
||||
sta = 2
|
||||
if not group and plugin.block_type in [
|
||||
BlockType.ALL,
|
||||
BlockType.PRIVATE,
|
||||
]:
|
||||
sta = 2
|
||||
if group:
|
||||
if f"{plugin.module}:super," in group.block_plugin:
|
||||
sta = 2
|
||||
if f"{plugin.module}," in group.block_plugin:
|
||||
sta = 1
|
||||
return Item(plugin_name=plugin.name, sta=sta)
|
||||
|
||||
|
||||
def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
|
||||
"""构建前端插件数据
|
||||
|
||||
参数:
|
||||
classify: 插件数据
|
||||
|
||||
返回:
|
||||
list[dict[str, str]]: 前端插件数据
|
||||
"""
|
||||
lengths = [len(classify[c]) for c in classify]
|
||||
index = lengths.index(max(lengths))
|
||||
menu_key = list(classify.keys())[index]
|
||||
max_data = classify[menu_key]
|
||||
del classify[menu_key]
|
||||
plugin_list = []
|
||||
for menu_type in classify:
|
||||
icon = "fa fa-pencil-square-o"
|
||||
if menu_type in ICON2STR.keys():
|
||||
icon = ICON2STR[menu_type]
|
||||
logo = LOGO_PATH / random.choice(os.listdir(LOGO_PATH))
|
||||
data = {
|
||||
"name": menu_type if menu_type != "normal" else "功能",
|
||||
"items": classify[menu_type],
|
||||
"icon": icon,
|
||||
"logo": str(logo.absolute()),
|
||||
}
|
||||
plugin_list.append(data)
|
||||
plugin_list.insert(
|
||||
0,
|
||||
{
|
||||
"name": menu_key if menu_key != "normal" else "功能",
|
||||
"items": max_data,
|
||||
"icon": "fa fa-pencil-square-o",
|
||||
"logo": str((LOGO_PATH / random.choice(os.listdir(LOGO_PATH))).absolute()),
|
||||
},
|
||||
)
|
||||
return plugin_list
|
||||
|
||||
|
||||
async def build_html_image(group_id: str | None) -> bytes:
|
||||
"""构造HTML帮助图片
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
classify = await classify_plugin(group_id, __handle_item)
|
||||
plugin_list = build_plugin_data(classify)
|
||||
return await template_to_pic(
|
||||
template_path=str((TEMPLATE_PATH / "menu").absolute()),
|
||||
template_name="zhenxun_menu.html",
|
||||
templates={"plugin_list": plugin_list},
|
||||
pages={
|
||||
"viewport": {"width": 1903, "height": 975},
|
||||
"base_url": f"file://{TEMPLATE_PATH}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
99
zhenxun/builtin_plugins/help/normal_help.py
Normal file
99
zhenxun/builtin_plugins/help/normal_help.py
Normal file
@ -0,0 +1,99 @@
|
||||
from zhenxun.utils.enum import BlockType
|
||||
from zhenxun.utils._build_image import BuildImage
|
||||
from zhenxun.configs.path_config import IMAGE_PATH
|
||||
from zhenxun.models.group_console import GroupConsole
|
||||
from zhenxun.utils.image_utils import group_image, build_sort_image
|
||||
|
||||
from ._utils import sort_type
|
||||
|
||||
BACKGROUND_PATH = IMAGE_PATH / "background" / "help" / "simple_help"
|
||||
|
||||
|
||||
async def build_normal_image(group_id: str | None) -> BuildImage:
|
||||
"""构造PIL帮助图片
|
||||
|
||||
参数:
|
||||
group_id: 群号
|
||||
"""
|
||||
image_list = []
|
||||
font_size = 24
|
||||
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
|
||||
sort_data = await sort_type()
|
||||
for idx, menu_type in enumerate(sort_data):
|
||||
plugin_list = sort_data[menu_type]
|
||||
"""拿到最大宽度和结算高度"""
|
||||
wh_list = [
|
||||
BuildImage.get_text_size(f"{x.id}.{x.name}", font) for x in plugin_list
|
||||
]
|
||||
wh_list.append(BuildImage.get_text_size(menu_type, font))
|
||||
sum_height = (font_size + 6) * len(plugin_list) + 10
|
||||
max_width = max(x[0] for x in wh_list) + 30
|
||||
bk = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height + 50,
|
||||
font_size=30,
|
||||
color="#a7d1fc",
|
||||
font="CJGaoDeGuo.otf",
|
||||
)
|
||||
title_size = bk.getsize(menu_type)
|
||||
max_width = max_width if max_width > title_size[0] else title_size[0]
|
||||
row = BuildImage(
|
||||
max_width + 40,
|
||||
sum_height,
|
||||
font_size=font_size,
|
||||
color="black" if idx % 2 else "white",
|
||||
)
|
||||
curr_h = 10
|
||||
group = await GroupConsole.get_or_none(group_id=group_id)
|
||||
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:
|
||||
text_color = (252, 75, 13)
|
||||
pos = None
|
||||
# 禁用状态划线
|
||||
if plugin.block_type in [BlockType.ALL, BlockType.GROUP] or (
|
||||
group and f"super:{plugin.module}," in group.block_plugin
|
||||
):
|
||||
w = curr_h + int(row.getsize(plugin.name)[1] / 2) + 2
|
||||
line_width = row.getsize(plugin.name)[0] + 35
|
||||
pos = (7, w, line_width, w)
|
||||
await row.text((10, curr_h), f"{plugin.id}.{plugin.name}", text_color)
|
||||
if pos:
|
||||
await row.line(pos, (236, 66, 7), 3)
|
||||
curr_h += font_size + 5
|
||||
await bk.text((0, 14), menu_type, center_type="width")
|
||||
await bk.paste(row, (0, 50))
|
||||
await bk.transparent(2)
|
||||
image_list.append(bk)
|
||||
image_group, h = group_image(image_list)
|
||||
|
||||
async def _a(image: BuildImage):
|
||||
await image.filter("GaussianBlur", 5)
|
||||
|
||||
result = await build_sort_image(
|
||||
image_group,
|
||||
h,
|
||||
background_path=BACKGROUND_PATH,
|
||||
background_handle=_a,
|
||||
)
|
||||
width, height = 10, 10
|
||||
for s in [
|
||||
"目前支持的功能列表:",
|
||||
"可以通过 ‘帮助 [功能名称或功能Id]’ 来获取对应功能的使用方法",
|
||||
]:
|
||||
text = await BuildImage.build_text_image(s, "HYWenHei-85W.ttf", 24)
|
||||
await result.paste(text, (width, height))
|
||||
height += 50
|
||||
if s == "目前支持的功能列表:":
|
||||
width += 50
|
||||
text = await BuildImage.build_text_image(
|
||||
"注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
"HYWenHei-85W.ttf",
|
||||
24,
|
||||
(231, 74, 57),
|
||||
)
|
||||
await result.paste(
|
||||
text,
|
||||
(300, 10),
|
||||
)
|
||||
return result
|
||||
143
zhenxun/builtin_plugins/help/zhenxun_help.py
Normal file
143
zhenxun/builtin_plugins/help/zhenxun_help.py
Normal file
@ -0,0 +1,143 @@
|
||||
from pydantic import BaseModel
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
|
||||
from zhenxun.utils.enum import BlockType
|
||||
from zhenxun.utils.platform import PlatformUtils
|
||||
from zhenxun.models.plugin_info import PluginInfo
|
||||
from zhenxun.configs.path_config import TEMPLATE_PATH
|
||||
from zhenxun.models.group_console import GroupConsole
|
||||
|
||||
from ._utils import classify_plugin
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
plugin_name: str
|
||||
"""插件名称"""
|
||||
|
||||
|
||||
def __handle_item(plugin: PluginInfo, group: GroupConsole | None):
|
||||
"""构造Item
|
||||
|
||||
参数:
|
||||
plugin: PluginInfo
|
||||
group: 群组
|
||||
|
||||
返回:
|
||||
Item: Item
|
||||
"""
|
||||
if not plugin.status:
|
||||
if plugin.block_type == BlockType.ALL:
|
||||
plugin.name = f"{plugin.name}(不可用)"
|
||||
elif (
|
||||
group
|
||||
and plugin.block_type == BlockType.GROUP
|
||||
or not group
|
||||
and plugin.block_type == BlockType.PRIVATE
|
||||
):
|
||||
plugin.name = f"{plugin.name}(不可用)"
|
||||
return Item(plugin_name=f"{plugin.id}-{plugin.name}")
|
||||
|
||||
|
||||
def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
|
||||
"""构建前端插件数据
|
||||
|
||||
参数:
|
||||
classify: 插件数据
|
||||
|
||||
返回:
|
||||
list[dict[str, str]]: 前端插件数据
|
||||
"""
|
||||
|
||||
lengths = [len(classify[c]) for c in classify]
|
||||
index = lengths.index(max(lengths))
|
||||
menu_key = list(classify.keys())[index]
|
||||
max_data = classify[menu_key]
|
||||
del classify[menu_key]
|
||||
plugin_list = [
|
||||
{
|
||||
"name": "主要功能" if menu in ["normal", "功能"] else menu,
|
||||
"items": value,
|
||||
}
|
||||
for menu, value in classify.items()
|
||||
]
|
||||
plugin_list = build_line_data(plugin_list)
|
||||
plugin_list.insert(0, build_plugin_line(menu_key, max_data, 30, 100))
|
||||
return plugin_list
|
||||
|
||||
|
||||
def build_plugin_line(
|
||||
name: str, items: list, left: int, width: int | None = None
|
||||
) -> dict:
|
||||
"""构造插件行数据
|
||||
|
||||
参数:
|
||||
name: 菜单名称
|
||||
items: 插件名称列表
|
||||
left: 左边距
|
||||
width: 总插件长度.
|
||||
|
||||
返回:
|
||||
dict: 插件数据
|
||||
"""
|
||||
_plugins = []
|
||||
width = width or 50
|
||||
if len(items) // 2 > 6:
|
||||
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 = []
|
||||
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
|
||||
|
||||
|
||||
async def build_zhenxun_image(
|
||||
bot_id: str, group_id: str | None, platform: str
|
||||
) -> bytes:
|
||||
"""构造真寻帮助图片
|
||||
|
||||
参数:
|
||||
bot_id: bot_id
|
||||
group_id: 群号
|
||||
platform: 平台
|
||||
"""
|
||||
classify = await classify_plugin(group_id, __handle_item)
|
||||
plugin_list = build_plugin_data(classify)
|
||||
return await template_to_pic(
|
||||
template_path=str((TEMPLATE_PATH / "ss_menu").absolute()),
|
||||
template_name="main.html",
|
||||
templates={
|
||||
"data": {
|
||||
"plugin_list": plugin_list,
|
||||
"ava": PlatformUtils.get_user_avatar_url(bot_id, platform),
|
||||
}
|
||||
},
|
||||
pages={
|
||||
"viewport": {"width": 637, "height": 453},
|
||||
"base_url": f"file://{TEMPLATE_PATH}",
|
||||
},
|
||||
wait=2,
|
||||
)
|
||||
@ -266,6 +266,18 @@ class PlatformUtils:
|
||||
)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_user_avatar_url(cls, user_id: str, platform: str) -> str | None:
|
||||
"""快捷获取用户头像url
|
||||
|
||||
参数:
|
||||
user_id: 用户id
|
||||
platform: 平台
|
||||
"""
|
||||
if platform == "qq":
|
||||
return f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_group_avatar(cls, gid: str, platform: str) -> bytes | None:
|
||||
"""快捷获取用群头像
|
||||
|
||||
Loading…
Reference in New Issue
Block a user