新增好感度/金币设置和详细帮助 (#1831)

*  新增好感度/金币设置和详细帮助

* 🔧 修复html帮助
This commit is contained in:
HibiKier 2025-01-10 18:39:23 +08:00 committed by GitHub
parent dc143c0682
commit 264929e5cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 281 additions and 44 deletions

View File

@ -14,7 +14,7 @@ from nonebot_plugin_alconna import (
from nonebot_plugin_session import EventSession
import pytz
from zhenxun.configs.utils import PluginExtraData
from zhenxun.configs.utils import Command, PluginExtraData
from zhenxun.models.chat_history import ChatHistory
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.services.log import logger
@ -45,6 +45,13 @@ __plugin_meta__ = PluginMetadata(
version="0.1",
plugin_type=PluginType.NORMAL,
menu_type="数据统计",
commands=[
Command(command="消息统计"),
Command(command="日消息统计"),
Command(command="周消息排行"),
Command(command="月消息排行"),
Command(command="年消息排行"),
],
).to_dict(),
)

View File

@ -13,7 +13,11 @@ from nonebot_plugin_alconna import (
)
from nonebot_plugin_uninfo import Uninfo
from zhenxun.builtin_plugins.help._config import GROUP_HELP_PATH, SIMPLE_HELP_IMAGE
from zhenxun.builtin_plugins.help._config import (
GROUP_HELP_PATH,
SIMPLE_DETAIL_HELP_IMAGE,
SIMPLE_HELP_IMAGE,
)
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
@ -47,6 +51,7 @@ _matcher = on_alconna(
"功能",
Args["name?", str],
Option("-s|--superuser", action=store_true, help_text="超级用户帮助"),
Option("-d|--detail", action=store_true, help_text="详细帮助"),
),
aliases={"help", "帮助", "菜单"},
rule=to_me(),
@ -55,12 +60,21 @@ _matcher = on_alconna(
)
_matcher.shortcut(
r"详细帮助",
command="功能",
arguments=["--detail"],
prefix=True,
)
@_matcher.handle()
async def _(
bot: Bot,
name: Match[str],
session: Uninfo,
is_superuser: Query[bool] = AlconnaQuery("superuser.value", False),
is_detail: Query[bool] = AlconnaQuery("detail.value", False),
):
_is_superuser = is_superuser.result if is_superuser.available else False
if name.available:
@ -74,11 +88,15 @@ async def _(
)
logger.info(f"查看帮助详情: {name.result}", "帮助", session=session)
elif session.group and (gid := session.group.id):
_image_path = GROUP_HELP_PATH / f"{gid}.png"
_image_path = GROUP_HELP_PATH / f"{gid}_{is_detail.result}.png"
if not _image_path.exists():
await create_help_img(session, gid)
result = await create_help_img(session, gid, is_detail.result)
await MessageUtils.build_message(_image_path).finish()
else:
if not SIMPLE_HELP_IMAGE.exists():
await create_help_img(session, None)
await MessageUtils.build_message(SIMPLE_HELP_IMAGE).finish()
if is_detail.result:
_image_path = SIMPLE_DETAIL_HELP_IMAGE
else:
_image_path = SIMPLE_HELP_IMAGE
if not _image_path.exists():
result = await create_help_img(session, None, is_detail.result)
await MessageUtils.build_message(_image_path).finish()

View File

@ -10,4 +10,8 @@ SIMPLE_HELP_IMAGE = IMAGE_PATH / "SIMPLE_HELP.png"
if SIMPLE_HELP_IMAGE.exists():
SIMPLE_HELP_IMAGE.unlink()
SIMPLE_DETAIL_HELP_IMAGE = IMAGE_PATH / "SIMPLE_DETAIL_HELP.png"
if SIMPLE_DETAIL_HELP_IMAGE.exists():
SIMPLE_DETAIL_HELP_IMAGE.unlink()
base_config = Config.get("help")

View File

@ -1,3 +1,5 @@
from pathlib import Path
import nonebot
from nonebot_plugin_uninfo import Uninfo
@ -7,7 +9,12 @@ from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.enum import PluginType
from zhenxun.utils.image_utils import BuildImage, ImageTemplate
from ._config import GROUP_HELP_PATH, SIMPLE_HELP_IMAGE, base_config
from ._config import (
GROUP_HELP_PATH,
SIMPLE_DETAIL_HELP_IMAGE,
SIMPLE_HELP_IMAGE,
base_config,
)
from .html_help import build_html_image
from .normal_help import build_normal_image
from .zhenxun_help import build_zhenxun_image
@ -20,7 +27,9 @@ background = IMAGE_PATH / "background" / "0.png"
driver = nonebot.get_driver()
async def create_help_img(session: Uninfo, group_id: str | None):
async def create_help_img(
session: Uninfo, group_id: str | None, is_detail: bool
) -> Path:
"""生成帮助图片
参数:
@ -31,14 +40,21 @@ async def create_help_img(session: Uninfo, group_id: str | None):
match help_type:
case "html":
result = BuildImage.open(await build_html_image(group_id))
result = BuildImage.open(await build_html_image(group_id, is_detail))
case "zhenxun":
result = BuildImage.open(await build_zhenxun_image(session, group_id))
result = BuildImage.open(
await build_zhenxun_image(session, group_id, is_detail)
)
case _:
result = await build_normal_image(group_id)
save_path = GROUP_HELP_PATH / f"{group_id}.png" if group_id else SIMPLE_HELP_IMAGE
result = await build_normal_image(group_id, is_detail)
if group_id:
save_path = GROUP_HELP_PATH / f"{group_id}_{is_detail}.png"
elif is_detail:
save_path = SIMPLE_DETAIL_HELP_IMAGE
else:
save_path = SIMPLE_HELP_IMAGE
await result.save(save_path)
return save_path
async def get_user_allow_help(user_id: str) -> list[PluginType]:

View File

@ -26,11 +26,14 @@ async def sort_type() -> dict[str, list[PluginInfo]]:
return sort_data
async def classify_plugin(group_id: str | None, handle: Callable) -> dict[str, list]:
async def classify_plugin(
group_id: str | None, is_detail: bool, handle: Callable
) -> dict[str, list]:
"""对插件进行分类并判断状态
参数:
group_id: 群组id
is_detail: 是否详细帮助
返回:
dict[str, list[Item]]: 分类插件数据
@ -42,5 +45,5 @@ async def classify_plugin(group_id: str | None, handle: Callable) -> dict[str, l
for plugin in value:
if not classify.get(menu):
classify[menu] = []
classify[menu].append(handle(plugin, group))
classify[menu].append(handle(plugin, group, is_detail))
return classify

View File

@ -47,12 +47,15 @@ ICON2STR = {
}
def __handle_item(plugin: PluginInfo, group: GroupConsole | None) -> Item:
def __handle_item(
plugin: PluginInfo, group: GroupConsole | None, is_detail: bool
) -> Item:
"""构造Item
参数:
plugin: PluginInfo
group: 群组
is_detail: 是否详细
返回:
Item: Item
@ -116,13 +119,14 @@ def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
return plugin_list
async def build_html_image(group_id: str | None) -> bytes:
async def build_html_image(group_id: str | None, is_detail: bool) -> bytes:
"""构造HTML帮助图片
参数:
group_id: 群号
is_detail: 是否详细帮助
"""
classify = await classify_plugin(group_id, __handle_item)
classify = await classify_plugin(group_id, is_detail, __handle_item)
plugin_list = build_plugin_data(classify)
return await template_to_pic(
template_path=str((TEMPLATE_PATH / "menu").absolute()),

View File

@ -9,11 +9,12 @@ from ._utils import sort_type
BACKGROUND_PATH = IMAGE_PATH / "background" / "help" / "simple_help"
async def build_normal_image(group_id: str | None) -> BuildImage:
async def build_normal_image(group_id: str | None, is_detail: bool) -> BuildImage:
"""构造PIL帮助图片
参数:
group_id: 群号
is_detail: 详细帮助
"""
image_list = []
font_size = 24

View File

@ -1,9 +1,11 @@
import nonebot
from nonebot_plugin_htmlrender import template_to_pic
from nonebot_plugin_uninfo import Uninfo
from pydantic import BaseModel
from zhenxun.configs.config import BotConfig
from zhenxun.configs.path_config import TEMPLATE_PATH
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.enum import BlockType
@ -15,9 +17,11 @@ from ._utils import classify_plugin
class Item(BaseModel):
plugin_name: str
"""插件名称"""
commands: list[str]
"""插件命令"""
def __handle_item(plugin: PluginInfo, group: GroupConsole | None):
def __handle_item(plugin: PluginInfo, group: GroupConsole | None, is_detail: bool):
"""构造Item
参数:
@ -36,7 +40,12 @@ def __handle_item(plugin: PluginInfo, group: GroupConsole | None):
plugin.name = f"{plugin.name}(不可用)"
elif group and f"{plugin.module}," in group.block_plugin:
plugin.name = f"{plugin.name}(不可用)"
return Item(plugin_name=f"{plugin.id}-{plugin.name}")
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)
def build_plugin_data(classify: dict[str, list[Item]]) -> list[dict[str, str]]:
@ -123,18 +132,24 @@ def build_line_data(plugin_list: list[dict]) -> list[dict]:
return data
async def build_zhenxun_image(session: Uninfo, group_id: str | None) -> bytes:
async def build_zhenxun_image(
session: Uninfo, group_id: str | None, is_detail: bool
) -> bytes:
"""构造真寻帮助图片
参数:
bot_id: bot_id
group_id: 群号
is_detail: 是否详细帮助
"""
classify = await classify_plugin(group_id, __handle_item)
classify = await classify_plugin(group_id, is_detail, __handle_item)
plugin_list = build_plugin_data(classify)
platform = PlatformUtils.get_platform(session)
bot_id = BotConfig.get_qbot_uid(session.self_id) or session.self_id
bot_ava = PlatformUtils.get_user_avatar_url(bot_id, platform)
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
return await template_to_pic(
template_path=str((TEMPLATE_PATH / "ss_menu").absolute()),
template_name="main.html",
@ -142,10 +157,13 @@ async def build_zhenxun_image(session: Uninfo, group_id: str | None) -> bytes:
"data": {
"plugin_list": plugin_list,
"ava": bot_ava,
"width": width,
"font_size": (title_font, tip_font),
"is_detail": is_detail,
}
},
pages={
"viewport": {"width": 637, "height": 453},
"viewport": {"width": width, "height": 453},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,

View File

@ -4,7 +4,7 @@ from nonebot_plugin_alconna import Alconna, Args, Arparma, At, Match, on_alconna
from nonebot_plugin_uninfo import Uninfo
from playwright.async_api import TimeoutError
from zhenxun.configs.utils import PluginExtraData
from zhenxun.configs.utils import Command, PluginExtraData
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.services.log import logger
from zhenxun.utils.depends import UserName
@ -20,7 +20,9 @@ __plugin_meta__ = PluginMetadata(
指令
我的信息 ?[at]
""".strip(),
extra=PluginExtraData(author="HibiKier", version="0.1").to_dict(),
extra=PluginExtraData(
author="HibiKier", version="0.1", commands=[Command(command="我的信息")]
).to_dict(),
)

View File

@ -145,7 +145,7 @@ async def get_user_info(
bytes: 图片数据
"""
platform = PlatformUtils.get_platform(session) or "qq"
ava_url = PlatformUtils.get_user_avatar_url(user_id, platform)
ava_url = PlatformUtils.get_user_avatar_url(user_id, platform, session.self_id)
user = await UserConsole.get_user(user_id, platform)
level = await LevelUser.get_user_level(user_id, group_id)
sign_level = 0

View File

@ -72,6 +72,8 @@ async def _handle_setting(
cost_gold=setting.cost_gold,
plugin_type=extra_data.plugin_type,
admin_level=extra_data.admin_level,
is_show=extra_data.is_show,
ignore_prompt=extra_data.ignore_prompt,
parent=(plugin.parent_plugin.module_name if plugin.parent_plugin else None),
)
)
@ -121,6 +123,7 @@ async def _():
"admin_level",
"plugin_type",
"is_show",
"ignore_prompt",
]
)
update_list.append(plugin)

