新增超级用户与管理员帮助模板 (#1655)

This commit is contained in:
HibiKier 2024-09-27 16:59:41 +08:00 committed by GitHub
parent 7890cc577f
commit 28b61e57cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 796 additions and 330 deletions

View File

@ -0,0 +1,116 @@
@font-face {
font-family: fzrzFont;
/* 导入的字体文件 */
src: url("./res/font/fzrzExtraBold.ttf");
}
@font-face {
font-family: syhtFont;
/* 导入的字体文件 */
src: url("./res/font/syht.otf");
}
@font-face {
font-family: systFont;
/* 导入的字体文件 */
src: url("./res/font/syst.otf");
}
body {
position: absolute;
left: -8px;
top: -8px;
}
.wrapper{
width: 1400px;
position: relative;
background-image: url('res/img/bk.jpg');
background-size: cover;
font-family: 'cr105Font';
padding: 20px;
}
.title {
font-size: 60px;
font-family: 'fzrzFont';
/* margin-left: 40px; */
/* color: #F67186; */
background: linear-gradient(to right, #F67186, #F7889C);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-align: center;
}
.main {
background-image: url('res/img/main.png');
background-size: 100% 100%;
/* background-size: cover; */
height: 100%;
width: 1370px;
position: relative;
padding: 20px;
/* box-shadow: 5px 5px 10px 0 rgba(0,0,0,0.5); */
}
.items-border {
display: flex;
flex-wrap: wrap;
padding-left: 18px;
}
.items {
border: #F67186 2px solid;
border-radius: 20px;
width: 675px;
padding: 30px;
max-width: 600px;
}
.item-title {
background-image: url('res/img/title.png');
background-size: cover;
background-repeat: no-repeat;
height: 55px;
width: 350px;
font-family: 'fzrzFont';
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 35px;
border-radius: 16px;
letter-spacing:4px;
}
.usage-title {
font-size: 30px;
position: absolute;
top: -30px;
}
.item-des {
color: #F78094;
border-radius: 20px;
font-family: 'fzrzFont';
font-size: 30px;
padding: 10px;
}
.item-usage {
color: #F78094;
border-radius: 20px;
font-family: 'fzrzFont';
font-size: 20px;
border: #F67186 5px dotted;
padding: 60px 10px 10px 10px;
position: relative;
}

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test</title>
<!-- <link rel="stylesheet" href="./res/font-awesome/css/font-awesome.min.css"> -->
<link rel="stylesheet" href="main.css">
</head>
<body>
<div class="wrapper">
<div class="main">
<div class="title">
{{data.nickname}}的{{data.help_name}}帮助
</div>
<div class="items-border">
{% for plugin in data['plugin_list'] %}
<div class="items">
<div class="item-title">
{{plugin.name}}
</div>
<div class="item-des">
简介: {{plugin.description}}
</div>
<div class="item-usage">
<p class="usage-title">用法:</p>
{{plugin.usage}}
</div>
</div>
{% endfor %}
</div>
<!-- <div class="items">
<div class="item-title">
刷屏禁言
</div>
<div class="item-des">
简介: 刷屏禁言相关操作
</div>
<div class="item-usage">
用法:<br>
指令:<br>
设置刷屏: 查看当前设置<br>
-c [count]: 检测最大次数<br>
-t [time]: 规定时间内<br>
-d [duration]: 禁言时长<br>
示例:<br>
设置刷屏 -c 10: 设置最大次数为10<br>
设置刷屏 -t 100 -d 20: 设置规定时间和禁言时长<br>
设置刷屏 -d 10: 设置禁言时长为10<br>
* 即 X 秒内发送同样消息 N 次,禁言 M 分钟 *
</div>
</div> -->
</div>
</div>
</body>
<script type="text/javascript" src="main.js">
</script>
</html>

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -1,160 +0,0 @@
import nonebot
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from nonebot_plugin_alconna.matcher import AlconnaMatcher
from nonebot_plugin_session import EventSession
from zhenxun.configs.path_config import IMAGE_PATH
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.models.task_info import TaskInfo
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.utils.exception import EmptyError
from zhenxun.utils.image_utils import (
BuildImage,
build_sort_image,
group_image,
text2image,
)
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.rules import admin_check, ensure_group
__plugin_meta__ = PluginMetadata(
name="群组管理员帮助",
description="管理员帮助列表",
usage="""
管理员帮助
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.ADMIN,
admin_level=1,
).dict(),
)
_matcher = on_alconna(
Alconna("管理员帮助"),
rule=admin_check(1) & ensure_group,
priority=5,
block=True,
)
ADMIN_HELP_IMAGE = IMAGE_PATH / "ADMIN_HELP.png"
if ADMIN_HELP_IMAGE.exists():
ADMIN_HELP_IMAGE.unlink()
async def build_help() -> BuildImage:
"""构造管理员帮助图片
异常:
EmptyError: 管理员帮助为空
返回:
BuildImage: 管理员帮助图片
"""
plugin_list = await PluginInfo.filter(
plugin_type__in=[PluginType.ADMIN, PluginType.SUPER_AND_ADMIN]
).all()
data_list = []
for plugin in plugin_list:
if _plugin := nonebot.get_plugin_by_module_name(plugin.module_path):
if _plugin.metadata:
data_list.append({"plugin": plugin, "metadata": _plugin.metadata})
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
image_list = []
for data in data_list:
plugin = data["plugin"]
metadata = data["metadata"]
try:
usage = None
description = None
if metadata.usage:
usage = await text2image(
metadata.usage,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
if metadata.description:
description = await text2image(
metadata.description,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
width = 0
height = 100
if usage:
width = usage.width
height += usage.height
if description and description.width > width:
width = description.width
height += description.height
font_width, font_height = BuildImage.get_text_size(
plugin.name + f"[{plugin.level}]", font
)
if font_width > width:
width = font_width
A = BuildImage(width + 30, height + 120, "#EAEDF2")
await A.text((15, 10), plugin.name + f"[{plugin.level}]")
await A.text((15, 70), "简介:")
if not description:
description = BuildImage(A.width - 30, 30, (255, 255, 255))
await description.circle_corner(10)
await A.paste(description, (15, 100))
if not usage:
usage = BuildImage(A.width - 30, 30, (255, 255, 255))
await usage.circle_corner(10)
await A.text((15, description.height + 115), "用法:")
await A.paste(usage, (15, description.height + 145))
await A.circle_corner(10)
image_list.append(A)
except Exception as e:
logger.warning(
f"获取群管理员插件 {plugin.module}: {plugin.name} 设置失败...",
"管理员帮助",
e=e,
)
if task_list := await TaskInfo.all():
task_str = "\n".join([task.name for task in task_list])
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
await task_image.circle_corner(10)
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
await A.text((25, 10), "被动技能")
await A.paste(task_image, (25, 50))
await A.circle_corner(10)
image_list.append(A)
if not image_list:
raise EmptyError()
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color=(255, 255, 255), padding_top=160)
text = await BuildImage.build_text_image(
"群管理员帮助",
size=40,
)
tip = await BuildImage.build_text_image(
"注: * 代表可有多个相同参数 ? 代表可省略该参数", size=25, font_color="red"
)
await A.paste(text, (50, 30))
await A.paste(tip, (50, 90))
await A.save(ADMIN_HELP_IMAGE)
return BuildImage(1, 1)
@_matcher.handle()
async def _(
session: EventSession,
arparma: Arparma,
):
if not ADMIN_HELP_IMAGE.exists():
try:
await build_help()
except EmptyError:
await MessageUtils.build_message("管理员帮助为空").finish(reply_to=True)
await MessageUtils.build_message(ADMIN_HELP_IMAGE).send()
logger.info("查看管理员帮助", arparma.header_result, session=session)

View File

@ -0,0 +1,63 @@
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.enum import PluginType
from zhenxun.utils.exception import EmptyError
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.rules import admin_check, ensure_group
from zhenxun.configs.utils import RegisterConfig, PluginExtraData
from .normal_help import build_help
from .config import ADMIN_HELP_IMAGE
from .html_help import build_html_help
__plugin_meta__ = PluginMetadata(
name="群组管理员帮助",
description="管理员帮助列表",
usage="""
管理员帮助
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.ADMIN,
admin_level=1,
configs=[
RegisterConfig(
key="type",
value="zhenxun",
help="管理员帮助样式normal, zhenxun",
default_value="zhenxun",
)
],
).dict(),
)
_matcher = on_alconna(
Alconna("管理员帮助"),
rule=admin_check(1) & ensure_group,
priority=5,
block=True,
)
@_matcher.handle()
async def _(
session: EventSession,
arparma: Arparma,
):
if not ADMIN_HELP_IMAGE.exists():
try:
if Config.get_config("admin_help", "type") == "zhenxun":
await build_html_help()
else:
await build_help()
except EmptyError:
await MessageUtils.build_message("当前管理员帮助为空...").finish(
reply_to=True
)
await MessageUtils.build_message(ADMIN_HELP_IMAGE).send()
logger.info("查看管理员帮助", arparma.header_result, session=session)

View File

@ -0,0 +1,23 @@
from pydantic import BaseModel
from nonebot.plugin import PluginMetadata
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.configs.path_config import IMAGE_PATH
ADMIN_HELP_IMAGE = IMAGE_PATH / "ADMIN_HELP.png"
if ADMIN_HELP_IMAGE.exists():
ADMIN_HELP_IMAGE.unlink()
class PluginData(BaseModel):
"""
插件信息
"""
plugin: PluginInfo
"""插件信息"""
metadata: PluginMetadata
"""元数据"""
class Config:
arbitrary_types_allowed = True

View File

@ -0,0 +1,55 @@
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.configs.config import BotConfig
from zhenxun.models.task_info import TaskInfo
from zhenxun.utils._build_image import BuildImage
from zhenxun.configs.path_config import TEMPLATE_PATH
from zhenxun.builtin_plugins.admin.admin_help.config import ADMIN_HELP_IMAGE
from .utils import get_plugins
async def get_task() -> dict[str, str] | None:
"""获取被动技能帮助"""
if task_list := await TaskInfo.all():
return {
"name": "被动技能",
"description": "控制群组中的被动技能状态",
"usage": "通过 开启/关闭群被动 来控制群被<br>----------<br>"
+ "<br>".join([task.name for task in task_list]),
}
return None
async def build_html_help():
"""构建帮助图片"""
plugins = await get_plugins()
plugin_list = [
{
"name": data.plugin.name,
"description": data.metadata.description.replace("\n", "<br>"),
"usage": data.metadata.usage.replace("\n", "<br>"),
}
for data in plugins
]
if task := await get_task():
plugin_list.append(task)
plugin_list.sort(key=lambda p: len(p["description"]) + len(p["usage"]))
pic = await template_to_pic(
template_path=str((TEMPLATE_PATH / "help").absolute()),
template_name="main.html",
templates={
"data": {
"plugin_list": plugin_list,
"nickname": BotConfig.self_nickname,
"help_name": "群管理员",
}
},
pages={
"viewport": {"width": 1024, "height": 1024},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,
)
result = await BuildImage.open(pic).resize(0.5)
await result.save(ADMIN_HELP_IMAGE)

View File

@ -0,0 +1,127 @@
from PIL.ImageFont import FreeTypeFont
from nonebot.plugin import PluginMetadata
from zhenxun.services.log import logger
from zhenxun.models.task_info import TaskInfo
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import text2image, group_image, build_sort_image
from .utils import get_plugins
from .config import ADMIN_HELP_IMAGE
async def build_usage_des_image(
metadata: PluginMetadata,
) -> tuple[BuildImage | None, BuildImage | None]:
"""构建用法和描述图片
参数:
metadata: PluginMetadata
返回:
tuple[BuildImage | None, BuildImage | None]: 用法和描述图片
"""
usage = None
description = None
if metadata.usage:
usage = await text2image(
metadata.usage,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
if metadata.description:
description = await text2image(
metadata.description,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
return usage, description
async def build_image(
plugin: PluginInfo, metadata: PluginMetadata, font: FreeTypeFont
) -> BuildImage:
"""构建帮助图片
参数:
plugin: PluginInfo
metadata: PluginMetadata
font: FreeTypeFont
返回:
BuildImage: 帮助图片
"""
usage, description = await build_usage_des_image(metadata)
width = 0
height = 100
if usage:
width = usage.width
height += usage.height
if description and description.width > width:
width = description.width
height += description.height
font_width, _ = BuildImage.get_text_size(f"{plugin.name}[{plugin.level}]", font)
if font_width > width:
width = font_width
A = BuildImage(width + 30, height + 120, "#EAEDF2")
await A.text((15, 10), f"{plugin.name}[{plugin.level}]")
await A.text((15, 70), "简介:")
if not description:
description = BuildImage(A.width - 30, 30, (255, 255, 255))
await description.circle_corner(10)
await A.paste(description, (15, 100))
if not usage:
usage = BuildImage(A.width - 30, 30, (255, 255, 255))
await usage.circle_corner(10)
await A.text((15, description.height + 115), "用法:")
await A.paste(usage, (15, description.height + 145))
await A.circle_corner(10)
return A
async def build_help():
"""构造管理员帮助图片
返回:
BuildImage: 管理员帮助图片
"""
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
image_list = []
for data in await get_plugins():
plugin = data.plugin
metadata = data.metadata
try:
A = await build_image(plugin, metadata, font)
image_list.append(A)
except Exception as e:
logger.warning(
f"获取群管理员插件 {plugin.module}: {plugin.name} 设置失败...",
"管理员帮助",
e=e,
)
if task_list := await TaskInfo.all():
task_str = "\n".join([task.name for task in task_list])
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
await task_image.circle_corner(10)
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
await A.text((25, 10), "被动技能")
await A.paste(task_image, (25, 50))
await A.circle_corner(10)
image_list.append(A)
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color=(255, 255, 255), padding_top=160)
text = await BuildImage.build_text_image(
"群管理员帮助",
size=40,
)
tip = await BuildImage.build_text_image(
"注: * 代表可有多个相同参数 ? 代表可省略该参数", size=25, font_color="red"
)
await A.paste(text, (50, 30))
await A.paste(tip, (50, 90))
await A.save(ADMIN_HELP_IMAGE)

View File

@ -0,0 +1,22 @@
import nonebot
from zhenxun.utils.enum import PluginType
from zhenxun.utils.exception import EmptyError
from zhenxun.models.plugin_info import PluginInfo
from .config import PluginData
async def get_plugins() -> list[PluginData]:
"""获取插件数据"""
plugin_list = await PluginInfo.filter(
plugin_type__in=[PluginType.ADMIN, PluginType.SUPER_AND_ADMIN]
).all()
data_list = []
for plugin in plugin_list:
if _plugin := nonebot.get_plugin_by_module_name(plugin.module_path):
if _plugin.metadata:
data_list.append(PluginData(plugin=plugin, metadata=_plugin.metadata))
if not data_list:
raise EmptyError()
return data_list

View File

@ -1,158 +0,0 @@
import nonebot
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_alconna.matcher import AlconnaMatcher
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.models.task_info import TaskInfo
from zhenxun.utils.exception import EmptyError
from zhenxun.utils.message import MessageUtils
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.configs.path_config import IMAGE_PATH
from zhenxun.utils.image_utils import (
BuildImage,
text2image,
group_image,
build_sort_image,
)
__plugin_meta__ = PluginMetadata(
name="超级用户帮助",
description="超级用户帮助",
usage="""
超级用户帮助
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.SUPERUSER,
).dict(),
)
_matcher = on_alconna(
Alconna("超级用户帮助"),
permission=SUPERUSER,
priority=5,
block=True,
)
SUPERUSER_HELP_IMAGE = IMAGE_PATH / "SUPERUSER_HELP.png"
if SUPERUSER_HELP_IMAGE.exists():
SUPERUSER_HELP_IMAGE.unlink()
async def build_help() -> BuildImage:
"""构造超级用户帮助图片
异常:
EmptyError: 超级用户帮助为空
返回:
BuildImage: 超级用户帮助图片
"""
plugin_list = await PluginInfo.filter(plugin_type=PluginType.SUPERUSER).all()
data_list = []
for plugin in plugin_list:
if _plugin := nonebot.get_plugin_by_module_name(plugin.module_path):
if _plugin.metadata:
data_list.append({"plugin": plugin, "metadata": _plugin.metadata})
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
image_list = []
for data in data_list:
plugin = data["plugin"]
metadata = data["metadata"]
try:
usage = None
description = None
if metadata.usage:
usage = await text2image(
metadata.usage,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
if metadata.description:
description = await text2image(
metadata.description,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
width = 0
height = 100
if usage:
width = usage.width
height += usage.height
if description and description.width > width:
width = description.width
height += description.height
font_width, font_height = BuildImage.get_text_size(
plugin.name + f"[{plugin.level}]", font
)
if font_width > width:
width = font_width
A = BuildImage(width + 30, height + 120, "#EAEDF2")
await A.text((15, 10), plugin.name + f"[{plugin.level}]")
await A.text((15, 70), "简介:")
if not description:
description = BuildImage(A.width - 30, 30, (255, 255, 255))
await description.circle_corner(10)
await A.paste(description, (15, 100))
if not usage:
usage = BuildImage(A.width - 30, 30, (255, 255, 255))
await usage.circle_corner(10)
await A.text((15, description.height + 115), "用法:")
await A.paste(usage, (15, description.height + 145))
await A.circle_corner(10)
image_list.append(A)
except Exception as e:
logger.warning(
f"获取超级用户管理员插件 {plugin.module}: {plugin.name} 设置失败...",
"超级用户帮助",
e=e,
)
if task_list := await TaskInfo.all():
task_str = "\n".join([task.name for task in task_list])
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
await task_image.circle_corner(10)
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
await A.text((25, 10), "被动技能")
await A.paste(task_image, (25, 50))
await A.circle_corner(10)
image_list.append(A)
if not image_list:
raise EmptyError()
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color=(255, 255, 255), padding_top=160)
text = await BuildImage.build_text_image(
"超级用户帮助",
size=40,
)
tip = await BuildImage.build_text_image(
"注: * 代表可有多个相同参数 ? 代表可省略该参数", size=25, font_color="red"
)
await A.paste(text, (50, 30))
await A.paste(tip, (50, 90))
await A.save(SUPERUSER_HELP_IMAGE)
return BuildImage(1, 1)
@_matcher.handle()
async def _(
session: EventSession,
matcher: AlconnaMatcher,
arparma: Arparma,
):
if not SUPERUSER_HELP_IMAGE.exists():
try:
await build_help()
except EmptyError:
await MessageUtils.build_message("超级用户帮助为空").finish(reply_to=True)
await MessageUtils.build_message(SUPERUSER_HELP_IMAGE).send()
logger.info("查看超级用户帮助", arparma.header_result, session=session)

View File

@ -0,0 +1,59 @@
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.enum import PluginType
from zhenxun.utils.exception import EmptyError
from zhenxun.utils.message import MessageUtils
from zhenxun.configs.utils import RegisterConfig, PluginExtraData
from .normal_help import build_help
from .config import SUPERUSER_HELP_IMAGE
from .zhenxun_help import build_html_help
__plugin_meta__ = PluginMetadata(
name="超级用户帮助",
description="超级用户帮助",
usage="""
超级用户帮助
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.SUPERUSER,
configs=[
RegisterConfig(
key="type",
value="zhenxun",
help="超级用户帮助样式normal, zhenxun",
default_value="zhenxun",
)
],
).dict(),
)
_matcher = on_alconna(
Alconna("超级用户帮助"),
permission=SUPERUSER,
priority=5,
block=True,
)
@_matcher.handle()
async def _(session: EventSession, arparma: Arparma):
if not SUPERUSER_HELP_IMAGE.exists():
try:
if Config.get_config("admin_help", "type") == "zhenxun":
await build_html_help()
else:
await build_help()
except EmptyError:
await MessageUtils.build_message("当前超级用户帮助为空...").finish(
reply_to=True
)
await MessageUtils.build_message(SUPERUSER_HELP_IMAGE).send()
logger.info("查看超级用户帮助", arparma.header_result, session=session)

View File

@ -0,0 +1,23 @@
from pydantic import BaseModel
from nonebot.plugin import PluginMetadata
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.configs.path_config import IMAGE_PATH
SUPERUSER_HELP_IMAGE = IMAGE_PATH / "SUPERUSER_HELP.png"
if SUPERUSER_HELP_IMAGE.exists():
SUPERUSER_HELP_IMAGE.unlink()
class PluginData(BaseModel):
"""
插件信息
"""
plugin: PluginInfo
"""插件信息"""
metadata: PluginMetadata
"""元数据"""
class Config:
arbitrary_types_allowed = True

View File

@ -0,0 +1,127 @@
from PIL.ImageFont import FreeTypeFont
from nonebot.plugin import PluginMetadata
from zhenxun.services.log import logger
from zhenxun.models.task_info import TaskInfo
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import text2image, group_image, build_sort_image
from .utils import get_plugins
from .config import SUPERUSER_HELP_IMAGE
async def build_usage_des_image(
metadata: PluginMetadata,
) -> tuple[BuildImage | None, BuildImage | None]:
"""构建用法和描述图片
参数:
metadata: PluginMetadata
返回:
tuple[BuildImage | None, BuildImage | None]: 用法和描述图片
"""
usage = None
description = None
if metadata.usage:
usage = await text2image(
metadata.usage,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
if metadata.description:
description = await text2image(
metadata.description,
padding=5,
color=(255, 255, 255),
font_color=(0, 0, 0),
)
return usage, description
async def build_image(
plugin: PluginInfo, metadata: PluginMetadata, font: FreeTypeFont
) -> BuildImage:
"""构建帮助图片
参数:
plugin: PluginInfo
metadata: PluginMetadata
font: FreeTypeFont
返回:
BuildImage: 帮助图片
"""
usage, description = await build_usage_des_image(metadata)
width = 0
height = 100
if usage:
width = usage.width
height += usage.height
if description and description.width > width:
width = description.width
height += description.height
font_width, _ = BuildImage.get_text_size(f"{plugin.name}[{plugin.level}]", font)
if font_width > width:
width = font_width
A = BuildImage(width + 30, height + 120, "#EAEDF2")
await A.text((15, 10), f"{plugin.name}[{plugin.level}]")
await A.text((15, 70), "简介:")
if not description:
description = BuildImage(A.width - 30, 30, (255, 255, 255))
await description.circle_corner(10)
await A.paste(description, (15, 100))
if not usage:
usage = BuildImage(A.width - 30, 30, (255, 255, 255))
await usage.circle_corner(10)
await A.text((15, description.height + 115), "用法:")
await A.paste(usage, (15, description.height + 145))
await A.circle_corner(10)
return A
async def build_help():
"""构造超级用户帮助图片
返回:
BuildImage: 超级用户帮助图片
"""
font = BuildImage.load_font("HYWenHei-85W.ttf", 20)
image_list = []
for data in await get_plugins():
plugin = data.plugin
metadata = data.metadata
try:
A = await build_image(plugin, metadata, font)
image_list.append(A)
except Exception as e:
logger.warning(
f"获取群超级用户插件 {plugin.module}: {plugin.name} 设置失败...",
"超级用户帮助",
e=e,
)
if task_list := await TaskInfo.all():
task_str = "\n".join([task.name for task in task_list])
task_str = "通过 开启/关闭群被动 来控制群被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(255, 255, 255))
await task_image.circle_corner(10)
A = BuildImage(task_image.width + 50, task_image.height + 85, "#EAEDF2")
await A.text((25, 10), "被动技能")
await A.paste(task_image, (25, 50))
await A.circle_corner(10)
image_list.append(A)
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color=(255, 255, 255), padding_top=160)
text = await BuildImage.build_text_image(
"群超级用户帮助",
size=40,
)
tip = await BuildImage.build_text_image(
"注: * 代表可有多个相同参数 ? 代表可省略该参数", size=25, font_color="red"
)
await A.paste(text, (50, 30))
await A.paste(tip, (50, 90))
await A.save(SUPERUSER_HELP_IMAGE)

View File

@ -0,0 +1,22 @@
import nonebot
from zhenxun.utils.enum import PluginType
from zhenxun.utils.exception import EmptyError
from zhenxun.models.plugin_info import PluginInfo
from .config import PluginData
async def get_plugins() -> list[PluginData]:
"""获取插件数据"""
plugin_list = await PluginInfo.filter(
plugin_type__in=[PluginType.SUPERUSER, PluginType.SUPER_AND_ADMIN]
).all()
data_list = []
for plugin in plugin_list:
if _plugin := nonebot.get_plugin_by_module_name(plugin.module_path):
if _plugin.metadata:
data_list.append(PluginData(plugin=plugin, metadata=_plugin.metadata))
if not data_list:
raise EmptyError()
return data_list

View File

@ -0,0 +1,59 @@
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.configs.config import BotConfig
from zhenxun.models.task_info import TaskInfo
from zhenxun.utils._build_image import BuildImage
from zhenxun.configs.path_config import TEMPLATE_PATH
from .utils import get_plugins
from .config import SUPERUSER_HELP_IMAGE
async def get_task() -> dict[str, str] | None:
"""获取被动技能帮助"""
if task_list := await TaskInfo.all():
return {
"name": "被动技能",
"description": "控制群组中的被动技能状态",
"usage": "通过 开启/关闭群被动 来控制群被动 <br> ---------- <br> "
+ "<br>".join([task.name for task in task_list]),
}
return None
async def build_html_help():
"""构建帮助图片"""
plugins = await get_plugins()
plugin_list = []
for data in plugins:
if data.metadata.extra:
if superuser_help := data.metadata.extra.get("superuser_help"):
data.metadata.usage += f"<br>以下为超级用户额外命令<br>{superuser_help}"
plugin_list.append(
{
"name": data.plugin.name,
"description": data.metadata.description.replace("\n", "<br>"),
"usage": data.metadata.usage.replace("\n", "<br>"),
}
)
if task := await get_task():
plugin_list.append(task)
plugin_list.sort(key=lambda p: len(p["description"]) + len(p["usage"]))
pic = await template_to_pic(
template_path=str((TEMPLATE_PATH / "help").absolute()),
template_name="main.html",
templates={
"data": {
"plugin_list": plugin_list,
"nickname": BotConfig.self_nickname,
"help_name": "超级用户",
}
},
pages={
"viewport": {"width": 1024, "height": 1024},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,
)
result = await BuildImage.open(pic).resize(0.5)
await result.save(SUPERUSER_HELP_IMAGE)

View File

@ -17,6 +17,32 @@ _yaml.indent = 2
_yaml.allow_unicode = True
class Example(BaseModel):
"""
示例
"""
exec: str
"""执行命令"""
description: str = ""
"""命令描述"""
class Command(BaseModel):
"""
具体参数说明
"""
command: str
"""命令"""
params: list[str] = []
"""参数"""
description: str = ""
"""描述"""
examples: list[Example] = []
"""示例列表"""
class RegisterConfig(BaseModel):
"""
注册配置项
@ -167,6 +193,8 @@ class PluginExtraData(BaseModel):
"""插件基本配置"""
limits: list[BaseBlock | PluginCdBlock | PluginCountBlock] | None = None
"""插件限制"""
commands: list[Command] = []
"""命令列表,用于说明帮助"""
tasks: list[Task] | None = None
"""技能被动"""
superuser_help: str | None = None

View File

@ -9,8 +9,6 @@ from zhenxun.services.log import logger
driver = nonebot.get_driver()
PLUGINS_METHOD = []
class PluginInit(ABC):
"""

View File

@ -1,23 +1,22 @@
import os
import random
import re
import random
from io import BytesIO
from pathlib import Path
from typing import Awaitable, Callable
from collections.abc import Callable, Awaitable
import cv2
import imagehash
from imagehash import ImageHash
from nonebot.utils import is_coroutine_callable
from PIL import Image
from nonebot.utils import is_coroutine_callable
from zhenxun.configs.path_config import IMAGE_PATH, TEMP_PATH
from zhenxun.services.log import logger
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.configs.path_config import TEMP_PATH, IMAGE_PATH
from ._build_image import BuildImage, ColorAlias
from ._build_mat import BuildMat, MatType
from ._image_template import ImageTemplate, RowStyle
from ._build_mat import MatType, BuildMat # noqa: F401
from ._image_template import RowStyle, ImageTemplate # noqa: F401
# TODO: text2image 长度错误
@ -192,8 +191,9 @@ async def text2image(
s.strip(), font, font_size, font_color
)
)
height = sum(img.height + 8 for img in image_list) + pw
width += pw
height += ph
# height += ph
A = BuildImage(
width + left_padding,
height + top_padding + 2,
@ -386,7 +386,7 @@ def get_img_hash(image_file: str | Path) -> str:
with open(image_file, "rb") as fp:
hash_value = imagehash.average_hash(Image.open(fp))
except Exception as e:
logger.warning(f"获取图片Hash出错", "禁言检测", e=e)
logger.warning("获取图片Hash出错", "禁言检测", e=e)
return str(hash_value)
@ -407,7 +407,7 @@ async def get_download_image_hash(url: str, mark: str) -> str:
img_hash = get_img_hash(TEMP_PATH / f"compare_download_{mark}_img.jpg")
return str(img_hash)
except Exception as e:
logger.warning(f"下载读取图片Hash出错", e=e)
logger.warning("下载读取图片Hash出错", e=e)
return ""