View File

@ -10,7 +10,7 @@ from nonebot_plugin_alconna import Alconna, Option, on_alconna, store_true
from nonebot_plugin_uninfo import Uninfo
from zhenxun.configs.config import BotConfig, Config
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
from zhenxun.configs.utils import Command, PluginExtraData, RegisterConfig
from zhenxun.models.ban_console import BanConsole
from zhenxun.models.friend_user import FriendUser
from zhenxun.models.group_member_info import GroupInfoUser
@ -36,6 +36,11 @@ __plugin_meta__ = PluginMetadata(
version="0.1",
plugin_type=PluginType.NORMAL,
menu_type="其他",
commands=[
Command(command="以后叫我 [昵称]"),
Command(command="全局昵称设置 [昵称]"),
Command(command=f"{BotConfig.self_nickname}我是谁"),
],
configs=[
RegisterConfig(
key="BLACK_WORD",

View File

@ -16,7 +16,7 @@ from nonebot_plugin_alconna import (
)
from nonebot_plugin_uninfo import Uninfo
from zhenxun.configs.utils import BaseBlock, PluginExtraData, RegisterConfig
from zhenxun.configs.utils import BaseBlock, Command, PluginExtraData, RegisterConfig
from zhenxun.services.log import logger
from zhenxun.utils.depends import UserName
from zhenxun.utils.enum import BlockType, PluginType
@ -44,6 +44,14 @@ __plugin_meta__ = PluginMetadata(
version="0.1",
plugin_type=PluginType.NORMAL,
menu_type="商店",
commands=[
Command(command="我的金币"),
Command(command="我的道具"),
Command(command="购买道具"),
Command(command="使用道具"),
Command(command="金币排行"),
Command(command="金币总排行"),
],
limits=[BaseBlock(check_type=BlockType.GROUP)],
configs=[
RegisterConfig(

View File

@ -12,7 +12,12 @@ from nonebot_plugin_alconna import (
from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_uninfo import Uninfo
from zhenxun.configs.utils import PluginCdBlock, PluginExtraData, RegisterConfig
from zhenxun.configs.utils import (
Command,
PluginCdBlock,
PluginExtraData,
RegisterConfig,
)
from zhenxun.services.log import logger
from zhenxun.utils.depends import UserName
from zhenxun.utils.message import MessageUtils
@ -37,6 +42,12 @@ __plugin_meta__ = PluginMetadata(
extra=PluginExtraData(
author="HibiKier",
version="0.1",
commands=[
Command(command="签到"),
Command(command="我的签到"),
Command(command="签到排行"),
Command(command="签到总排行"),
],
configs=[
RegisterConfig(
module="send_setu",

View File

@ -10,7 +10,7 @@ from nonebot_plugin_alconna import (
)
from nonebot_plugin_session import EventSession
from zhenxun.configs.utils import PluginExtraData
from zhenxun.configs.utils import Command, PluginExtraData
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
@ -45,6 +45,15 @@ __plugin_meta__ = PluginMetadata(
"全局周功能调用统计",
"全局月功能调用统计",
""".strip(),
commands=[
Command(command="功能调用统计"),
Command(command="日功能调用统计"),
Command(command="周功能调用统计"),
Command(command="我的功能调用统计"),
Command(command="我的日功能调用统计"),
Command(command="我的周功能调用统计"),
Command(command="我的月功能调用统计"),
],
).to_dict(),
)

View File

@ -0,0 +1,114 @@
from decimal import Decimal
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna import Alconna, Args, Arparma, At, Field, on_alconna
from nonebot_plugin_uninfo import Uninfo
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.sign_user import SignUser
from zhenxun.models.user_console import UserConsole
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
__plugin_meta__ = PluginMetadata(
name="高贵的作弊器",
description="这是一个作弊器,设置用户金币数量和好感度",
usage="""
金币设置 100金币数量 @用户
好感度设置 100好感度 @用户
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.SUPERUSER,
).to_dict(),
)
_gold_matcher = on_alconna(
Alconna(
"金币设置",
Args[
"gold",
int,
Field(
missing_tips=lambda: "请在命令后跟随金币数量!",
unmatch_tips=lambda _: "金币数量必须为数字!",
),
][
"at_user",
At,
Field(
missing_tips=lambda: "必须要at一名指定用户",
),
],
),
skip_for_unmatch=False,
permission=SUPERUSER,
priority=5,
block=True,
)
_impression_matcher = on_alconna(
Alconna(
"好感度设置",
Args[
"impression",
float,
Field(
missing_tips=lambda: "请在命令后跟随好感度!",
unmatch_tips=lambda _: "好感度数量必须为数字!",
),
][
"at_user",
At,
Field(
missing_tips=lambda: "必须要at一名指定用户",
),
],
),
skip_for_unmatch=False,
permission=SUPERUSER,
priority=5,
block=True,
)
@_gold_matcher.handle()
async def _(session: Uninfo, arparma: Arparma, gold: int, at_user: At):
user = await UserConsole.get_user(
at_user.target, PlatformUtils.get_platform(session)
)
user.gold = gold
await user.save(update_fields=["gold"])
await MessageUtils.build_message(
["成功将用户", at_user, f"的金币设置为 {gold}"]
).send(reply_to=True)
logger.info(
f"成功将用户{at_user.target}的金币设置为{gold}",
arparma.header_result,
session=session,
)
@_impression_matcher.handle()
async def _(session: Uninfo, arparma: Arparma, impression: float, at_user: At):
platform = PlatformUtils.get_platform(session)
user_console = await UserConsole.get_user(at_user.target, platform)
user, _ = await SignUser.get_or_create(
user_id=at_user.target,
defaults={"user_console": user_console, "platform": platform},
)
user.impression = Decimal(impression)
await user.save(update_fields=["impression"])
await MessageUtils.build_message(
["成功将用户", at_user, f"的好感度设置为 {impression}"]
).send(reply_to=True)
logger.info(
f"成功将用户{at_user.target}的好感度设置为{impression}",
arparma.header_result,
session=session,
)

View File

@ -7,6 +7,7 @@ from nonebot.drivers import Driver
from tortoise.expressions import RawSQL
from tortoise.functions import Count
from zhenxun.configs.config import BotConfig
from zhenxun.models.bot_connect_log import BotConnectLog
from zhenxun.models.chat_history import ChatHistory
from zhenxun.models.statistics import Statistics
@ -50,7 +51,12 @@ class ApiDataSource:
if platform == "qq":
login_info = await bot.get_login_info()
nickname = login_info["nickname"]
ava_url = PlatformUtils.get_user_avatar_url(bot.self_id, "qq") or ""
ava_url = (
PlatformUtils.get_user_avatar_url(
bot.self_id, "qq", BotConfig.get_qbot_uid(bot.self_id)
)
or ""
)
else:
nickname = bot.self_id
ava_url = ""

View File

@ -5,7 +5,7 @@ from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from nonebot_plugin_alconna.uniseg.tools import reply_fetch
from nonebot_plugin_uninfo import Uninfo
from zhenxun.configs.utils import PluginExtraData
from zhenxun.configs.utils import Command, PluginExtraData
from zhenxun.services.log import logger
from zhenxun.utils.manager.message_manager import MessageManager
from zhenxun.utils.message import MessageUtils
@ -17,7 +17,12 @@ __plugin_meta__ = PluginMetadata(
usage="""
引用消息 撤回
""".strip(),
extra=PluginExtraData(author="HibiKier", version="0.1", menu_type="其他").to_dict(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
menu_type="其他",
commands=[Command(command="[引用消息] 撤回")],
).to_dict(),
)

View File

@ -37,7 +37,7 @@ class Command(BaseModel):
"""
command: str
"""命令"""
"""命令名称"""
params: list[str] = []
"""参数"""
description: str = ""
@ -236,6 +236,8 @@ class PluginExtraData(BaseModel):
"""插件限制"""
commands: list[Command] = []
"""命令列表,用于说明帮助"""
ignore_prompt: bool = False
"""是否忽略阻断提示"""
tasks: list[Task] | None = None
"""技能被动"""
superuser_help: str | None = None

View File

@ -44,6 +44,8 @@ class PluginInfo(Model):
"""插件限制"""
admin_level = fields.IntField(default=0, null=True, description="调用所需权限等级")
"""调用所需权限等级"""
ignore_prompt = fields.BooleanField(default=False, description="是否忽略提示")
"""是否忽略阻断提示"""
is_delete = fields.BooleanField(default=False, description="是否删除")
"""是否删除"""
parent = fields.CharField(max_length=255, null=True, description="父插件")
@ -84,4 +86,5 @@ class PluginInfo(Model):
return [
"ALTER TABLE plugin_info ADD COLUMN parent character varying(255);",
"ALTER TABLE plugin_info ADD COLUMN is_show boolean DEFAULT true;",
"ALTER TABLE plugin_info ADD COLUMN ignore_prompt boolean DEFAULT false;",
]

View File

@ -207,7 +207,7 @@ class PlatformUtils:
if user_id.isdigit():
url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
else:
url = f"https://q.qlogo.cn/qqapp/{appid}/{user_id}/100"
url = f"https://q.qlogo.cn/qqapp/{appid}/{user_id}/640"
return await AsyncHttpx.get_content(url) if url else None
@classmethod
@ -220,14 +220,12 @@ class PlatformUtils:
user_id: 用户id
platform: 平台
"""
if platform == "qq":
return (
f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
if user_id.isdigit()
else f"https://q.qlogo.cn/qqapp/{appid}/{user_id}/100"
)
else:
if platform != "qq":
return None
if user_id.isdigit():
return f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
else:
return f"https://q.qlogo.cn/qqapp/{appid}/{user_id}/640"
@classmethod
async def get_group_avatar(cls, gid: str, platform: str) -> bytes | None: