版本更新

This commit is contained in:
HibiKier
2024-10-20 21:11:16 +08:00
parent 4e33bf3a50
commit d594d17bf6
253 changed files with 34987 additions and 2241 deletions
+39 -23
View File
@@ -1,34 +1,53 @@
import uuid
from datetime import datetime
import nonebot
import ujson as json
from nonebot import require
from nonebot.drivers import Driver
from tortoise import Tortoise
from nonebot.adapters import Bot
from nonebot.drivers import Driver
from tortoise.exceptions import OperationalError
from zhenxun.models.goods_info import GoodsInfo
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.models.sign_user import SignUser
from zhenxun.models.user_console import UserConsole
from zhenxun.services.log import logger
from zhenxun.utils.decorator.shop import shop_register
require("nonebot_plugin_apscheduler")
require("nonebot_plugin_alconna")
require("nonebot_plugin_session")
require("nonebot_plugin_userinfo")
require("nonebot_plugin_htmlrender")
# require("nonebot_plugin_uninfo")
import nonebot
import ujson as json
from zhenxun.services.log import logger
from zhenxun.models.sign_user import SignUser
from zhenxun.models.goods_info import GoodsInfo
from zhenxun.models.user_console import UserConsole
from zhenxun.utils.decorator.shop import shop_register
from zhenxun.models.bot_connect_log import BotConnectLog
from zhenxun.models.group_member_info import GroupInfoUser
driver: Driver = nonebot.get_driver()
@driver.on_bot_connect
async def _(bot: Bot):
logger.debug(f"Bot: {bot.self_id} 建立连接...")
await BotConnectLog.create(
bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=1
)
@driver.on_bot_disconnect
async def _(bot: Bot):
logger.debug(f"Bot: {bot.self_id} 断开连接...")
await BotConnectLog.create(
bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=0
)
SIGN_SQL = """
select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability, t1.specify_probability, t1.impression
select distinct on("user_id") t1.user_id, t1.checkin_count, t1.add_probability,
t1.specify_probability, t1.impression
from public.sign_group_users t1
join (
join (
select user_id, max(t2.impression) as max_impression
from public.sign_group_users t2
group by user_id
@@ -38,7 +57,7 @@ from public.sign_group_users t1
BAG_SQL = """
select t1.user_id, t1.gold, t1.property
from public.bag_users t1
join (
join (
select user_id, max(t2.gold) as max_gold
from public.bag_users t2
group by user_id
@@ -74,15 +93,12 @@ async def _():
}
create_list = []
sign_id_list = []
max_uid = 0
if user2uid:
max_uid = max(user2uid.values()) + 1
max_uid = max(user2uid.values()) + 1 if user2uid else 0
for old_sign in old_sign_list:
sign_id_list.append(old_sign["user_id"])
old_bag = [
if old_bag := [
b for b in old_bag_list if b["user_id"] == old_sign["user_id"]
]
if old_bag:
]:
old_bag = old_bag[0]
property = json.loads(old_bag["property"])
props = {}
@@ -115,9 +131,9 @@ async def _():
create_list.clear()
uc_dict = {u.user_id: u for u in await UserConsole.all()}
for old_sign in old_sign_list:
user_console = uc_dict.get(old_sign["user_id"])
if not user_console:
user_console = await UserConsole.get_user(old_sign["user_id"], "qq")
user_console = uc_dict.get(
old_sign["user_id"]
) or await UserConsole.get_user(old_sign["user_id"], "qq")
create_list.append(
SignUser(
user_id=old_sign["user_id"],
+21 -5
View File
@@ -2,12 +2,14 @@ from pathlib import Path
import aiofiles
from nonebot.rule import to_me
from nonebot_plugin_uninfo import Uninfo
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.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
from zhenxun.configs.path_config import DATA_PATH
from zhenxun.configs.utils import PluginExtraData
__plugin_meta__ = PluginMetadata(
@@ -25,19 +27,33 @@ _matcher = on_alconna(Alconna("关于"), priority=5, block=True, rule=to_me())
@_matcher.handle()
async def _(session: EventSession, arparma: Arparma):
async def _(session: Uninfo, arparma: Arparma):
ver_file = Path() / "__version__"
version = None
if ver_file.exists():
async with aiofiles.open(ver_file, encoding="utf8") as f:
if text := await f.read():
version = text.split(":")[-1].strip()
info = f"""
if PlatformUtils.is_qbot(session):
info: list[str | Path] = [
f"""
『绪山真寻Bot』
版本:{version}
简介:基于Nonebot2开发,支持多平台,是一个非常可爱的Bot呀,希望与大家要好好相处
""".strip()
]
path = DATA_PATH / "about.png"
if path.exists():
info.append(path)
else:
info = [
f"""
『绪山真寻Bot』
版本:{version}
简介:基于Nonebot2开发,支持多平台,是一个非常可爱的Bot呀,希望与大家要好好相处
项目地址:https://github.com/HibiKier/zhenxun_bot
文档地址:https://hibikier.github.io/zhenxun_bot/
""".strip()
await MessageUtils.build_message(info).send()
""".strip()
]
await MessageUtils.build_message(info).send() # type: ignore
logger.info("查看关于", arparma.header_result, session=session)
+10 -7
View File
@@ -1,16 +1,18 @@
from nonebot import on_notice
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
from nonebot.plugin import PluginMetadata
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
from zhenxun.configs.config import Config
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
from zhenxun.models.level_user import LevelUser
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.enum import PluginType
from zhenxun.utils.rules import notice_rule
from zhenxun.models.level_user import LevelUser
from zhenxun.configs.utils import RegisterConfig, PluginExtraData
__plugin_meta__ = PluginMetadata(
name="群管理员变动监测",
description="检测群管理员变动, 添加与删除管理员默认权限, 当配置项 ADMIN_DEFAULT_AUTH 为空时, 不会添加管理员权限",
description="""检测群管理员变动, 添加与删除管理员默认权限,
当配置项 ADMIN_DEFAULT_AUTH 为空时, 不会添加管理员权限""",
usage="",
extra=PluginExtraData(
author="HibiKier",
@@ -29,7 +31,7 @@ __plugin_meta__ = PluginMetadata(
)
admin_notice = on_notice(priority=5)
admin_notice = on_notice(priority=5, rule=notice_rule(GroupAdminNoticeEvent))
base_config = Config.get("admin_bot_manage")
@@ -52,7 +54,8 @@ async def _(event: GroupAdminNoticeEvent):
)
else:
logger.warning(
f"配置项 MODULE: [<u><y>admin_bot_manage</y></u>] | KEY: [<u><y>ADMIN_DEFAULT_AUTH</y></u>] 为空"
"配置项 MODULE: [<u><y>admin_bot_manage</y></u>] |"
" KEY: [<u><y>ADMIN_DEFAULT_AUTH</y></u>] 为空"
)
elif event.sub_type == "unset":
await LevelUser.delete_level(str(event.user_id), str(event.group_id))
+43 -43
View File
@@ -30,7 +30,7 @@ __plugin_meta__ = PluginMetadata(
usage="""
普通管理员
格式:
ban [At用户] -t [时长(分钟)]
ban [At用户] ?[-t [时长(分钟)]]
示例:
ban @用户 : 永久拉黑用户
@@ -44,7 +44,8 @@ __plugin_meta__ = PluginMetadata(
superuser_help="""
超级管理员额外命令
格式:
ban [At用户/用户Id] [时长]
ban [At用户/用户Id] ?[-t [时长]]
unban --id [idx] : 通过id来进行unban操作
ban列表: 获取所有Ban数据
群组ban列表: 获取群组Ban数据
@@ -98,6 +99,7 @@ _unban_matcher = on_alconna(
"unban",
Args["user?", [str, At]],
Option("-g|--group", Args["group_id", str]),
Option("--id", Args["idx", int]),
),
rule=admin_check("ban", "BAN_LEVEL"),
priority=5,
@@ -238,8 +240,10 @@ async def _(
arparma: Arparma,
user: Match[str | At],
group_id: Match[str],
idx: Match[int],
):
user_id = ""
_idx = idx.result if idx.available else None
if user.available:
if isinstance(user.result, At):
user_id = user.result.target
@@ -248,48 +252,44 @@ async def _(
await MessageUtils.build_message("权限不足捏...").finish(reply_to=True)
user_id = user.result
if gid := session.id3 or session.id2:
u_d = user_id
if group_id.available:
u_d = gid
gid = group_id.result
if await BanManage.unban(
user_id, gid, session, session.id1 in bot.config.superusers
):
logger.info(
"管理员UnBan",
arparma.header_result,
session=session,
target=f"{gid}:{user_id}",
)
await MessageUtils.build_message(
[
"将 ",
(
At(flag="user", target=user_id)
if isinstance(user.result, At)
else u_d
), # type: ignore
" 从黑屋中拉了出来并急救了一下!",
]
).finish(reply_to=True)
else:
await MessageUtils.build_message("该用户不在黑名单中捏...").finish(
reply_to=True
)
is_unban, result = await BanManage.unban(
user_id, gid, session, _idx, session.id1 in bot.config.superusers
)
if not is_unban:
await MessageUtils.build_message(result).finish(reply_to=True)
logger.info(
"管理员UnBan",
arparma.header_result,
session=session,
target=f"{gid}:{result}",
)
await MessageUtils.build_message(
[
"将 ",
(
At(flag="user", target=user_id)
if isinstance(user.result, At)
else result
), # type: ignore
" 从黑屋中拉了出来并急救了一下!",
]
).finish(reply_to=True)
elif session.id1 in bot.config.superusers:
_group_id = group_id.result if group_id.available else None
if await BanManage.unban(user_id, _group_id, session, True):
logger.info(
"超级用户UnBan",
arparma.header_result,
session=session,
target=f"{_group_id}:{user_id}",
)
at_msg = user_id or f"群组:{_group_id}"
await MessageUtils.build_message(
f"对 {at_msg} 从黑屋中拉了出来并急救了一下!"
).finish(reply_to=True)
else:
await MessageUtils.build_message("该用户不在黑名单中捏...").finish(
reply_to=True
)
is_unban, result = await BanManage.unban(
user_id, _group_id, session, _idx, True
)
if not is_unban:
await MessageUtils.build_message(result).finish(reply_to=True)
logger.info(
"超级用户UnBan",
arparma.header_result,
session=session,
target=f"{_group_id}:{user_id}",
)
at_msg = user_id or f"群组:{result}"
await MessageUtils.build_message(
f"对 {at_msg} 从黑屋中拉了出来并急救了一下!"
).finish(reply_to=True)
@@ -32,11 +32,10 @@ class BanManage:
query = query.filter(user_id=user_id)
elif group_id:
query = query.filter(group_id=group_id)
else:
if filter_type == "user":
query = query.filter(group_id__isnull=True)
elif filter_type == "group":
query = query.filter(user_id__isnull=True)
elif filter_type == "user":
query = query.filter(group_id__isnull=True)
elif filter_type == "group":
query = query.filter(user_id__isnull=True)
data_list = await query.all()
if not data_list:
return None
@@ -85,26 +84,36 @@ class BanManage:
user_id: str | None,
group_id: str | None,
session: EventSession,
idx: int | None = None,
is_superuser: bool = False,
) -> bool:
) -> tuple[bool, str]:
"""unban目标用户
参数:
user_id: 用户id
group_id: 群组id
session: Session
idx: 指定id
is_superuser: 是否为超级用户操作
返回:
bool: 是否unban成功
tuple[bool, str]: 是否unban成功, 群组/用户id或提示
"""
user_level = 9999
if not is_superuser and user_id and session.id1:
user_level = await LevelUser.get_user_level(session.id1, group_id)
if await BanConsole.check_ban_level(user_id, group_id, user_level):
if idx:
ban_data = await BanConsole.get_or_none(id=idx)
if not ban_data:
return False, "该用户/群组不在黑名单中不足捏..."
if ban_data.ban_level > user_level:
return False, "unBan权限等级不足捏..."
await ban_data.delete()
return True, str(ban_data.user_id or ban_data.group_id)
elif await BanConsole.check_ban_level(user_id, group_id, user_level):
await BanConsole.unban(user_id, group_id)
return True
return False
return True, str(group_id)
return False, "该用户/群组不在黑名单中不足捏..."
@classmethod
async def ban(
@@ -1,19 +1,19 @@
import nonebot
from nonebot import on_notice
from nonebot.adapters import Bot
from nonebot.adapters.onebot.v11 import GroupIncreaseNoticeEvent
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_session import EventSession
from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from nonebot.adapters.onebot.v11 import GroupIncreaseNoticeEvent
from zhenxun.configs.config import BotConfig
from zhenxun.configs.utils import PluginExtraData
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.configs.config import BotConfig
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
from zhenxun.utils.rules import admin_check, ensure_group
from zhenxun.configs.utils import PluginExtraData
from zhenxun.utils.rules import admin_check, notice_rule, ensure_group
from ._data_source import MemberUpdateManage
@@ -42,25 +42,22 @@ _matcher = on_alconna(
)
_notice = on_notice(priority=1, block=False)
_notice = on_notice(priority=1, block=False, rule=notice_rule(GroupIncreaseNoticeEvent))
@_matcher.handle()
async def _(bot: Bot, session: EventSession, arparma: Arparma):
if gid := session.id3 or session.id2:
logger.info("更新群组成员信息", arparma.header_result, session=session)
await MemberUpdateManage.update(bot, gid)
await MessageUtils.build_message("已经成功更新了群组成员信息!").finish(
reply_to=True
)
result = await MemberUpdateManage.update_group_member(bot, gid)
await MessageUtils.build_message(result).finish(reply_to=True)
await MessageUtils.build_message("群组id为空...").send()
@_notice.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
# TODO: 其他适配器的加群自动更新群组成员信息
if str(event.user_id) == bot.self_id:
await MemberUpdateManage.update(bot, str(event.group_id))
await MemberUpdateManage.update_group_member(bot, str(event.group_id))
logger.info(
f"{BotConfig.self_nickname}加入群聊更新群组信息",
"更新群组成员列表",
@@ -81,7 +78,9 @@ async def _():
if group_list:
for group in group_list:
try:
await MemberUpdateManage.update(bot, group.group_id)
await MemberUpdateManage.update_group_member(
bot, group.group_id
)
logger.debug("自动更新群组成员信息成功...")
except Exception as e:
logger.error(
@@ -1,213 +1,132 @@
from datetime import datetime, timezone, timedelta
from datetime import datetime
import nonebot
from nonebot.adapters import Bot
# from nonebot.adapters.kaiheila import Bot as KaiheilaBot
from nonebot.adapters.onebot.v11 import Bot as v11Bot
from nonebot.adapters.onebot.v12 import Bot as v12Bot
from nonebot_plugin_uninfo import Member, SceneType, get_interface
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.models.level_user import LevelUser
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.group_member_info import GroupInfoUser
# from nonebot.adapters.discord import Bot as DiscordBot
# from nonebot.adapters.dodo import Bot as DodoBot
class MemberUpdateManage:
@classmethod
async def update(cls, bot: Bot, group_id: str):
if not group_id:
return logger.warning(
f"bot: {bot.self_id},group_id为空,无法更新群成员信息..."
)
if isinstance(bot, v11Bot):
await cls.v11(bot, group_id)
elif isinstance(bot, v12Bot):
await cls.v12(bot, group_id)
# elif isinstance(bot, KaiheilaBot):
# await cls.kaiheila(bot, group_id)
# elif isinstance(bot, DodoBot):
# await cls.dodo(bot, group_id)
# elif isinstance(bot, DiscordBot):
# await cls.discord(bot, group_id)
async def __handle_user(
cls,
member: Member,
db_user: list[GroupInfoUser],
group_id: str,
data_list: tuple[list, list, list],
platform: str | None,
):
"""单个成员操作
# @classmethod
# async def discord(cls, bot: DiscordBot, group_id: str):
# # TODO: discord更新群组成员信息
# pass
# @classmethod
# async def dodo(cls, bot: DodoBot, group_id: str):
# page_size = 100
# result_size = 100
# max_id = 0
# exist_member_list = []
# group_member_list: list[MemberInfo] = []
# while result_size == page_size:
# group_member_data = await bot.get_member_list(
# island_source_id=group_id, page_size=page_size
# )
# result_size = len(group_member_data.list)
# group_member_list += group_member_data.list
# max_id = group_member_data.max_id
# if group_member_list:
# for user in group_member_list:
# exist_member_list.append(user.dodo_source_id)
# await GroupInfoUser.update_or_create(
# user_id=user.dodo_source_id,
# group_id=group_id,
# defaults={
# "user_name": user.nick_name or user.personal_nick_name,
# "user_join_time": user.join_time,
# "platform": "dodo",
# },
# )
# if delete_member_list := list(
# set(exist_member_list).difference(
# set(await GroupInfoUser.get_group_member_id_list(group_id))
# )
# ):
# await GroupInfoUser.filter(
# user_id__in=delete_member_list, group_id=group_id
# ).delete()
# logger.info(
# f"删除已退群用户",
# "更新群组成员信息",
# group_id=group_id,
# platform="dodo",
# )
# @classmethod
# async def kaiheila(cls, bot: KaiheilaBot, group_id: str):
# # TODO: kaiheila 更新群组成员信息
# pass
@classmethod
async def v11(cls, bot: v11Bot, group_id: str):
exist_member_list = []
参数:
member: Member
db_user: db成员数据
group_id: 群组id
data_list: 数据列表
platform: 平台
"""
driver = nonebot.get_driver()
default_auth = Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH")
group_member_list = await bot.get_group_member_list(group_id=int(group_id))
db_user = await GroupInfoUser.filter(group_id=group_id).all()
nickname = member.nick or member.user.name or ""
role = member.role
db_user_uid = [u.user_id for u in db_user]
uid2name = {u.user_id: u.user_name for u in db_user}
create_list = []
update_list = []
delete_list = []
for user_info in group_member_list:
user_id = str(user_info["user_id"])
nickname = user_info["card"] or user_info["nickname"]
role = user_info["role"]
if (
default_auth
and role in ["owner", "admin"]
and not await LevelUser.is_group_flag(user_id, group_id)
if member.id in driver.config.superusers:
await LevelUser.set_level(member.id, group_id, 9)
elif role and default_auth:
if role.id != "MEMBER" and not await LevelUser.is_group_flag(
member.id, group_id
):
if role == "owner":
await LevelUser.set_level(user_id, group_id, default_auth + 1)
else:
await LevelUser.set_level(user_id, group_id, default_auth)
if user_id in bot.config.superusers:
await LevelUser.set_level(user_id, group_id, 9)
join_time = datetime.fromtimestamp(
user_info["join_time"], timezone(timedelta(hours=8))
)
if cnt := db_user_uid.count(user_id):
users = [u for u in db_user if u.user_id == user_id]
if cnt > 1:
for u in users[1:]:
delete_list.append(u.id)
if nickname != uid2name.get(user_id):
user = users[0]
user.user_name = nickname
update_list.append(user)
else:
create_list.append(
GroupInfoUser(
user_id=user_id,
group_id=group_id,
user_name=nickname,
user_join_time=join_time,
platform="qq",
)
if role.id == "OWNER":
await LevelUser.set_level(member.id, group_id, default_auth + 1)
elif role.id == "ADMINISTRATOR":
await LevelUser.set_level(member.id, group_id, default_auth)
if cnt := db_user_uid.count(member.id):
users = [u for u in db_user if u.user_id == member.id]
if cnt > 1:
for u in users[1:]:
data_list[2].append(u.id)
if nickname != uid2name.get(member.id):
user = users[0]
user.user_name = nickname
data_list[1].append(user)
else:
data_list[0].append(
GroupInfoUser(
user_id=member.id,
group_id=group_id,
user_name=nickname,
user_join_time=member.joined_at or datetime.now(),
platform=platform,
)
exist_member_list.append(user_id)
if create_list:
await GroupInfoUser.bulk_create(create_list, 30)
logger.debug(
f"创建用户数据 {len(create_list)} 条",
"更新群组成员信息",
target=group_id,
)
if update_list:
await GroupInfoUser.bulk_update(update_list, ["user_name"], 30)
logger.debug(
f"更新户数据 {len(update_list)} 条", "更新群组成员信息", target=group_id
)
if delete_list:
await GroupInfoUser.filter(id__in=delete_list).delete()
logger.debug(f"删除重复数据 Ids: {delete_list}", "更新群组成员信息")
if delete_member_list := [
uid for uid in db_user_uid if uid not in exist_member_list
]:
await GroupInfoUser.filter(
user_id__in=delete_member_list, group_id=group_id
).delete()
logger.info(
f"删除已退群用户 {len(delete_member_list)} 条",
"更新群组成员信息",
group_id=group_id,
platform="qq",
)
@classmethod
async def v12(cls, bot: v12Bot, group_id: str):
# TODO: v12更新群组成员信息
pass
# exist_member_list = []
# default_auth = Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH")
# group_member_list: list[GetGroupMemberInfoResp] =
# await bot.get_group_member_list(
# group_id=group_id
# )
# for user_info in group_member_list:
# user_id = user_info.user_id
# nickname = user_info.user_displayname or user_info.user_name
# role = user_info["role"]
# if default_auth:
# if role in ["owner", "admin"] and not LevelUser.is_group_flag(
# str(user_id), group_id
# ):
# await LevelUser.set_level(user_id, group_id, default_auth)
# if str(user_id) in bot.config.superusers:
# await LevelUser.set_level(str(user_id), group_id, 9)
# join_time = datetime.strptime(
# time.strftime("%Y-%m-%d %H:%M:%S",
# time.localtime(user_info["join_time"])),
# "%Y-%m-%d %H:%M:%S",
# )
# await GroupInfoUser.update_or_create(
# user_id=str(user_id),
# group_id=group_id,
# defaults={
# "user_name": nickname,
# "user_join_time": join_time.replace(
# tzinfo=timezone(timedelta(hours=8))
# ),
# },
# )
# exist_member_list.append(str(user_id))
# logger.debug("更新成功", "更新群组成员信息",
# session=user_id, group_id=group_id)
# if delete_member_list := list(
# set(exist_member_list).difference(
# set(await GroupInfoUser.get_group_member_id_list(group_id))
# )
# ):
# await GroupInfoUser.filter(
# user_id__in=delete_member_list, group_id=group_id
# ).delete()
# logger.info(f"删除已退群用户", "更新群组成员信息", group_id=group_id)
async def update_group_member(cls, bot: Bot, group_id: str) -> str:
"""更新群组成员信息
参数:
bot: Bot
group_id: 群组id
返回:
str: 返回消息
"""
if not group_id:
logger.warning(f"bot: {bot.self_id},group_id为空,无法更新群成员信息...")
return "群组id为空..."
if interface := get_interface(bot):
scenes = await interface.get_scenes()
platform = PlatformUtils.get_platform(bot)
group_list = [s for s in scenes if s.is_group and s.id == group_id]
if not group_list:
logger.warning(
f"bot: {bot.self_id},group_id: {group_id},群组不存在,"
"无法更新群成员信息..."
)
return "更新群组失败,群组不存在..."
members = await interface.get_members(SceneType.GROUP, group_list[0].id)
db_user = await GroupInfoUser.filter(group_id=group_id).all()
db_user_uid = [u.user_id for u in db_user]
data_list = ([], [], [])
exist_member_list = []
for member in members:
logger.debug(f"即将更新群组成员: {member}", "更新群组成员信息")
await cls.__handle_user(member, db_user, group_id, data_list, platform)
exist_member_list.append(member.id)
if data_list[0]:
await GroupInfoUser.bulk_create(data_list[0], 30)
logger.debug(
f"创建用户数据 {len(data_list[0])} 条",
"更新群组成员信息",
target=group_id,
)
if data_list[1]:
await GroupInfoUser.bulk_update(data_list[1], ["user_name"], 30)
logger.debug(
f"更新户数据 {len(data_list[1])} 条",
"更新群组成员信息",
target=group_id,
)
if data_list[2]:
await GroupInfoUser.filter(id__in=data_list[2]).delete()
logger.debug(f"删除重复数据 Ids: {data_list[2]}", "更新群组成员信息")
if delete_member_list := [
uid for uid in db_user_uid if uid not in exist_member_list
]:
await GroupInfoUser.filter(
user_id__in=delete_member_list, group_id=group_id
).delete()
logger.info(
f"删除已退群用户 {len(delete_member_list)} 条",
"更新群组成员信息",
group_id=group_id,
platform="qq",
)
return "群组成员信息更新完成!"
@@ -133,7 +133,7 @@ async def build_task(group_id: str | None) -> BuildImage:
task.id,
task.module,
task.name,
"开启" if task.module not in group.block_task else "关闭",
"开启" if f"<{task.module}," not in group.block_task else "关闭",
"开启" if task.status else "关闭",
task.run_time or "-",
]
@@ -211,12 +211,10 @@ class PluginManage:
if status:
for module in module_list:
group.block_plugin = group.block_plugin.replace(
f"{module},", ""
f"<{module},", ""
)
else:
module_list = await PluginInfo.filter(
plugin_type=PluginType.NORMAL
).values_list("module", flat=True)
module_list = [f"<{module}" for module in module_list]
group.block_plugin = ",".join(module_list) + "," # type: ignore
await group.save(update_fields=["block_plugin"])
return f'成功将此群组所有功能状态修改为: {"开启" if status else "关闭"}'
@@ -413,7 +411,7 @@ class PluginManage:
参数:
task_name: 被动技能名称
group_id: 群组Id
status: 状态
status: 状态,为True时是关闭
is_all: 所有群被动
返回:
@@ -426,6 +424,7 @@ class PluginManage:
group, _ = await GroupConsole.get_or_create(
group_id=group_id, channel_id__isnull=True
)
modules = [f"<{module}" for module in modules]
if status:
group.block_task = ",".join(modules) + "," # type: ignore
else:
@@ -434,16 +433,12 @@ class PluginManage:
await group.save(update_fields=["block_task"])
return f"已成功{status_str}全部被动技能!"
elif task := await TaskInfo.get_or_none(name=task_name):
group, _ = await GroupConsole.get_or_create(
group_id=group_id, channel_id__isnull=True
)
if status:
group.block_task += f"{task.module},"
elif f"super:{task.module}," in group.block_task:
await GroupConsole.set_block_task(group_id, task.module)
elif await GroupConsole.is_superuser_block_task(group_id, task.module):
return f"{status_str} {task_name} 被动技能失败,当前群组该被动已被管理员禁用" # noqa: E501
else:
group.block_task = group.block_task.replace(f"{task.module},", "")
await group.save(update_fields=["block_task"])
await GroupConsole.set_unblock_task(group_id, task.module)
return f"已成功{status_str} {task_name} 被动技能!"
return "没有找到这个被动技能喔..."
@@ -469,20 +464,13 @@ class PluginManage:
name=plugin_name, load_status=True, plugin_type__not=PluginType.PARENT
)
if plugin:
group, _ = await GroupConsole.get_or_create(
group_id=group_id, channel_id__isnull=True
)
status_str = "开启" if status else "关闭"
if status:
if plugin.module in group.block_plugin:
group.block_plugin = group.block_plugin.replace(
f"{plugin.module},", ""
)
await group.save(update_fields=["block_plugin"])
if await GroupConsole.is_normal_block_plugin(group_id, plugin.module):
await GroupConsole.set_unblock_plugin(group_id, plugin.module)
return f"已成功{status_str} {plugin.name} 功能!"
elif plugin.module not in group.block_plugin:
group.block_plugin += f"{plugin.module},"
await group.save(update_fields=["block_plugin"])
elif not await GroupConsole.is_normal_block_plugin(group_id, plugin.module):
await GroupConsole.set_block_plugin(group_id, plugin.module)
return f"已成功{status_str} {plugin.name} 功能!"
return f"该功能已经{status_str}了喔,不要重复{status_str}..."
return "没有找到这个功能喔..."
@@ -504,14 +492,10 @@ class PluginManage:
if not (task := await TaskInfo.get_or_none(name=task_name)):
return "没有找到这个功能喔..."
if group_id:
group, _ = await GroupConsole.get_or_create(
group_id=group_id, channel_id__isnull=True
)
if status:
group.block_task = group.block_task.replace(f"super:{task.module},", "")
await GroupConsole.set_unblock_task(group_id, task.module, True)
else:
group.block_task += f"super:{task.module},"
await group.save(update_fields=["block_task"])
await GroupConsole.set_block_task(group_id, task.module, True)
status_str = "开启" if status else "关闭"
return f"已成功将群组 {group_id} 被动技能 {task_name} {status_str}!"
return "没有找到这个群组喔..."
@@ -538,17 +522,12 @@ class PluginManage:
)
if plugin:
if group_id:
if group := await GroupConsole.get_or_none(
group_id=group_id, channel_id__isnull=True
if not await GroupConsole.is_superuser_block_plugin(
group_id, plugin.module
):
if f"super:{plugin.module}," not in group.block_plugin:
group.block_plugin += f"super:{plugin.module},"
await group.save(update_fields=["block_plugin"])
return (
f"已成功关闭群组 {group.group_name} 的 {plugin_name} 功能!"
)
return "此群组该功能已被超级用户关闭,不要重复关闭..."
return "群组信息未更新,请先更新群组信息..."
await GroupConsole.set_block_plugin(group_id, plugin.module, True)
return f"已成功关闭群组 {group_id} 的 {plugin_name} 功能!"
return "此群组该功能已被超级用户关闭,不要重复关闭..."
plugin.block_type = block_type
plugin.status = not bool(block_type)
await plugin.save(update_fields=["status", "block_type"])
@@ -584,19 +563,12 @@ class PluginManage:
)
if plugin:
if group_id:
if group := await GroupConsole.get_or_none(
group_id=group_id, channel_id__isnull=True
if await GroupConsole.is_superuser_block_plugin(
group_id, plugin.module
):
if f"super:{plugin.module}," in group.block_plugin:
group.block_plugin = group.block_plugin.replace(
f"super:{plugin.module},", ""
)
await group.save(update_fields=["block_plugin"])
return (
f"已成功开启群组 {group.group_name} 的 {plugin_name} 功能!"
)
return "此群组该功能已被超级用户开启,不要重复开启..."
return "群组信息未更新,请先更新群组信息..."
await GroupConsole.set_unblock_plugin(group_id, plugin.module, True)
return f"已成功开启群组 {group_id} 的 {plugin_name} 功能!"
return "此群组该功能已被超级用户开启,不要重复开启..."
plugin.block_type = block_type
plugin.status = not bool(block_type)
await plugin.save(update_fields=["status", "block_type"])
+7 -9
View File
@@ -1,7 +1,7 @@
from nonebot.rule import to_me
from nonebot.adapters import Bot
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_alconna import (
Args,
Match,
@@ -58,28 +58,26 @@ _matcher = on_alconna(
async def _(
bot: Bot,
name: Match[str],
session: EventSession,
session: Uninfo,
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:
if _is_superuser and session.user.id not in bot.config.superusers:
_is_superuser = False
if result := await get_plugin_help(session.id1, name.result, _is_superuser):
if result := await get_plugin_help(session.user.id, name.result, _is_superuser):
await MessageUtils.build_message(result).send(reply_to=True)
else:
await MessageUtils.build_message("没有此功能的帮助信息...").send(
reply_to=True
)
logger.info(f"查看帮助详情: {name.result}", "帮助", session=session)
elif gid := session.id3 or session.id2:
elif session.group and (gid := session.group.id):
_image_path = GROUP_HELP_PATH / f"{gid}.png"
if not _image_path.exists():
await create_help_img(bot.self_id, gid, session.platform)
await create_help_img(session, gid)
await MessageUtils.build_message(_image_path).finish()
else:
if not SIMPLE_HELP_IMAGE.exists():
await create_help_img(bot.self_id, None, session.platform)
await create_help_img(session, None)
await MessageUtils.build_message(SIMPLE_HELP_IMAGE).finish()
+4 -4
View File
@@ -1,4 +1,5 @@
import nonebot
from nonebot_plugin_uninfo import Uninfo
from zhenxun.utils.enum import PluginType
from zhenxun.models.level_user import LevelUser
@@ -19,19 +20,18 @@ background = IMAGE_PATH / "background" / "0.png"
driver = nonebot.get_driver()
async def create_help_img(bot_id: str, group_id: str | None, platform: str):
async def create_help_img(session: Uninfo, group_id: str | None):
"""生成帮助图片
参数:
bot_id: bot id
session: Uninfo
group_id: 群号
platform: 平台
"""
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))
result = BuildImage.open(await build_zhenxun_image(session, group_id))
else:
result = await build_normal_image(group_id)
if group_id:
+20 -8
View File
@@ -1,7 +1,9 @@
from pydantic import BaseModel
from nonebot_plugin_uninfo import Uninfo
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.utils.enum import BlockType
from zhenxun.configs.config import BotConfig
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.configs.path_config import TEMPLATE_PATH
@@ -60,12 +62,21 @@ 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, max_data, 30, 100))
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
name: str, items: list, left: int, width: int | None = None, is_max: bool = False
) -> dict:
"""构造插件行数据
@@ -74,13 +85,14 @@ def build_plugin_line(
items: 插件名称列表
left: 左边距
width: 总插件长度.
is_max: 是否为最大长度的插件菜单
返回:
dict: 插件数据
"""
_plugins = []
width = width or 50
if len(items) // 2 > 6:
if len(items) // 2 > 6 or is_max:
width = 100
plugin_list1 = []
plugin_list2 = []
@@ -113,25 +125,25 @@ def build_line_data(plugin_list: list[dict]) -> list[dict]:
return data
async def build_zhenxun_image(
bot_id: str, group_id: str | None, platform: str
) -> bytes:
async def build_zhenxun_image(session: Uninfo, group_id: str | None) -> bytes:
"""构造真寻帮助图片
参数:
bot_id: bot_id
group_id: 群号
platform: 平台
"""
classify = await classify_plugin(group_id, __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)
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),
"ava": bot_ava,
}
},
pages={
+8 -10
View File
@@ -88,7 +88,7 @@ class LimitManage:
@classmethod
async def check(
cls,
module_path: str,
module: str,
user_id: str,
group_id: str | None,
channel_id: str | None,
@@ -106,11 +106,11 @@ class LimitManage:
异常:
IgnoredException: IgnoredException
"""
if limit_model := cls.cd_limit.get(module_path):
if limit_model := cls.cd_limit.get(module):
await cls.__check(limit_model, user_id, group_id, channel_id, session)
if limit_model := cls.block_limit.get(module_path):
if limit_model := cls.block_limit.get(module):
await cls.__check(limit_model, user_id, group_id, channel_id, session)
if limit_model := cls.count_limit.get(module_path):
if limit_model := cls.count_limit.get(module):
await cls.__check(limit_model, user_id, group_id, channel_id, session)
@classmethod
@@ -298,7 +298,7 @@ class AuthChecker:
LimitManage.add_limit(limit)
if user_id:
await LimitManage.check(
plugin.module_path, user_id, group_id, channel_id, session
plugin.module, user_id, group_id, channel_id, session
)
async def auth_plugin(
@@ -319,8 +319,8 @@ class AuthChecker:
is_poke = isinstance(event, PokeNotifyEvent)
if group_id:
sid = group_id or user_id
if await GroupConsole.is_super_block_plugin(
group_id, plugin.module, channel_id
if await GroupConsole.is_superuser_block_plugin(
group_id, plugin.module
):
"""超级用户群组插件状态"""
if self.is_send_limit_message(plugin, sid) and not is_poke:
@@ -334,9 +334,7 @@ class AuthChecker:
session=session,
)
raise IgnoredException("超级管理员禁用了该群此功能...")
if await GroupConsole.is_block_plugin(
group_id, plugin.module, channel_id
):
if await GroupConsole.is_normal_block_plugin(group_id, plugin.module):
"""群组插件状态"""
if self.is_send_limit_message(plugin, sid) and not is_poke:
self._flmt_s.start_cd(group_id or user_id)
+14 -15
View File
@@ -1,19 +1,18 @@
import logging
from nonebot.adapters import Bot, Event
from nonebot.exception import IgnoredException
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At
from nonebot.adapters import Bot, Event
from nonebot.message import run_preprocessor
from nonebot.exception import IgnoredException
from nonebot_plugin_session import EventSession
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.enum import PluginType
from zhenxun.utils.utils import FreqLimiter
from zhenxun.utils.message import MessageUtils
from zhenxun.models.ban_console import BanConsole
from zhenxun.models.group_console import GroupConsole
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.utils import FreqLimiter
Config.add_plugin_config(
"hook",
@@ -41,11 +40,11 @@ async def _(
if user_id in bot.config.superusers:
return
if await BanConsole.is_ban(None, group_id):
logging.debug("群组处于黑名单中...", "ban_hook")
logger.debug("群组处于黑名单中...", "ban_hook")
raise IgnoredException("群组处于黑名单中...")
if g := await GroupConsole.get_group(group_id):
if g.level < 0:
logging.debug("群黑名单, 群权限-1...", "ban_hook")
logger.debug("群黑名单, 群权限-1...", "ban_hook")
raise IgnoredException("群黑名单, 群权限-1..")
if user_id:
ban_result = Config.get_config("hook", "BAN_RESULT")
@@ -58,12 +57,12 @@ async def _(
else:
time = abs(int(time))
if time < 60:
time_str = str(time) + " 秒"
time_str = f"{time!s} 秒"
else:
minute = int(time / 60)
if minute > 60:
hours = int(minute / 60)
minute = minute % 60
hours = minute // 60
minute %= 60
time_str = f"{hours} 小时 {minute}分钟"
else:
time_str = f"{minute} 分钟"
@@ -75,5 +74,5 @@ async def _(
f"{ban_result}\n在..在 {time_str} 后才会理你喔",
]
).send()
logging.debug("用户处于黑名单中...", "ban_hook")
logger.debug("用户处于黑名单中...", "ban_hook")
raise IgnoredException("用户处于黑名单中...")
+12 -10
View File
@@ -1,13 +1,12 @@
from nonebot.adapters import Bot
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from playwright.async_api import TimeoutError
from nonebot_plugin_alconna import At, Args, Match, Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.utils.depends import UserName
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.group_member_info import GroupInfoUser
@@ -31,26 +30,29 @@ _matcher = on_alconna(Alconna("我的信息", Args["at_user?", At]), priority=5,
@_matcher.handle()
async def _(
bot: Bot,
session: EventSession,
session: Uninfo,
arparma: Arparma,
at_user: Match[At],
nickname: str = UserName(),
):
user_id = session.id1
if at_user.available:
user_id = session.user.id
if at_user.available and session.group:
user_id = at_user.result.target
if user := await GroupInfoUser.get_or_none(
user_id=user_id, group_id=session.id2
user_id=user_id, group_id=session.group.id
):
nickname = user.user_name
else:
nickname = user_id
if not user_id:
await MessageUtils.build_message("用户id为空...").finish(reply_to=True)
try:
result = await get_user_info(bot, user_id, session.id2, nickname)
result = await get_user_info(
session, bot, user_id, session.group.id if session.group else None, nickname
)
await MessageUtils.build_message(result).send(at_sender=True)
logger.info("获取用户信息", arparma.header_result, session=session)
except TimeoutError as e:
logger.error("获取用户信息超时", arparma.header_result, session=session, e=e)
await MessageUtils.build_message("获取用户信息超时...").finish(reply_to=True)
except Exception as e:
logger.error("获取用户信息失败", arparma.header_result, session=session, e=e)
await MessageUtils.build_message("获取用户信息失败...").finish(reply_to=True)
+4 -2
View File
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
from nonebot.adapters import Bot
from tortoise.functions import Count
from tortoise.expressions import RawSQL
from nonebot_plugin_uninfo import Uninfo
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.models.sign_user import SignUser
@@ -130,11 +131,12 @@ async def get_chat_history(
async def get_user_info(
bot: Bot, user_id: str, group_id: str | None, nickname: str
session: Uninfo, bot: Bot, user_id: str, group_id: str | None, nickname: str
) -> bytes:
"""获取用户个人信息
参数:
session: Uninfo
bot: Bot
user_id: 用户id
group_id: 群id
@@ -162,7 +164,7 @@ async def get_user_info(
data = {
"date": now.date(),
"weather": weather,
"ava_url": ava_url,
"ava_url": session.user.avatar,
"nickname": nickname,
"title": "勇 者",
"race": random.choice(RACE),
+22 -18
View File
@@ -73,7 +73,7 @@ class Manager:
def add(
self,
module_path: str,
module: str,
data: BaseBlock | PluginCdBlock | PluginCountBlock | PluginLimit,
):
"""添加限制"""
@@ -106,20 +106,20 @@ class Manager:
max_count=data.max_count,
)
if isinstance(data, PluginCdBlock):
self.cd_data[module_path] = data
self.cd_data[module] = data
elif isinstance(data, PluginCountBlock):
self.count_data[module_path] = data
self.count_data[module] = data
elif isinstance(data, BaseBlock):
self.block_data[module_path] = data
self.block_data[module] = data
def exist(self, module_path: str, type: PluginLimitType):
def exist(self, module: str, type: PluginLimitType):
"""是否存在"""
if type == PluginLimitType.CD:
return module_path in self.cd_data
return module in self.cd_data
elif type == PluginLimitType.BLOCK:
return module_path in self.block_data
return module in self.block_data
elif type == PluginLimitType.COUNT:
return module_path in self.count_data
return module in self.count_data
def init(self):
if not self.cd_file.exists():
@@ -208,6 +208,8 @@ class Manager:
temp = _yaml.load(f)
if "PluginCdLimit" in temp.keys():
for k, v in temp["PluginCdLimit"].items():
if "." in k:
k = k.split(".")[-1]
self.cd_data[k] = PluginCdBlock.parse_obj(v)
def __load_block_file(self):
@@ -217,6 +219,8 @@ class Manager:
temp = _yaml.load(f)
if "PluginBlockLimit" in temp.keys():
for k, v in temp["PluginBlockLimit"].items():
if "." in k:
k = k.split(".")[-1]
self.block_data[k] = BaseBlock.parse_obj(v)
def __load_count_file(self):
@@ -226,6 +230,8 @@ class Manager:
temp = _yaml.load(f)
if "PluginCountLimit" in temp.keys():
for k, v in temp["PluginCountLimit"].items():
if "." in k:
k = k.split(".")[-1]
self.count_data[k] = PluginCountBlock.parse_obj(v)
def __replace_data(
@@ -270,10 +276,10 @@ class Manager:
if not db_data:
return (
PluginLimit(
module=k.split(".")[-1],
module_path=k,
module=k,
module_path=module2plugin[k].module_path,
limit_type=limit_type,
plugin=module2plugin.get(k),
plugin=module2plugin[k],
cd=getattr(limit, "cd", None),
max_count=getattr(limit, "max_count", None),
status=limit.status,
@@ -329,12 +335,10 @@ class Manager:
]
if data := self.__get_file_data(limit_type):
db_type_limit_modules = [
(limit.module_path, limit.id) for limit in db_type_limits
(limit.module, limit.id) for limit in db_type_limits
]
delete_list.extend(
id
for module_path, id in db_type_limit_modules
if module_path not in data.keys()
id for module, id in db_type_limit_modules if module not in data.keys()
)
for k, v in data.items():
if not module2plugin.get(k):
@@ -343,7 +347,7 @@ class Manager:
f"插件模块 {k} 未加载,已过滤当前 {v._type} 限制..."
)
continue
db_data = [limit for limit in db_type_limits if limit.module_path == k]
db_data = [limit for limit in db_type_limits if limit.module == k]
db_data, is_create = self.__set_data(
k, db_data[0] if db_data else None, v, limit_type, module2plugin
)
@@ -369,8 +373,8 @@ class Manager:
+ list(self.block_data.keys())
+ list(self.count_data.keys())
)
plugins = await PluginInfo.get_plugins(module_path__in=modules)
module2plugin = {p.module_path: p for p in plugins}
plugins = await PluginInfo.get_plugins(module__in=modules)
module2plugin = {p.module: p for p in plugins}
create_list, update_list, delete_list = self.__set_db_limits(
db_limits, module2plugin, PluginLimitType.CD
)
+17 -5
View File
@@ -1,11 +1,23 @@
import os
from pathlib import Path
import nonebot
from zhenxun.services.log import logger
path = Path(__file__).parent
for f in os.listdir(path):
_p = path / f
if _p.is_dir():
nonebot.load_plugins(str(_p.resolve()))
try:
from nonebot.adapters.onebot.v11 import Bot
nonebot.load_plugins(str((path / "qq").resolve()))
except ImportError:
logger.warning("未安装 onebot-adapter,无法加载QQ平台专用插件...")
try:
from nonebot.adapters.qq import Bot # noqa: F401
nonebot.load_plugins(str((path / "qq_api").resolve()))
except ImportError:
logger.warning("未安装 qq-adapter,无法加载QQ官平台专用插件...")
@@ -1,4 +1,5 @@
from nonebot.adapters import Bot
from nonebot_plugin_uninfo import Uninfo
from nonebot import on_notice, on_request
from nonebot.plugin import PluginMetadata
from nonebot.adapters.onebot.v11 import (
@@ -11,6 +12,7 @@ from nonebot.adapters.onebot.v12 import (
)
from zhenxun.utils.enum import PluginType
from zhenxun.utils.rules import notice_rule
from zhenxun.utils.platform import PlatformUtils
from zhenxun.utils.common_utils import CommonUtils
from zhenxun.configs.config import Config, BotConfig
@@ -82,16 +84,28 @@ base_config = Config.get("invite_manager")
limit_cd = base_config.get("welcome_msg_cd")
group_increase_handle = on_notice(priority=1, block=False)
group_increase_handle = on_notice(
priority=1,
block=False,
rule=notice_rule([GroupIncreaseNoticeEvent, GroupMemberIncreaseEvent]),
)
"""群员增加处理"""
group_decrease_handle = on_notice(priority=1, block=False)
group_decrease_handle = on_notice(
priority=1,
block=False,
rule=notice_rule([GroupMemberDecreaseEvent, GroupMemberIncreaseEvent]),
)
"""群员减少处理"""
add_group = on_request(priority=1, block=False)
"""加群同意请求"""
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent):
async def _(
bot: Bot,
session: Uninfo,
event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent,
):
user_id = str(event.user_id)
group_id = str(event.group_id)
if user_id == bot.self_id:
@@ -99,17 +113,20 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent
group, _ = await GroupConsole.get_or_create(
group_id=group_id, channel_id__isnull=True
)
if group.group_flag == 0:
try:
await GroupManager.add_bot(bot, str(event.operator_id), group_id, group)
except ForceAddGroupError as e:
await PlatformUtils.send_superuser(bot, e.get_info())
try:
await GroupManager.add_bot(bot, str(event.operator_id), group_id, group)
except ForceAddGroupError as e:
await PlatformUtils.send_superuser(bot, e.get_info())
else:
await GroupManager.add_user(bot, user_id, group_id)
await GroupManager.add_user(session, bot, user_id, group_id)
@group_decrease_handle.handle()
async def _(bot: Bot, event: GroupDecreaseNoticeEvent | GroupMemberDecreaseEvent):
async def _(
bot: Bot,
session: Uninfo,
event: GroupDecreaseNoticeEvent | GroupMemberDecreaseEvent,
):
user_id = str(event.user_id)
group_id = str(event.group_id)
if event.sub_type == "kick_me":
@@ -120,6 +137,6 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent | GroupMemberDecreaseEvent
bot, user_id, group_id, str(event.operator_id), event.sub_type
)
if result and not await CommonUtils.task_is_block(
"refund_group_remind", group_id
session, "refund_group_remind"
):
await group_decrease_handle.send(result)
@@ -7,6 +7,7 @@ from datetime import datetime
import ujson as json
from nonebot.adapters import Bot
from nonebot_plugin_alconna import At
from nonebot_plugin_uninfo import Uninfo
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
@@ -55,7 +56,7 @@ class GroupManager:
block_plugin = ""
if plugin_list := await PluginInfo.filter(default_status=False).all():
for plugin in plugin_list:
block_plugin += f"{plugin.module},"
block_plugin += f"<{plugin.module},"
group_info = await bot.get_group_info(group_id=group_id)
await GroupConsole.create(
group_id=group_info["group_id"],
@@ -83,10 +84,10 @@ class GroupManager:
).values_list("user_id", flat=True)
# 即刻刷新权限
for user_info in member_list:
user_id = user_info["user_id"]
user_id = str(user_info["user_id"])
role = user_info["role"]
if user_id in bot.config.superusers:
await LevelUser.set_level(user_id, user_info["group_id"], 9)
await LevelUser.set_level(user_id, group_id, 9)
logger.debug(
"添加超级用户权限: 9",
"入群检测",
@@ -100,8 +101,8 @@ class GroupManager:
):
await LevelUser.set_level(
user_id,
user_info["group_id"],
admin_default_auth,
group_id,
admin_default_auth if role == "admin" else admin_default_auth + 1,
)
logger.debug(
f"添加默认群管理员权限: {admin_default_auth}",
@@ -202,7 +203,7 @@ class GroupManager:
).send()
@classmethod
async def add_user(cls, bot: Bot, user_id: str, group_id: str):
async def add_user(cls, session: Uninfo, bot: Bot, user_id: str, group_id: str):
"""拉入用户
参数:
@@ -219,7 +220,7 @@ class GroupManager:
)
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
if not await CommonUtils.task_is_block(
"group_welcome", group_id
session, "group_welcome"
) and cls._flmt.check(group_id):
await cls.__send_welcome_message(user_id, group_id)
@@ -0,0 +1,28 @@
from nonebot_plugin_uninfo import Uninfo
from nonebot.message import run_preprocessor
from zhenxun.services.log import logger
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.friend_user import FriendUser
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.group_member_info import GroupInfoUser
@run_preprocessor
async def do_something(session: Uninfo):
platform = PlatformUtils.get_platform(session)
if session.group:
if not await GroupConsole.exists(group_id=session.group.id):
await GroupConsole.create(group_id=session.group.id)
logger.info("添加当前群组ID信息" "", session=session)
if not await GroupInfoUser.exists(
user_id=session.user.id, group_id=session.group.id
):
await GroupInfoUser.create(
user_id=session.user.id, group_id=session.group.id, platform=platform
)
logger.info("添加当前用户群组ID信息", "", session=session)
elif not await FriendUser.exists(user_id=session.user.id, platform=platform):
await FriendUser.create(user_id=session.user.id, platform=platform)
logger.info("添加当前好友用户信息", "", session=session)
@@ -15,10 +15,11 @@ __plugin_meta__ = PluginMetadata(
description="插件商店",
usage="""
插件商店 : 查看当前的插件商店
添加插件 id : 添加插件
移除插件 id : 移除插件
添加插件 id or module : 添加插件
移除插件 id or module : 移除插件
搜索插件 name or author : 搜索插件
更新插件 id : 更新插件
更新插件 id or module : 更新插件
更新全部插件 : 更新全部插件
""".strip(),
extra=PluginExtraData(
author="HibiKier",
@@ -30,10 +31,11 @@ __plugin_meta__ = PluginMetadata(
_matcher = on_alconna(
Alconna(
"插件商店",
Subcommand("add", Args["plugin_id", int]),
Subcommand("remove", Args["plugin_id", int]),
Subcommand("add", Args["plugin_id", int | str]),
Subcommand("remove", Args["plugin_id", int | str]),
Subcommand("search", Args["plugin_name_or_author", str]),
Subcommand("update", Args["plugin_id", int]),
Subcommand("update", Args["plugin_id", int | str]),
Subcommand("update_all"),
),
permission=SUPERUSER,
priority=1,
@@ -68,6 +70,13 @@ _matcher.shortcut(
prefix=True,
)
_matcher.shortcut(
r"更新全部插件",
command="插件商店",
arguments=["update_all"],
prefix=True,
)
@_matcher.assign("$main")
async def _(session: EventSession):
@@ -81,9 +90,12 @@ async def _(session: EventSession):
@_matcher.assign("add")
async def _(session: EventSession, plugin_id: int):
async def _(session: EventSession, plugin_id: int | str):
try:
await MessageUtils.build_message(f"正在添加插件 Id: {plugin_id}").send()
if isinstance(plugin_id, str):
await MessageUtils.build_message(f"正在添加插件 Module: {plugin_id}").send()
else:
await MessageUtils.build_message(f"正在添加插件 Id: {plugin_id}").send()
result = await ShopManage.add_plugin(plugin_id)
except Exception as e:
logger.error(f"添加插件 Id: {plugin_id}失败", "插件商店", session=session, e=e)
@@ -95,7 +107,7 @@ async def _(session: EventSession, plugin_id: int):
@_matcher.assign("remove")
async def _(session: EventSession, plugin_id: int):
async def _(session: EventSession, plugin_id: int | str):
try:
result = await ShopManage.remove_plugin(plugin_id)
except Exception as e:
@@ -126,9 +138,12 @@ async def _(session: EventSession, plugin_name_or_author: str):
@_matcher.assign("update")
async def _(session: EventSession, plugin_id: int):
async def _(session: EventSession, plugin_id: int | str):
try:
await MessageUtils.build_message(f"正在更新插件 Id: {plugin_id}").send()
if isinstance(plugin_id, str):
await MessageUtils.build_message(f"正在更新插件 Module: {plugin_id}").send()
else:
await MessageUtils.build_message(f"正在更新插件 Id: {plugin_id}").send()
result = await ShopManage.update_plugin(plugin_id)
except Exception as e:
logger.error(f"更新插件 Id: {plugin_id}失败", "插件商店", session=session, e=e)
@@ -137,3 +152,16 @@ async def _(session: EventSession, plugin_id: int):
).finish()
logger.info(f"更新插件 Id: {plugin_id}", "插件商店", session=session)
await MessageUtils.build_message(result).send()
@_matcher.assign("update_all")
async def _(session: EventSession):
try:
await MessageUtils.build_message("正在更新全部插件").send()
result = await ShopManage.update_all_plugin()
except Exception as e:
logger.error("更新全部插件失败", "插件商店", session=session, e=e)
await MessageUtils.build_message(
f"更新全部插件失败 e: {e}"
).finish()
logger.info("更新全部插件", "插件商店", session=session)
await MessageUtils.build_message(result).send()
@@ -175,19 +175,20 @@ class ShopManage:
)
@classmethod
async def add_plugin(cls, plugin_id: int) -> str:
async def add_plugin(cls, plugin_id: int | str) -> str:
"""添加插件
参数:
plugin_id: 插件id
plugin_id: 插件id或模块名
返回:
str: 返回消息
"""
data: dict[str, StorePluginInfo] = await cls.get_data()
if plugin_id < 0 or plugin_id >= len(data):
return "插件ID不存在..."
plugin_key = list(data.keys())[plugin_id]
try:
plugin_key = await cls._resolve_plugin_key(plugin_id)
except ValueError as e:
return str(e)
plugin_list = await cls.get_loaded_plugins("module")
plugin_info = data[plugin_key]
if plugin_info.module in [p[0] for p in plugin_list]:
@@ -265,20 +266,21 @@ class ShopManage:
raise Exception("插件下载失败")
@classmethod
async def remove_plugin(cls, plugin_id: int) -> str:
async def remove_plugin(cls, plugin_id: int | str) -> str:
"""移除插件
参数:
plugin_id: 插件id
plugin_id: 插件id或模块名
返回:
str: 返回消息
"""
data: dict[str, StorePluginInfo] = await cls.get_data()
if plugin_id < 0 or plugin_id >= len(data):
return "插件ID不存在..."
plugin_key = list(data.keys())[plugin_id]
plugin_info = data[plugin_key] # type: ignore
try:
plugin_key = await cls._resolve_plugin_key(plugin_id)
except ValueError as e:
return str(e)
plugin_info = data[plugin_key]
path = BASE_PATH
if plugin_info.github_url:
path = BASE_PATH / "plugins"
@@ -340,7 +342,7 @@ class ShopManage:
)
@classmethod
async def update_plugin(cls, plugin_id: int) -> str:
async def update_plugin(cls, plugin_id: int | str) -> str:
"""更新插件
参数:
@@ -350,9 +352,10 @@ class ShopManage:
str: 返回消息
"""
data: dict[str, StorePluginInfo] = await cls.get_data()
if plugin_id < 0 or plugin_id >= len(data):
return "插件ID不存在..."
plugin_key = list(data.keys())[plugin_id]
try:
plugin_key = await cls._resolve_plugin_key(plugin_id)
except ValueError as e:
return str(e)
logger.info(f"尝试更新插件 {plugin_key}", "插件管理")
plugin_info = data[plugin_key]
plugin_list = await cls.get_loaded_plugins("module", "version")
@@ -373,3 +376,56 @@ class ShopManage:
is_external,
)
return f"插件 {plugin_key} 更新成功! 重启后生效"
@classmethod
async def update_all_plugin(cls) -> str:
"""更新插件
参数:
plugin_id: 插件id
返回:
str: 返回消息
"""
data: dict[str, StorePluginInfo] = await cls.get_data()
plugin_list = list(data.keys())
update_list = []
logger.info(f"尝试更新全部插件 {plugin_list}", "插件管理")
for plugin_key in plugin_list:
plugin_info = data[plugin_key]
plugin_list = await cls.get_loaded_plugins("module", "version")
suc_plugin = {p[0]: (p[1] or "Unknown") for p in plugin_list}
if plugin_info.module not in [p[0] for p in plugin_list]:
logger.debug(f"插件 {plugin_key} 未安装,跳过", "插件管理")
continue
if cls.check_version_is_new(plugin_info, suc_plugin):
logger.debug(f"插件 {plugin_key} 已是最新版本,跳过", "插件管理")
continue
logger.info(f"正在更新插件 {plugin_key}", "插件管理")
is_external = True
if plugin_info.github_url is None:
plugin_info.github_url = DEFAULT_GITHUB_URL
is_external = False
await cls.install_plugin_with_repo(
plugin_info.github_url,
plugin_info.module_path,
plugin_info.is_dir,
is_external,
)
update_list.append(plugin_key)
if len(update_list) == 0:
return "全部插件已是最新版本"
return "已更新插件 {}\n共计{}个插件! 重启后生效".format(
"\n- ".join(update_list), len(update_list)
)
@classmethod
async def _resolve_plugin_key(cls, plugin_id: int | str) -> str:
data: dict[str, StorePluginInfo] = await cls.get_data()
if isinstance(plugin_id, int):
if plugin_id < 0 or plugin_id >= len(data):
raise ValueError("插件ID不存在...")
return list(data.keys())[plugin_id]
elif isinstance(plugin_id, str):
if plugin_id not in [v.module for k, v in data.items()]:
raise ValueError("插件Module不存在...")
return {v.module: k for k, v in data.items()}[plugin_id]
@@ -53,6 +53,7 @@ async def _():
_group, _ = await GroupConsole.get_or_create(
group_id=group.group_id, channel_id__isnull=True
)
modules = [f"<{module}" for module in modules]
_group.block_task = ",".join(modules) + "," # type: ignore
update_list.append(_group)
logger.info(
+3 -2
View File
@@ -1,4 +1,5 @@
import nonebot
from nonebot.adapters import Bot
from nonebot.plugin import PluginMetadata
from nonebot_plugin_apscheduler import scheduler
@@ -26,8 +27,8 @@ __plugin_meta__ = PluginMetadata(
driver = nonebot.get_driver()
async def check(group_id: str) -> bool:
return not await CommonUtils.task_is_block("morning_goodnight", group_id)
async def check(bot: Bot, group_id: str) -> bool:
return not await CommonUtils.task_is_block(bot, "morning_goodnight", group_id)
# 早上好
+34 -4
View File
@@ -1,13 +1,15 @@
from asyncio.exceptions import TimeoutError
import nonebot
import aiofiles
import ujson as json
from nonebot.drivers import Driver
from nonebot_plugin_apscheduler import scheduler
from zhenxun.configs.path_config import TEXT_PATH
from zhenxun.services.log import logger
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.configs.path_config import TEXT_PATH
from zhenxun.models.group_console import GroupConsole
driver: Driver = nonebot.get_driver()
@@ -19,8 +21,8 @@ async def update_city():
这里直接更新,避免插件内代码重复
"""
china_city = TEXT_PATH / "china_city.json"
data = {}
if not china_city.exists():
data = {}
try:
logger.debug("开始更新城市列表...")
res = await AsyncHttpx.get(
@@ -38,7 +40,7 @@ async def update_city():
city_data = json.loads(res.text)
for city in city_data.keys():
data[provinces_data[province]].append(city_data[city])
with open(china_city, "w", encoding="utf8") as f:
async with aiofiles.open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....")
except TimeoutError as e:
@@ -46,7 +48,7 @@ async def update_city():
except ValueError as e:
logger.warning("自动城市列表失败.....", e=e)
except Exception as e:
logger.error(f"自动城市列表未知错误", e=e)
logger.error("自动城市列表未知错误", e=e)
# 自动更新城市列表
@@ -57,3 +59,31 @@ async def update_city():
)
async def _():
await update_city()
@driver.on_startup
async def _():
"""开启/禁用插件格式修改"""
_, is_create = await GroupConsole.get_or_create(group_id=133133133)
"""标记"""
if is_create:
data_list = []
for group in await GroupConsole.all():
if group.block_plugin:
if modules := group.block_plugin.split(","):
block_plugin = "".join(
(f"{module}," if module.startswith("<") else f"<{module},")
for module in modules
if module.strip()
)
group.block_plugin = block_plugin.replace("<,", "")
if group.block_task:
if modules := group.block_task.split(","):
block_task = "".join(
(f"{module}," if module.startswith("<") else f"<{module},")
for module in modules
if module.strip()
)
group.block_task = block_task.replace("<,", "")
data_list.append(group)
await GroupConsole.bulk_update(data_list, ["block_plugin", "block_task"], 10)
+52 -43
View File
@@ -1,9 +1,10 @@
from nonebot.adapters import Bot, Event
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_userinfo import UserInfo, EventUserInfo
from nonebot_plugin_alconna import (
Args,
Match,
Query,
Option,
UniMsg,
@@ -17,6 +18,7 @@ from nonebot_plugin_alconna import (
)
from zhenxun.services.log import logger
from zhenxun.utils.depends import UserName
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.exception import GoodsNotFound
from zhenxun.utils.enum import BlockType, PluginType
@@ -53,8 +55,8 @@ _matcher = on_alconna(
Option("--all", action=store_true),
Subcommand("my-cost", help_text="我的金币"),
Subcommand("my-props", help_text="我的道具"),
Subcommand("buy", Args["name", str]["num", int, 1], help_text="购买道具"),
Subcommand("use", Args["name", str]["num?", int, 1], help_text="使用道具"),
Subcommand("buy", Args["name?", str]["num?", int], help_text="购买道具"),
Subcommand("use", Args["name?", str]["num?", int], help_text="使用道具"),
Subcommand("gold-list", Args["num?", int], help_text="金币排行"),
),
priority=5,
@@ -76,16 +78,16 @@ _matcher.shortcut(
)
_matcher.shortcut(
"购买道具",
"购买道具(?P<name>.*?)",
command="商店",
arguments=["buy", "{%0}"],
arguments=["buy", "{name}"],
prefix=True,
)
_matcher.shortcut(
"使用道具",
"使用道具(?P<name>.*?)",
command="商店",
arguments=["use", "{%0}"],
arguments=["use", "{name}"],
prefix=True,
)
@@ -122,14 +124,12 @@ async def _(session: EventSession, arparma: Arparma):
@_matcher.assign("my-props")
async def _(
session: EventSession, arparma: Arparma, user_info: UserInfo = EventUserInfo()
):
async def _(session: EventSession, arparma: Arparma, nickname: str = UserName()):
if session.id1:
logger.info("查看道具", arparma.header_result, session=session)
if image := await ShopManage.my_props(
session.id1,
user_info.user_displayname or user_info.user_name,
nickname,
session.platform,
):
await MessageUtils.build_message(image.pic2bytes()).finish(reply_to=True)
@@ -139,17 +139,23 @@ async def _(
@_matcher.assign("buy")
async def _(session: EventSession, arparma: Arparma, name: str, num: int):
if session.id1:
logger.info(
f"购买道具 {name}, 数量: {num}",
arparma.header_result,
session=session,
)
result = await ShopManage.buy_prop(session.id1, name, num, session.platform)
await MessageUtils.build_message(result).send(reply_to=True)
else:
await MessageUtils.build_message("用户id为空...").send(reply_to=True)
async def _(
session: Uninfo,
arparma: Arparma,
name: Match[str],
num: Query[int] = AlconnaQuery("num", 1),
):
if not name.available:
await MessageUtils.build_message(
"请在指令后跟需要购买的道具名称或id..."
).finish(reply_to=True)
logger.info(
f"购买道具 {name}, 数量: {num}",
arparma.header_result,
session=session,
)
result = await ShopManage.buy_prop(session.user.id, name.result, num.result)
await MessageUtils.build_message(result).send(reply_to=True)
@_matcher.assign("use")
@@ -159,11 +165,17 @@ async def _(
message: UniMsg,
session: EventSession,
arparma: Arparma,
name: str,
num: int,
name: Match[str],
num: Query[int] = AlconnaQuery("num", 1),
):
if not name.available:
await MessageUtils.build_message(
"请在指令后跟需要使用的道具名称或id..."
).finish(reply_to=True)
try:
result = await ShopManage.use(bot, event, session, message, name, num, "")
result = await ShopManage.use(
bot, event, session, message, name.result, num.result, ""
)
logger.info(
f"使用道具 {name}, 数量: {num}", arparma.header_result, session=session
)
@@ -179,24 +191,21 @@ async def _(
@_matcher.assign("gold-list")
async def _(
session: EventSession, arparma: Arparma, num: Query[int] = AlconnaQuery("num", 10)
session: Uninfo, arparma: Arparma, num: Query[int] = AlconnaQuery("num", 10)
):
if num.result > 50:
await MessageUtils.build_message("排行榜人数不能超过50哦...").finish()
if session.id1:
gid = session.id3 or session.id2
if not arparma.find("all") and not gid:
await MessageUtils.build_message(
"私聊中无法查看 '金币排行',请发送 '金币总排行'"
).finish()
if arparma.find("all"):
gid = None
result = await gold_rank(session.id1, gid, num.result, session.platform)
logger.info(
"查看金币排行",
arparma.header_result,
session=session,
)
await MessageUtils.build_message(result).send(reply_to=True)
else:
await MessageUtils.build_message("用户id为空...").send(reply_to=True)
gid = session.group.id if session.group else None
if not arparma.find("all") and not gid:
await MessageUtils.build_message(
"私聊中无法查看 '金币排行',请发送 '金币总排行'"
).finish()
if arparma.find("all"):
gid = None
result = await gold_rank(session, gid, num.result)
logger.info(
"查看金币排行",
arparma.header_result,
session=session,
)
await MessageUtils.build_message(result).send(reply_to=True)
+23 -10
View File
@@ -6,6 +6,7 @@ from types import MappingProxyType
from collections.abc import Callable
from nonebot.adapters import Bot, Event
from nonebot_plugin_uninfo import Uninfo
from pydantic import BaseModel, create_model
from nonebot_plugin_session import EventSession
from nonebot_plugin_alconna import UniMsg, UniMessage
@@ -58,9 +59,9 @@ class Goods(BaseModel):
class ShopParam(BaseModel):
goods_name: str
"""商品名称"""
user_id: int
user_id: str
"""用户id"""
group_id: int
group_id: str | None
"""群聊id"""
bot: Any
"""bot"""
@@ -80,16 +81,21 @@ class ShopParam(BaseModel):
"""UniMessage"""
async def gold_rank(user_id: str, group_id: str | None, num: int, platform: str):
async def gold_rank(
session: Uninfo, group_id: str | None, num: int
) -> BuildImage | str:
query = UserConsole
if group_id:
uid_list = await GroupInfoUser.filter(group_id=group_id).values_list(
"user_id", flat=True
)
query = query.filter(user_id__in=uid_list)
if uid_list:
query = query.filter(user_id__in=uid_list)
user_list = await query.annotate().order_by("-gold").values_list("user_id", "gold")
if not user_list:
return "当前还没有人拥有金币哦..."
user_id_list = [user[0] for user in user_list]
index = user_id_list.index(user_id) + 1
index = user_id_list.index(session.user.id) + 1
user_list = user_list[:num] if num < len(user_list) else user_list
friend_user = await FriendUser.filter(user_id__in=user_id_list).values_list(
"user_id", "user_name"
@@ -103,8 +109,11 @@ async def gold_rank(user_id: str, group_id: str | None, num: int, platform: str)
uid2name[g[0]] = g[1]
column_name = ["排名", "-", "名称", "金币", "平台"]
data_list = []
platform = PlatformUtils.get_platform(session)
for i, user in enumerate(user_list):
ava_bytes = await PlatformUtils.get_user_avatar(user[0], platform)
ava_bytes = await PlatformUtils.get_user_avatar(
user[0], platform, session.self_id
)
data_list.append(
[
f"{i+1}",
@@ -294,9 +303,12 @@ class ShopManage:
str | MessageFactory | None: 使用完成后返回信息
"""
if goods_name.isdigit():
user = await UserConsole.get_user(user_id=session.id1) # type: ignore
uuid = list(user.props.keys())[int(goods_name)]
goods_info = await GoodsInfo.get_or_none(uuid=uuid)
try:
user = await UserConsole.get_user(user_id=session.id1) # type: ignore
uuid = list(user.props.keys())[int(goods_name)]
goods_info = await GoodsInfo.get_or_none(uuid=uuid)
except IndexError:
return "仓库中道具不存在..."
else:
goods_info = await GoodsInfo.get_or_none(goods_name=goods_name)
if not goods_info:
@@ -647,9 +659,10 @@ class ShopManage:
shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
await shop.paste(A, (20, 230))
await shop.paste(shop_logo, (450, 30))
tip = "注【通过 购买道具 序号 或者 商品名称 购买】"
await shop.text(
(
int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2),
int((1000 - shop.getsize(tip)[0]) / 2),
170,
),
"注【通过 序号 或者 商品名称 购买】",
+30 -21
View File
@@ -1,3 +1,4 @@
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_apscheduler import scheduler
@@ -115,6 +116,13 @@ _sign_matcher.shortcut(
prefix=True,
)
_sign_matcher.shortcut(
"签到排行",
command="签到",
arguments=["--list"],
prefix=True,
)
_sign_matcher.shortcut(
"好感度总排行",
command="签到",
@@ -122,43 +130,44 @@ _sign_matcher.shortcut(
prefix=True,
)
_sign_matcher.shortcut(
"签到总排行",
command="签到",
arguments=["--global", "--list"],
prefix=True,
)
@_sign_matcher.assign("$main")
async def _(session: EventSession, arparma: Arparma, nickname: str = UserName()):
if session.id1:
if path := await SignManage.sign(session, nickname):
logger.info("签到成功", arparma.header_result, session=session)
await MessageUtils.build_message(path).finish()
return MessageUtils.build_message("用户id为空...").send()
async def _(session: Uninfo, arparma: Arparma, nickname: str = UserName()):
path = await SignManage.sign(session, nickname)
logger.info("签到成功", arparma.header_result, session=session)
await MessageUtils.build_message(path).finish()
@_sign_matcher.assign("my")
async def _(session: EventSession, arparma: Arparma, nickname: str = UserName()):
if session.id1:
if image := await SignManage.sign(session, nickname, True):
logger.info("查看我的签到", arparma.header_result, session=session)
await MessageUtils.build_message(image).finish()
return MessageUtils.build_message("用户id为空...").send()
async def _(session: Uninfo, arparma: Arparma, nickname: str = UserName()):
path = await SignManage.sign(session, nickname, True)
logger.info("查看我的签到", arparma.header_result, session=session)
await MessageUtils.build_message(path).finish()
@_sign_matcher.assign("list")
async def _(
session: EventSession, arparma: Arparma, num: Query[int] = AlconnaQuery("num", 10)
session: Uninfo, arparma: Arparma, num: Query[int] = AlconnaQuery("num", 10)
):
if num.result > 50:
await MessageUtils.build_message("排行榜人数不能超过50哦...").finish()
gid = session.id3 or session.id2
gid = session.group.id if session.group else None
if not arparma.find("global") and not gid:
await MessageUtils.build_message(
"私聊中无法查看 '好感度排行',请发送 '好感度总排行'"
).finish()
if session.id1:
if arparma.find("global"):
gid = None
if image := await SignManage.rank(session.id1, num.result, gid):
logger.info("查看签到排行", arparma.header_result, session=session)
await MessageUtils.build_message(image).finish()
return MessageUtils.build_message("用户id为空...").send()
if arparma.find("global"):
gid = None
image = await SignManage.rank(session, num.result, gid)
logger.info("查看签到排行", arparma.header_result, session=session)
await MessageUtils.build_message(image).send()
@scheduler.scheduled_job(
+39 -26
View File
@@ -4,12 +4,12 @@ from pathlib import Path
from datetime import datetime
import pytz
from nonebot_plugin_session import EventSession
from nonebot_plugin_uninfo import Uninfo
from zhenxun.services.log import logger
from zhenxun.models.sign_log import SignLog
from zhenxun.models.sign_user import SignUser
from zhenxun.utils.utils import get_user_avatar
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.friend_user import FriendUser
from zhenxun.configs.path_config import IMAGE_PATH
from zhenxun.models.user_console import UserConsole
@@ -32,12 +32,12 @@ PLATFORM_PATH = {
class SignManage:
@classmethod
async def rank(
cls, user_id: str, num: int, group_id: str | None = None
) -> BuildImage: # sourcery skip: avoid-builtin-shadow
cls, session: Uninfo, num: int, group_id: str | None = None
) -> BuildImage | str: # sourcery skip: avoid-builtin-shadow
"""好感度排行
参数:
user_id: 用户id
session: Uninfo
num: 排行榜数量
group_id: 群组id
@@ -49,14 +49,17 @@ class SignManage:
user_list = await GroupInfoUser.filter(group_id=group_id).values_list(
"user_id", flat=True
)
query = query.filter(user_id__in=user_list)
if user_list:
query = query.filter(user_id__in=user_list)
user_list = (
await query.annotate()
.order_by("-impression")
.values_list("user_id", "impression", "sign_count", "platform")
)
if not user_list:
return "当前还没有人签到过哦..."
user_id_list = [user[0] for user in user_list]
index = user_id_list.index(user_id) + 1
index = user_id_list.index(session.user.id) + 1
user_list = user_list[:num] if num < len(user_list) else user_list
column_name = ["排名", "-", "名称", "好感度", "签到次数", "平台"]
friend_list = await FriendUser.filter(user_id__in=user_id_list).values_list(
@@ -70,8 +73,11 @@ class SignManage:
for g in group_user:
uid2name[g[0]] = g[1]
data_list = []
platform = PlatformUtils.get_platform(session)
for i, user in enumerate(user_list):
bytes = await get_user_avatar(user[0])
bytes = await PlatformUtils.get_user_avatar(
user[0], platform, session.self_id
)
data_list.append(
[
f"{i+1}",
@@ -92,28 +98,29 @@ class SignManage:
@classmethod
async def sign(
cls, session: EventSession, nickname: str, is_card_view: bool = False
) -> Path | None:
cls, session: Uninfo, nickname: str, is_card_view: bool = False
) -> Path:
"""签到
参数:
session: Session
session: Uninfo
nickname: 用户昵称
is_card_view: 是否展示卡片
返回:
Path: 卡片路径
"""
if not session.id1:
return None
platform = PlatformUtils.get_platform(session)
now = datetime.now(pytz.timezone("Asia/Shanghai"))
user_console = await UserConsole.get_user(session.id1, session.platform)
user_console = await UserConsole.get_user(session.user.id, platform)
user, _ = await SignUser.get_or_create(
user_id=session.id1,
defaults={"user_console": user_console, "platform": session.platform},
user_id=session.user.id,
defaults={"user_console": user_console, "platform": platform},
)
new_log = (
await SignLog.filter(user_id=session.id1).order_by("-create_time").first()
await SignLog.filter(user_id=session.user.id)
.order_by("-create_time")
.first()
)
log_time = None
if new_log:
@@ -123,7 +130,13 @@ class SignManage:
if not is_card_view and (not new_log or (log_time and log_time != now.date())):
return await cls._handle_sign_in(user, nickname, session)
return await get_card(
user, nickname, -1, user_console.gold, "", is_card_view=is_card_view
user,
session,
nickname,
-1,
user_console.gold,
"",
is_card_view=is_card_view,
)
@classmethod
@@ -131,36 +144,35 @@ class SignManage:
cls,
user: SignUser,
nickname: str,
session: EventSession,
session: Uninfo,
) -> Path:
"""签到处理
参数:
user: SignUser
nickname: 用户昵称
session: Session
session: Uninfo
返回:
Path: 卡片路径
"""
platform = PlatformUtils.get_platform(session)
impression_added = (secrets.randbelow(99) + 1) / 100
rand = random.random()
add_probability = float(user.add_probability)
specify_probability = user.specify_probability
if rand + add_probability > 0.97 or rand < specify_probability:
impression_added *= 2
await SignUser.sign(user, impression_added, session.bot_id, session.platform)
await SignUser.sign(user, impression_added, session.self_id, platform)
gold = random.randint(1, 100)
gift = random_event(float(user.impression))
if isinstance(gift, int):
gold += gift
await UserConsole.add_gold(
user.user_id, gold + gift, "sign_in", session.platform
)
await UserConsole.add_gold(user.user_id, gold + gift, "sign_in", platform)
gift = f"额外金币 +{gift}"
else:
await UserConsole.add_gold(user.user_id, gold, "sign_in", session.platform)
await UserConsole.add_props_by_name(user.user_id, gift, 1, session.platform)
await UserConsole.add_gold(user.user_id, gold, "sign_in", platform)
await UserConsole.add_props_by_name(user.user_id, gift, 1, platform)
gift += " + 1"
logger.info(
f"签到成功. score: {user.impression:.2f} "
@@ -170,6 +182,7 @@ class SignManage:
)
return await get_card(
user,
session,
nickname,
impression_added,
gold,
+14 -11
View File
@@ -7,13 +7,13 @@ from datetime import datetime
import pytz
import nonebot
from nonebot.drivers import Driver
from nonebot_plugin_uninfo import Uninfo
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.models.sign_log import SignLog
from zhenxun.models.sign_user import SignUser
from zhenxun.utils.utils import get_user_avatar
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.utils.image_utils import BuildImage
from zhenxun.utils.platform import PlatformUtils
from zhenxun.configs.config import Config, BotConfig
from zhenxun.configs.path_config import IMAGE_PATH, TEMPLATE_PATH
@@ -63,6 +63,7 @@ async def init_image():
async def get_card(
user: SignUser,
session: Uninfo,
nickname: str,
add_impression: float,
gold: int | None,
@@ -74,6 +75,7 @@ async def get_card(
参数:
user: SignUser
session: Uninfo
nickname: 用户昵称
impression: 新增的好感度
gold: 金币
@@ -99,17 +101,18 @@ async def get_card(
is_card_view = True
return (
await _generate_html_card(
user, nickname, add_impression, gold, gift, is_double, is_card_view
user, session, nickname, add_impression, gold, gift, is_double, is_card_view
)
if base_config.get("IMAGE_STYLE") == "zhenxun"
else await _generate_card(
user, nickname, add_impression, gold, gift, is_double, is_card_view
user, session, nickname, add_impression, gold, gift, is_double, is_card_view
)
)
async def _generate_card(
user: SignUser,
session: Uninfo,
nickname: str,
add_impression: float,
gold: int | None,
@@ -121,6 +124,7 @@ async def _generate_card(
参数:
user: SignUser
session: Uninfo
nickname: 用户昵称
add_impression: 新增的好感度
gold: 金币
@@ -137,7 +141,9 @@ async def _generate_card(
140,
background=SIGN_BORDER_PATH / "ava_border_01.png",
)
if user.platform == "qq" and (byt := await get_user_avatar(user.user_id)):
if session.user.avatar and (
byt := await AsyncHttpx.get_content(session.user.avatar)
):
ava = BuildImage(107, 107, background=BytesIO(byt))
else:
ava = BuildImage(107, 107, (0, 0, 0))
@@ -273,10 +279,6 @@ async def _generate_card(
await bk.paste(nickname_img, (30, 15))
await bk.paste(uid_img, (30, 85))
await bk.paste(A, (0, 150))
# await bk.text((30, 167), "Accumulative check-in for")
# _x = bk.getsize("Accumulative check-in for")[0] + sign_day_img.width + 45
# await bk.paste(sign_day_img, (398, 158))
# await bk.text((_x, 167), "days")
await bk.paste(tip_image, (10, 167))
await bk.paste(date_img, (220, 370))
await bk.paste(lik_text1_img, (220, 240))
@@ -377,6 +379,7 @@ def clear_sign_data_pic():
async def _generate_html_card(
user: SignUser,
session: Uninfo,
nickname: str,
add_impression: float,
gold: int | None,
@@ -388,6 +391,7 @@ async def _generate_html_card(
参数:
user: SignUser
session: Uninfo
nickname: 用户昵称
add_impression: 新增的好感度
gold: 金币
@@ -420,9 +424,8 @@ async def _generate_html_card(
next_impression - previous_impression
)
now = datetime.now()
ava_url = PlatformUtils.get_user_avatar_url(user.user_id, "qq")
data = {
"ava_url": ava_url,
"ava_url": session.user.avatar,
"name": nickname,
"uid": uid,
"sign_count": f"{user.sign_count}",
@@ -2,18 +2,17 @@ from datetime import datetime, timedelta
from tortoise.functions import Count
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.enum import PluginType
from zhenxun.models.statistics import Statistics
from zhenxun.utils.image_utils import BuildImage
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.echart_utils import ChartUtils
from zhenxun.utils.echart_utils.models import Barh
from zhenxun.utils.enum import PluginType
from zhenxun.utils.image_utils import BuildImage, BuildMat, MatType
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.group_member_info import GroupInfoUser
class StatisticsManage:
@classmethod
async def get_statistics(
cls,
@@ -28,12 +27,12 @@ class StatisticsManage:
if search_type == "day":
day = 1
day_type = "日"
if search_type == "week":
day = 7
day_type = "周"
if search_type == "month":
elif search_type == "month":
day = 30
day_type = "月"
elif search_type == "week":
day = 7
day_type = "周"
if day_type:
day_type += f"({day}天)"
title = ""
@@ -53,7 +52,7 @@ class StatisticsManage:
else:
title = "功能调用统计"
if is_global and not user_id:
title = "全局 " + title
title = f"全局 {title}"
return await cls.get_global_statistics(plugin_name, day, title)
if user_id:
return await cls.get_my_statistics(user_id, group_id, day, title)
@@ -76,9 +75,11 @@ class StatisticsManage:
.group_by("plugin_name")
.values_list("plugin_name", "count")
)
if not data_list:
return "统计数据为空..."
return await cls.__build_image(data_list, title)
return (
await cls.__build_image(data_list, title)
if data_list
else "统计数据为空..."
)
@classmethod
async def get_my_statistics(
@@ -95,9 +96,11 @@ class StatisticsManage:
.group_by("plugin_name")
.values_list("plugin_name", "count")
)
if not data_list:
return "统计数据为空..."
return await cls.__build_image(data_list, title)
return (
await cls.__build_image(data_list, title)
if data_list
else "统计数据为空..."
)
@classmethod
async def get_group_statistics(cls, group_id: str, day: int | None, title: str):
@@ -110,15 +113,18 @@ class StatisticsManage:
.group_by("plugin_name")
.values_list("plugin_name", "count")
)
if not data_list:
return "统计数据为空..."
return await cls.__build_image(data_list, title)
return (
await cls.__build_image(data_list, title)
if data_list
else "统计数据为空..."
)
@classmethod
async def __build_image(cls, data_list: list[tuple[str, int]], title: str):
module2count = {x[0]: x[1] for x in data_list}
plugin_info = await PluginInfo.filter(
module__in=module2count.keys(),
load_status=True,
plugin_type=PluginType.NORMAL,
).all()
x_index = []
@@ -126,5 +132,5 @@ class StatisticsManage:
for plugin in plugin_info:
x_index.append(plugin.name)
data.append(module2count.get(plugin.module, 0))
barh = Barh(data=data, category_data=x_index)
barh = Barh(data=data, category_data=x_index, title=title)
return await ChartUtils.barh(barh)
@@ -1,16 +1,18 @@
from datetime import datetime
from nonebot.adapters import Bot, Event
from nonebot.adapters.onebot.v11 import PokeNotifyEvent
from nonebot.matcher import Matcher
from nonebot.message import run_postprocessor
from nonebot.adapters import Bot, Event
from nonebot.plugin import PluginMetadata
from nonebot.message import run_postprocessor
from nonebot_plugin_session import EventSession
from nonebot_plugin_apscheduler import scheduler
from nonebot.adapters.onebot.v11 import PokeNotifyEvent
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.models.statistics import Statistics
from zhenxun.configs.utils import PluginExtraData
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.models.statistics import Statistics
from zhenxun.utils.enum import PluginType
__plugin_meta__ = PluginMetadata(
name="功能调用统计",
@@ -21,6 +23,8 @@ __plugin_meta__ = PluginMetadata(
).dict(),
)
TEMP_LIST = []
@run_postprocessor
async def _(
@@ -33,16 +37,32 @@ async def _(
if matcher.type == "notice" and not isinstance(event, PokeNotifyEvent):
"""过滤除poke外的notice"""
return
if session.id1:
plugin = await PluginInfo.get_or_none(module=matcher.module_name)
if session.id1 and matcher.plugin:
plugin = await PluginInfo.get_plugin(module_path=matcher.plugin.module_name)
plugin_type = plugin.plugin_type if plugin else None
if plugin_type == PluginType.NORMAL and matcher.plugin_name not in [
"update_info",
"statistics_handle",
]:
await Statistics.create(
user_id=session.id1,
group_id=session.id3 or session.id2,
plugin_name=matcher.plugin_name,
create_time=datetime.now(),
if plugin_type == PluginType.NORMAL:
logger.debug(f"提交调用记录: {matcher.plugin_name}...", session=session)
TEMP_LIST.append(
Statistics(
user_id=session.id1,
group_id=session.id3 or session.id2,
plugin_name=matcher.plugin_name,
create_time=datetime.now(),
bot_id=bot.self_id,
)
)
@scheduler.scheduled_job(
"interval",
minutes=1,
)
async def _():
try:
call_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if call_list:
await Statistics.bulk_create(call_list)
logger.debug(f"批量添加调用记录 {len(call_list)} 条", "定时任务")
except Exception as e:
logger.error("定时批量添加调用记录", "定时任务", e=e)
@@ -16,7 +16,6 @@ from zhenxun.utils.common_utils import CommonUtils
class BroadcastManage:
@classmethod
async def send(
cls, bot: Bot, message: UniMsg, session: EventSession
@@ -43,8 +42,9 @@ class BroadcastManage:
for group in group_list:
try:
if not await CommonUtils.task_is_block(
group.group_id,
bot,
"broadcast", # group.channel_id
group.group_id,
):
target = PlatformUtils.get_target(
bot, None, group.channel_id or group.group_id
@@ -1,22 +1,201 @@
from datetime import datetime, timedelta
import nonebot
from nonebot import require
from fastapi import APIRouter
from nonebot.config import Config
from tortoise.functions import Count
from tortoise.expressions import RawSQL
from fastapi.responses import JSONResponse
from zhenxun.models.statistics import Statistics
from zhenxun.models.chat_history import ChatHistory
from zhenxun.models.bot_connect_log import BotConnectLog
from ....base_model import Result
from .data_source import BotManage
from ....utils import authentication
from ....base_model import Result, QueryModel, BaseResultModel
from .model import BotInfo, ChatCallMonthCount, QueryChatCallCount, AllChatAndCallCount
require("plugin_store")
router = APIRouter(prefix="/dashboard")
driver = nonebot.get_driver()
@router.get(
"/get_bot_list",
dependencies=[authentication()],
response_model=Result[list[BotInfo]],
response_class=JSONResponse,
deprecated="获取bot列表", # type: ignore
)
async def _() -> Result:
async def _() -> Result[list[BotInfo]]:
try:
return Result.ok(await BotManage.get_bot_list(), "拿到信息啦!")
except Exception as e:
return Result.fail(f"发生了一点错误捏 {type(e)}: {e}")
@router.get(
"/get_chat_and_call_count",
dependencies=[authentication()],
response_model=Result[QueryChatCallCount],
response_class=JSONResponse,
description="获取聊天/调用记录的全部和今日数量",
)
async def _(bot_id: str | None = None) -> Result[QueryChatCallCount]:
now = datetime.now()
query = ChatHistory
if bot_id:
query = query.filter(bot_id=bot_id)
chat_all_count = await query.annotate().count()
chat_day_count = await query.filter(
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
).count()
query = Statistics
if bot_id:
query = query.filter(bot_id=bot_id)
call_all_count = await query.annotate().count()
call_day_count = await query.filter(
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
).count()
return Result.ok(
QueryChatCallCount(
chat_num=chat_all_count,
chat_day=chat_day_count,
call_num=call_all_count,
call_day=call_day_count,
)
)
@router.get(
"/get_all_chat_and_call_count",
dependencies=[authentication()],
response_model=Result[AllChatAndCallCount],
response_class=JSONResponse,
description="获取聊天/调用记录的全部数据次数",
)
async def _(bot_id: str | None = None) -> Result[AllChatAndCallCount]:
now = datetime.now()
query = ChatHistory
if bot_id:
query = query.filter(bot_id=bot_id)
chat_week_count = await query.filter(
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
).count()
chat_month_count = await query.filter(
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
).count()
chat_year_count = await query.filter(
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
).count()
query = Statistics
if bot_id:
query = query.filter(bot_id=bot_id)
call_week_count = await query.filter(
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
).count()
call_month_count = await query.filter(
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
).count()
call_year_count = await query.filter(
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
).count()
return Result.ok(
AllChatAndCallCount(
chat_week=chat_week_count,
chat_month=chat_month_count,
chat_year=chat_year_count,
call_week=call_week_count,
call_month=call_month_count,
call_year=call_year_count,
)
)
@router.get(
"/get_chat_and_call_month",
dependencies=[authentication()],
response_model=Result[ChatCallMonthCount],
response_class=JSONResponse,
deprecated="获取聊天/调用记录的一个月数量", # type: ignore
)
async def _(bot_id: str | None = None) -> Result[ChatCallMonthCount]:
now = datetime.now()
filter_date = now - timedelta(days=30, hours=now.hour, minutes=now.minute)
chat_query = ChatHistory
call_query = Statistics
if bot_id:
chat_query = chat_query.filter(bot_id=bot_id)
call_query = call_query.filter(bot_id=bot_id)
chat_date_list = (
await chat_query.filter(create_time__gte=filter_date)
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
.group_by("date")
.values("date", "count")
)
call_date_list = (
await call_query.filter(create_time__gte=filter_date)
.annotate(date=RawSQL("DATE(create_time)"), count=Count("id"))
.group_by("date")
.values("date", "count")
)
date_list = []
chat_count_list = []
call_count_list = []
chat_date2cnt = {str(date["date"]): date["count"] for date in chat_date_list}
call_date2cnt = {str(date["date"]): date["count"] for date in call_date_list}
date = now.date()
for _ in range(30):
if str(date) in chat_date2cnt:
chat_count_list.append(chat_date2cnt[str(date)])
else:
chat_count_list.append(0)
if str(date) in call_date2cnt:
call_count_list.append(call_date2cnt[str(date)])
else:
call_count_list.append(0)
date_list.append(str(date)[5:])
date -= timedelta(days=1)
chat_count_list.reverse()
call_count_list.reverse()
date_list.reverse()
return Result.ok(
ChatCallMonthCount(chat=chat_count_list, call=call_count_list, date=date_list)
)
@router.post(
"/get_connect_log",
dependencies=[authentication()],
response_model=Result[BaseResultModel],
response_class=JSONResponse,
deprecated="获取Bot连接记录", # type: ignore
)
async def _(query: QueryModel) -> Result[BaseResultModel]:
total = await BotConnectLog.all().count()
if total % query.size:
total += 1
data = (
await BotConnectLog.all()
.order_by("-id")
.offset((query.index - 1) * query.size)
.limit(query.size)
)
for v in data:
v.connect_time = v.connect_time.replace(tzinfo=None).replace(microsecond=0)
return Result.ok(BaseResultModel(total=total, data=data))
@router.get(
"/get_nonebot_config",
dependencies=[authentication()],
response_model=Result[Config],
response_class=JSONResponse,
deprecated="获取nb配置", # type: ignore
)
async def _() -> Result[Config]:
return Result.ok(driver.config)
@@ -49,11 +49,12 @@ class BotManage:
)
group_list, _ = await PlatformUtils.get_group_list(bot)
group_list = [g for g in group_list if g.channel_id is None]
friend_list = await PlatformUtils.get_friend_list(bot)
friend_list, _ = await PlatformUtils.get_friend_list(bot)
bot_info.group_count = len(group_list)
bot_info.friend_count = len(friend_list)
bot_info.day_call = await Statistics.filter(
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute),
bot_id=bot.self_id,
).count()
bot_info.received_messages = await ChatHistory.filter(
bot_id=bot_info.self_id,
@@ -62,10 +63,7 @@ class BotManage:
bot_info.connect_time = bot_live.get(bot.self_id) or 0
if bot_info.connect_time:
connect_date = datetime.fromtimestamp(CONNECT_TIME)
connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S")
bot_info.connect_date = datetime.strptime(
connect_date_str, "%Y-%m-%d %H:%M:%S"
)
bot_info.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
return bot_info
@classmethod
@@ -1,5 +1,3 @@
from datetime import datetime
from pydantic import BaseModel
@@ -22,5 +20,52 @@ class BotInfo(BaseModel):
"""今日调用插件次数"""
connect_time: int = 0
"""连接时间"""
connect_date: datetime | None = None
connect_date: str | None = None
"""连接日期"""
class QueryChatCallCount(BaseModel):
"""
查询聊天/调用记录次数
"""
chat_num: int
"""聊天记录总数"""
chat_day: int
"""今日消息"""
call_num: int
"""调用记录总数"""
call_day: int
"""今日调用"""
class ChatCallMonthCount(BaseModel):
"""
查询聊天/调用一个月记录次数
"""
chat: list[int]
"""一个月内聊天总数"""
call: list[int]
"""一个月内调用数据"""
date: list[str]
"""日期"""
class AllChatAndCallCount(BaseModel):
"""
查询聊天/调用记录次数
"""
chat_week: int
"""一周内聊天次数"""
chat_month: int
"""一月内聊天次数"""
chat_year: int
"""一年内聊天次数"""
call_week: int
"""一周内调用次数"""
call_month: int
"""一月内调用次数"""
call_year: int
"""一年内调用次数"""
@@ -2,6 +2,7 @@ import nonebot
from tortoise import Tortoise
from nonebot.drivers import Driver
from fastapi import Request, APIRouter
from fastapi.responses import JSONResponse
from tortoise.exceptions import OperationalError
from zhenxun.models.task_info import TaskInfo
@@ -62,26 +63,40 @@ async def _():
@router.get(
"/get_table_list", dependencies=[authentication()], description="获取数据库表"
"/get_table_list",
dependencies=[authentication()],
response_model=Result[list[dict]],
response_class=JSONResponse,
description="获取数据库表",
)
async def _() -> Result:
async def _() -> Result[list[dict]]:
db = Tortoise.get_connection("default")
query = await db.execute_query_dict(SELECT_TABLE_SQL)
return Result.ok(query)
@router.get(
"/get_table_column", dependencies=[authentication()], description="获取表字段"
"/get_table_column",
dependencies=[authentication()],
response_model=Result[list[dict]],
response_class=JSONResponse,
description="获取表字段",
)
async def _(table_name: str) -> Result:
async def _(table_name: str) -> Result[list[dict]]:
db = Tortoise.get_connection("default")
# print(SELECT_TABLE_COLUMN_SQL.format(table_name))
query = await db.execute_query_dict(SELECT_TABLE_COLUMN_SQL.format(table_name))
return Result.ok(query)
@router.post("/exec_sql", dependencies=[authentication()], description="执行sql")
async def _(sql: SqlText, request: Request) -> Result:
@router.post(
"/exec_sql",
dependencies=[authentication()],
response_model=Result[list[dict]],
response_class=JSONResponse,
description="执行sql",
)
async def _(sql: SqlText, request: Request) -> Result[list[dict]]:
ip = request.client.host if request.client else "unknown"
try:
if sql.sql.lower().startswith("select"):
@@ -98,8 +113,14 @@ async def _(sql: SqlText, request: Request) -> Result:
return Result.warning_(f"sql执行错误: {e}")
@router.post("/get_sql_log", dependencies=[authentication()], description="sql日志列表")
async def _(query: QueryModel) -> Result:
@router.post(
"/get_sql_log",
dependencies=[authentication()],
response_model=Result[BaseResultModel],
response_class=JSONResponse,
description="sql日志列表",
)
async def _(query: QueryModel) -> Result[BaseResultModel]:
total = await SqlLog.all().count()
if total % query.size:
total += 1
@@ -112,8 +133,14 @@ async def _(query: QueryModel) -> Result:
return Result.ok(BaseResultModel(total=total, data=data))
@router.get("/get_common_sql", dependencies=[authentication()], description="常用sql")
async def _(plugin_name: str | None = None) -> Result:
@router.get(
"/get_common_sql",
dependencies=[authentication()],
response_model=Result[dict],
response_class=JSONResponse,
description="常用sql",
)
async def _(plugin_name: str | None = None) -> Result[dict]:
if plugin_name:
return Result.ok(SQL_DICT.get(plugin_name))
return Result.ok(str(SQL_DICT))
@@ -6,31 +6,48 @@ from datetime import datetime, timedelta
import nonebot
from fastapi import APIRouter
from nonebot.config import Config
from tortoise.functions import Count
from fastapi.responses import JSONResponse
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
from zhenxun.services.log import logger
from zhenxun.models.group_info import GroupInfo
from zhenxun.models.statistics import Statistics
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.models.chat_history import ChatHistory
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.bot_connect_log import BotConnectLog
from ....base_model import Result
from .data_source import bot_live
from ....utils import authentication, get_system_status
from ....config import AVA_URL, GROUP_AVA_URL, QueryDateType
from .model import BaseInfo, HotPlugin, ActiveGroup, ChatHistoryCount
from .model import (
BaseInfo,
HotPlugin,
QueryCount,
ActiveGroup,
NonebotData,
TemplateBaseInfo,
)
driver = nonebot.get_driver()
run_time = time.time()
ws_router = APIRouter()
router = APIRouter(prefix="/main")
@router.get("/get_base_info", dependencies=[authentication()], description="基础信息")
async def _(bot_id: str | None = None) -> Result:
@router.get(
"/get_base_info",
dependencies=[authentication()],
response_model=Result[list[BaseInfo]],
response_class=JSONResponse,
description="基础信息",
)
async def _(bot_id: str | None = None) -> Result[list[BaseInfo]]:
"""获取Bot基础信息
参数:
@@ -39,13 +56,14 @@ async def _(bot_id: str | None = None) -> Result:
返回:
Result: 获取指定bot信息与bot列表
"""
bot_list: list[BaseInfo] = []
global run_time
bot_list: list[TemplateBaseInfo] = []
if bots := nonebot.get_bots():
select_bot: BaseInfo
for key, bot in bots.items():
for _, bot in bots.items():
login_info = await bot.get_login_info()
bot_list.append(
BaseInfo(
TemplateBaseInfo(
bot=bot, # type: ignore
self_id=bot.self_id,
nickname=login_info["nickname"],
@@ -58,7 +76,6 @@ async def _(bot_id: str | None = None) -> Result:
else:
select_bot = bot_list[0]
select_bot.is_select = True
select_bot.config = select_bot.bot.config
now = datetime.now()
# 今日累计接收消息
select_bot.received_messages = await ChatHistory.filter(
@@ -82,10 +99,7 @@ async def _(bot_id: str | None = None) -> Result:
select_bot.connect_time = bot_live.get(select_bot.self_id) or 0
if select_bot.connect_time:
connect_date = datetime.fromtimestamp(select_bot.connect_time)
connect_date_str = connect_date.strftime("%Y-%m-%d %H:%M:%S")
select_bot.connect_date = datetime.strptime(
connect_date_str, "%Y-%m-%d %H:%M:%S"
)
select_bot.connect_date = connect_date.strftime("%Y-%m-%d %H:%M:%S")
version_file = Path() / "__version__"
if version_file.exists():
if text := version_file.open().read():
@@ -95,30 +109,40 @@ async def _(bot_id: str | None = None) -> Result:
create_time__gte=now - timedelta(hours=now.hour)
).count()
select_bot.day_call = day_call
return Result.ok(bot_list, "拿到信息啦!")
select_bot.connect_count = await BotConnectLog.filter(
bot_id=select_bot.self_id
).count()
return Result.ok([BaseInfo(**e.dict()) for e in bot_list], "拿到信息啦!")
return Result.warning_("无Bot连接...")
@router.get(
"/get_all_ch_count", dependencies=[authentication()], description="获取接收消息数量"
"/get_all_chat_count",
dependencies=[authentication()],
response_model=Result[QueryCount],
response_class=JSONResponse,
description="获取接收消息数量",
)
async def _(bot_id: str) -> Result:
async def _(bot_id: str | None = None) -> Result[QueryCount]:
now = datetime.now()
all_count = await ChatHistory.filter(bot_id=bot_id).count()
day_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour)
query = ChatHistory
if bot_id:
query = query.filter(bot_id=bot_id)
all_count = await query.annotate().count()
day_count = await query.filter(
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
).count()
week_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=7)
week_count = await query.filter(
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
).count()
month_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=30)
month_count = await query.filter(
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
).count()
year_count = await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=365)
year_count = await query.filter(
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
).count()
return Result.ok(
ChatHistoryCount(
QueryCount(
num=all_count,
day=day_count,
week=week_count,
@@ -129,44 +153,49 @@ async def _(bot_id: str) -> Result:
@router.get(
"/get_ch_count", dependencies=[authentication()], description="获取接收消息数量"
"/get_all_call_count",
dependencies=[authentication()],
response_model=Result[QueryCount],
response_class=JSONResponse,
description="获取调用次数",
)
async def _(bot_id: str, query_type: QueryDateType | None = None) -> Result:
if nonebot.get_bot(bot_id):
if not query_type:
return Result.ok(await ChatHistory.filter(bot_id=bot_id).count())
now = datetime.now()
if query_type == QueryDateType.DAY:
return Result.ok(
await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(hours=now.hour)
).count()
)
if query_type == QueryDateType.WEEK:
return Result.ok(
await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=7)
).count()
)
if query_type == QueryDateType.MONTH:
return Result.ok(
await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=30)
).count()
)
if query_type == QueryDateType.YEAR:
return Result.ok(
await ChatHistory.filter(
bot_id=bot_id, create_time__gte=now - timedelta(days=365)
).count()
)
return Result.warning_("无Bot连接...")
async def _(bot_id: str | None = None) -> Result[QueryCount]:
now = datetime.now()
query = Statistics
if bot_id:
query = query.filter(bot_id=bot_id)
all_count = await query.annotate().count()
day_count = await query.filter(
create_time__gte=now - timedelta(hours=now.hour, minutes=now.minute)
).count()
week_count = await query.filter(
create_time__gte=now - timedelta(days=7, hours=now.hour, minutes=now.minute)
).count()
month_count = await query.filter(
create_time__gte=now - timedelta(days=30, hours=now.hour, minutes=now.minute)
).count()
year_count = await query.filter(
create_time__gte=now - timedelta(days=365, hours=now.hour, minutes=now.minute)
).count()
return Result.ok(
QueryCount(
num=all_count,
day=day_count,
week=week_count,
month=month_count,
year=year_count,
)
)
@router.get(
"get_fg_count", dependencies=[authentication()], description="好友/群组数量"
"get_fg_count",
dependencies=[authentication()],
response_model=Result[dict[str, int]],
response_class=JSONResponse,
description="好友/群组数量",
)
async def _(bot_id: str) -> Result:
async def _(bot_id: str) -> Result[dict[str, int]]:
if bots := nonebot.get_bots():
if bot_id not in bots:
return Result.warning_("指定Bot未连接...")
@@ -183,26 +212,60 @@ async def _(bot_id: str) -> Result:
@router.get(
"/get_run_time", dependencies=[authentication()], description="获取nb运行时间"
"/get_nb_data",
dependencies=[authentication()],
response_model=Result[NonebotData],
response_class=JSONResponse,
description="获取nb数据",
)
async def _() -> Result:
return Result.ok(int(time.time() - run_time))
async def _() -> Result[NonebotData]:
return Result.ok(NonebotData(config=driver.config, run_time=int(run_time)))
@router.get(
"/get_active_group", dependencies=[authentication()], description="获取活跃群聊"
"/get_nb_config",
dependencies=[authentication()],
response_model=Result[Config],
response_class=JSONResponse,
description="获取nb配置",
)
async def _(date_type: QueryDateType | None = None) -> Result:
async def _() -> Result[Config]:
return Result.ok(driver.config)
@router.get(
"/get_run_time",
dependencies=[authentication()],
response_model=Result[int],
response_class=JSONResponse,
description="获取nb运行时间",
)
async def _() -> Result[int]:
return Result.ok(int(run_time))
@router.get(
"/get_active_group",
dependencies=[authentication()],
response_model=Result[list[ActiveGroup]],
response_class=JSONResponse,
description="获取活跃群聊",
)
async def _(
date_type: QueryDateType | None = None, bot_id: str | None = None
) -> Result[list[ActiveGroup]]:
query = ChatHistory
now = datetime.now()
if bot_id:
query = query.filter(bot_id=bot_id)
if date_type == QueryDateType.DAY:
query = ChatHistory.filter(create_time__gte=now - timedelta(hours=now.hour))
query = query.filter(create_time__gte=now - timedelta(hours=now.hour))
if date_type == QueryDateType.WEEK:
query = ChatHistory.filter(create_time__gte=now - timedelta(days=7))
query = query.filter(create_time__gte=now - timedelta(days=7))
if date_type == QueryDateType.MONTH:
query = ChatHistory.filter(create_time__gte=now - timedelta(days=30))
query = query.filter(create_time__gte=now - timedelta(days=30))
if date_type == QueryDateType.YEAR:
query = ChatHistory.filter(create_time__gte=now - timedelta(days=365))
query = query.filter(create_time__gte=now - timedelta(days=365))
data_list = (
await query.annotate(count=Count("id"))
.filter(group_id__not_isnull=True)
@@ -213,7 +276,7 @@ async def _(date_type: QueryDateType | None = None) -> Result:
)
id2name = {}
if data_list:
if info_list := await GroupInfo.filter(
if info_list := await GroupConsole.filter(
group_id__in=[x[0] for x in data_list]
).all():
for group_info in info_list:
@@ -236,19 +299,27 @@ async def _(date_type: QueryDateType | None = None) -> Result:
@router.get(
"/get_hot_plugin", dependencies=[authentication()], description="获取热门插件"
"/get_hot_plugin",
dependencies=[authentication()],
response_model=Result[list[HotPlugin]],
response_class=JSONResponse,
description="获取热门插件",
)
async def _(date_type: QueryDateType | None = None) -> Result:
async def _(
date_type: QueryDateType | None = None, bot_id: str | None = None
) -> Result[list[HotPlugin]]:
query = Statistics
now = datetime.now()
if bot_id:
query = query.filter(bot_id=bot_id)
if date_type == QueryDateType.DAY:
query = Statistics.filter(create_time__gte=now - timedelta(hours=now.hour))
query = query.filter(create_time__gte=now - timedelta(hours=now.hour))
if date_type == QueryDateType.WEEK:
query = Statistics.filter(create_time__gte=now - timedelta(days=7))
query = query.filter(create_time__gte=now - timedelta(days=7))
if date_type == QueryDateType.MONTH:
query = Statistics.filter(create_time__gte=now - timedelta(days=30))
query = query.filter(create_time__gte=now - timedelta(days=30))
if date_type == QueryDateType.YEAR:
query = Statistics.filter(create_time__gte=now - timedelta(days=365))
query = query.filter(create_time__gte=now - timedelta(days=365))
data_list = (
await query.annotate(count=Count("id"))
.group_by("plugin_name")
@@ -281,4 +352,3 @@ async def system_logs_realtime(websocket: WebSocket, sleep: int = 5):
system_status = await get_system_status()
await websocket.send_text(system_status.json())
await asyncio.sleep(sleep)
return
@@ -1,8 +1,8 @@
import time
import nonebot
from nonebot.adapters.onebot.v11 import Bot
from nonebot.drivers import Driver
from nonebot.adapters.onebot.v11 import Bot
driver: Driver = nonebot.get_driver()
@@ -1,8 +1,6 @@
from datetime import datetime
from pydantic import BaseModel
from nonebot.adapters import Bot
from nonebot.config import Config
from pydantic import BaseModel
class SystemStatus(BaseModel):
@@ -20,8 +18,6 @@ class BaseInfo(BaseModel):
基础信息
"""
bot: Bot
"""Bot"""
self_id: str
"""SELF ID"""
nickname: str
@@ -36,8 +32,10 @@ class BaseInfo(BaseModel):
"""今日 累计接收消息"""
connect_time: int = 0
"""连接时间"""
connect_date: datetime | None = None
connect_date: str | None = None
"""连接日期"""
connect_count: int = 0
"""连接次数"""
plugin_count: int = 0
"""加载插件数量"""
@@ -48,9 +46,6 @@ class BaseInfo(BaseModel):
is_select: bool = False
"""当前选择"""
config: Config | None = None
"""nb配置"""
day_call: int = 0
"""今日调用插件次数"""
version: str = "unknown"
@@ -60,7 +55,16 @@ class BaseInfo(BaseModel):
arbitrary_types_allowed = True
class ChatHistoryCount(BaseModel):
class TemplateBaseInfo(BaseInfo):
"""
基础信息
"""
bot: Bot
"""bot"""
class QueryCount(BaseModel):
"""
聊天记录数量
"""
@@ -103,3 +107,10 @@ class HotPlugin(BaseModel):
"""插件名称"""
count: int
"""调用次数"""
class NonebotData(BaseModel):
config: Config
"""nb配置"""
run_time: int
"""运行时间"""
@@ -1,6 +1,7 @@
import nonebot
from fastapi import APIRouter
from tortoise.functions import Count
from fastapi.responses import JSONResponse
from nonebot.adapters.onebot.v11 import ActionFailed
from zhenxun.services.log import logger
@@ -41,7 +42,11 @@ router = APIRouter(prefix="/manage")
@router.get(
"/get_group_list", dependencies=[authentication()], description="获取群组列表"
"/get_group_list",
dependencies=[authentication()],
response_model=Result[list[GroupResult]],
response_class=JSONResponse,
description="获取群组列表",
)
async def _(bot_id: str) -> Result:
"""
@@ -65,9 +70,13 @@ async def _(bot_id: str) -> Result:
@router.post(
"/update_group", dependencies=[authentication()], description="修改群组信息"
"/update_group",
dependencies=[authentication()],
response_model=Result[str],
response_class=JSONResponse,
description="修改群组信息",
)
async def _(group: UpdateGroup) -> Result:
async def _(group: UpdateGroup) -> Result[str]:
try:
group_id = group.group_id
if db_group := await GroupConsole.get_group(group_id):
@@ -75,9 +84,11 @@ async def _(group: UpdateGroup) -> Result:
db_group.level = group.level
db_group.status = group.status
if group.close_plugins:
group.close_plugins = [f"<{module}" for module in group.close_plugins]
db_group.block_plugin = ",".join(group.close_plugins) + ","
if group.task:
if block_task := [t for t in task_list if t not in group.task]:
block_task = [f"<{module}" for module in block_task]
db_group.block_task = ",".join(block_task) + "," # type: ignore
await db_group.save(
update_fields=["level", "status", "block_plugin", "block_task"]
@@ -89,9 +100,13 @@ async def _(group: UpdateGroup) -> Result:
@router.get(
"/get_friend_list", dependencies=[authentication()], description="获取好友列表"
"/get_friend_list",
dependencies=[authentication()],
response_model=Result[list[Friend]],
response_class=JSONResponse,
description="获取好友列表",
)
async def _(bot_id: str) -> Result:
async def _(bot_id: str) -> Result[list[Friend]]:
"""
获取群信息
"""
@@ -116,9 +131,13 @@ async def _(bot_id: str) -> Result:
@router.get(
"/get_request_count", dependencies=[authentication()], description="获取请求数量"
"/get_request_count",
dependencies=[authentication()],
response_model=Result[dict[str, int]],
response_class=JSONResponse,
description="获取请求数量",
)
async def _() -> Result:
async def _() -> Result[dict[str, int]]:
f_count = await FgRequest.filter(
request_type=RequestType.FRIEND, handle_type__isnull=True
).count()
@@ -133,9 +152,13 @@ async def _() -> Result:
@router.get(
"/get_request_list", dependencies=[authentication()], description="获取请求列表"
"/get_request_list",
dependencies=[authentication()],
response_model=Result[ReqResult],
response_class=JSONResponse,
description="获取请求列表",
)
async def _() -> Result:
async def _() -> Result[ReqResult]:
try:
req_result = ReqResult()
data_list = await FgRequest.filter(handle_type__isnull=True).all()
@@ -177,7 +200,11 @@ async def _() -> Result:
@router.post(
"/clear_request", dependencies=[authentication()], description="清空请求列表"
"/clear_request",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="清空请求列表",
)
async def _(cr: ClearRequest) -> Result:
await FgRequest.filter(
@@ -186,7 +213,13 @@ async def _(cr: ClearRequest) -> Result:
return Result.ok(info="成功清除了数据!")
@router.post("/refuse_request", dependencies=[authentication()], description="拒绝请求")
@router.post(
"/refuse_request",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="拒绝请求",
)
async def _(parma: HandleRequest) -> Result:
try:
if bots := nonebot.get_bots():
@@ -207,14 +240,24 @@ async def _(parma: HandleRequest) -> Result:
return Result.fail(f"{type(e)}: {e}")
@router.post("/delete_request", dependencies=[authentication()], description="忽略请求")
@router.post(
"/delete_request",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="忽略请求",
)
async def _(parma: HandleRequest) -> Result:
await FgRequest.ignore(parma.id)
return Result.ok(info="成功处理了请求!")
@router.post(
"/approve_request", dependencies=[authentication()], description="同意请求"
"/approve_request",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="同意请求",
)
async def _(parma: HandleRequest) -> Result:
try:
@@ -245,7 +288,13 @@ async def _(parma: HandleRequest) -> Result:
return Result.fail(f"{type(e)}: {e}")
@router.post("/leave_group", dependencies=[authentication()], description="退群")
@router.post(
"/leave_group",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="退群",
)
async def _(param: LeaveGroup) -> Result:
try:
if bots := nonebot.get_bots():
@@ -264,7 +313,13 @@ async def _(param: LeaveGroup) -> Result:
return Result.fail(f"{type(e)}: {e}")
@router.post("/delete_friend", dependencies=[authentication()], description="删除好友")
@router.post(
"/delete_friend",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="删除好友",
)
async def _(param: DeleteFriend) -> Result:
try:
if bots := nonebot.get_bots():
@@ -284,9 +339,13 @@ async def _(param: DeleteFriend) -> Result:
@router.get(
"/get_friend_detail", dependencies=[authentication()], description="获取好友详情"
"/get_friend_detail",
dependencies=[authentication()],
response_model=Result[UserDetail],
response_class=JSONResponse,
description="获取好友详情",
)
async def _(bot_id: str, user_id: str) -> Result:
async def _(bot_id: str, user_id: str) -> Result[UserDetail]:
if bots := nonebot.get_bots():
if bot_id in bots:
if fd := [
@@ -327,9 +386,13 @@ async def _(bot_id: str, user_id: str) -> Result:
@router.get(
"/get_group_detail", dependencies=[authentication()], description="获取群组详情"
"/get_group_detail",
dependencies=[authentication()],
response_model=Result[GroupDetail],
response_class=JSONResponse,
description="获取群组详情",
)
async def _(bot_id: str, group_id: str) -> Result:
async def _(bot_id: str, group_id: str) -> Result[GroupDetail]:
if not (bots := nonebot.get_bots()):
return Result.warning_("无Bot连接...")
if bot_id not in bots:
@@ -346,34 +409,46 @@ async def _(bot_id: str, group_id: str) -> Result:
.values_list("plugin_name", "count")
)
like_plugin = {}
plugins = await PluginInfo.all()
plugins = await PluginInfo.get_plugins()
module2name = {p.module: p.name for p in plugins}
for data in like_plugin_list:
name = module2name.get(data[0]) or data[0]
like_plugin[name] = data[1]
close_plugins = []
close_plugins: list[Plugin] = []
if group.block_plugin:
for module in group.block_plugin.split(","):
module_ = module.replace(":super", "")
is_super_block = module.endswith(":super")
plugin = Plugin(
module=module_,
plugin_name=module,
is_super_block=is_super_block,
)
plugin.plugin_name = module2name.get(module) or module
close_plugins.append(plugin)
for module in group.block_plugin.replace("<", "").split(","):
if module:
plugin = Plugin(
module=module,
plugin_name=module,
is_super_block=False,
)
plugin.plugin_name = module2name.get(module) or module
close_plugins.append(plugin)
exists_modules = [p.module for p in close_plugins]
if group.superuser_block_plugin:
for module in group.superuser_block_plugin.replace("<", "").split(","):
if module and module not in exists_modules:
plugin = Plugin(
module=module,
plugin_name=module,
is_super_block=True,
)
plugin.plugin_name = module2name.get(module) or module
close_plugins.append(plugin)
all_task = await TaskInfo.annotate().values_list("module", "name")
task_module2name = {x[0]: x[1] for x in all_task}
task_list = []
if group.block_task:
split_task = group.block_task.split(",")
if group.block_task or group.superuser_block_plugin:
sbp = group.superuser_block_plugin.replace("<", "").split(",")
split_task = group.block_task.replace("<", "").split(",")
for task in all_task:
task_list.append(
Task(
name=task[0],
zh_name=task_module2name.get(task[0]) or task[0],
status=task[0] not in split_task,
status=task[0] not in split_task and task[0] not in sbp,
is_super_block=task[0] in sbp,
)
)
else:
@@ -383,6 +458,7 @@ async def _(bot_id: str, group_id: str) -> Result:
name=task[0],
zh_name=task_module2name.get(task[0]) or task[0],
status=True,
is_super_block=False,
)
)
group_detail = GroupDetail(
@@ -403,7 +479,11 @@ async def _(bot_id: str, group_id: str) -> Result:
@router.post(
"/send_message", dependencies=[authentication()], description="获取群组详情"
"/send_message",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="获取群组详情",
)
async def _(param: SendMessage) -> Result:
if not (bots := nonebot.get_bots()):
@@ -29,6 +29,8 @@ class Task(BaseModel):
"""被动中文名称"""
status: bool
"""状态"""
is_super_block: bool
"""是否超级用户禁用"""
class Plugin(BaseModel):
@@ -2,6 +2,7 @@ import re
import cattrs
from fastapi import Query, APIRouter
from fastapi.responses import JSONResponse
from zhenxun.services.log import logger
from zhenxun.configs.config import Config
@@ -25,11 +26,13 @@ router = APIRouter(prefix="/plugin")
@router.get(
"/get_plugin_list",
dependencies=[authentication()],
response_model=Result[list[PluginInfo]],
response_class=JSONResponse,
deprecated="获取插件列表", # type: ignore
)
async def _(
plugin_type: list[PluginType] = Query(None), menu_type: str | None = None
) -> Result:
) -> Result[list[PluginInfo]]:
try:
plugin_list: list[PluginInfo] = []
query = DbPluginInfo
@@ -61,9 +64,11 @@ async def _(
@router.get(
"/get_plugin_count",
dependencies=[authentication()],
response_model=Result[int],
response_class=JSONResponse,
deprecated="获取插件数量", # type: ignore
)
async def _() -> Result:
async def _() -> Result[int]:
plugin_count = PluginCount()
plugin_count.normal = await DbPluginInfo.filter(
plugin_type=PluginType.NORMAL, load_status=True
@@ -82,7 +87,11 @@ async def _() -> Result:
@router.post(
"/update_plugin", dependencies=[authentication()], description="更新插件参数"
"/update_plugin",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="更新插件参数",
)
async def _(plugin: UpdatePlugin) -> Result:
try:
@@ -113,7 +122,13 @@ async def _(plugin: UpdatePlugin) -> Result:
return Result.ok(info="已经帮你写好啦!")
@router.post("/change_switch", dependencies=[authentication()], description="开关插件")
@router.post(
"/change_switch",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="开关插件",
)
async def _(param: PluginSwitch) -> Result:
db_plugin = await DbPluginInfo.get_or_none(module=param.module, load_status=True)
if not db_plugin:
@@ -129,9 +144,13 @@ async def _(param: PluginSwitch) -> Result:
@router.get(
"/get_plugin_menu_type", dependencies=[authentication()], description="获取插件类型"
"/get_plugin_menu_type",
dependencies=[authentication()],
response_model=Result[list[str]],
response_class=JSONResponse,
description="获取插件类型",
)
async def _() -> Result:
async def _() -> Result[list[str]]:
menu_type_list = []
result = await DbPluginInfo.annotate().values_list("menu_type", flat=True)
for r in result:
@@ -140,8 +159,14 @@ async def _() -> Result:
return Result.ok(menu_type_list)
@router.get("/get_plugin", dependencies=[authentication()], description="获取插件详情")
async def _(module: str) -> Result:
@router.get(
"/get_plugin",
dependencies=[authentication()],
response_model=Result[PluginDetail],
response_class=JSONResponse,
description="获取插件详情",
)
async def _(module: str) -> Result[PluginDetail]:
db_plugin = await DbPluginInfo.get_or_none(module=module, load_status=True)
if not db_plugin:
return Result.fail("插件不存在...")
@@ -1,25 +1,37 @@
from nonebot import require
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from zhenxun.models.plugin_info import PluginInfo
from .model import PluginIr
from ....base_model import Result
from ....utils import authentication
require("plugin_store")
from zhenxun.builtin_plugins.plugin_store import ShopManage
router = APIRouter(prefix="/store")
@router.get(
"/get_plugin_store",
dependencies=[authentication()],
response_model=Result[dict],
response_class=JSONResponse,
deprecated="获取插件商店插件信息", # type: ignore
)
async def _() -> Result:
async def _() -> Result[dict]:
try:
require("plugin_store")
from zhenxun.builtin_plugins.plugin_store import ShopManage
data = await ShopManage.get_data()
return Result.ok(data)
plugin_list = [
{**data[name].dict(), "name": name, "id": idx}
for idx, name in enumerate(data)
]
modules = await PluginInfo.filter(load_status=True).values_list(
"module", flat=True
)
return Result.ok({"install_module": modules, "plugin_list": plugin_list})
except Exception as e:
return Result.fail(f"获取插件商店插件信息失败: {type(e)}: {e}")
@@ -27,24 +39,52 @@ async def _() -> Result:
@router.post(
"/install_plugin",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
deprecated="安装插件", # type: ignore
)
async def _(param: PluginIr) -> Result:
try:
require("plugin_store")
from zhenxun.builtin_plugins.plugin_store import ShopManage
result = await ShopManage.add_plugin(param.id) # type: ignore
return Result.ok(result)
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"安装插件失败: {type(e)}: {e}")
@router.post(
"/update_plugin",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
deprecated="更新插件", # type: ignore
)
async def _(param: PluginIr) -> Result:
try:
require("plugin_store")
from zhenxun.builtin_plugins.plugin_store import ShopManage
result = await ShopManage.update_plugin(param.id) # type: ignore
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"更新插件失败: {type(e)}: {e}")
@router.post(
"/remove_plugin",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
deprecated="移除插件", # type: ignore
)
async def _(param: PluginIr) -> Result:
try:
require("plugin_store")
from zhenxun.builtin_plugins.plugin_store import ShopManage
result = await ShopManage.remove_plugin(param.id) # type: ignore
return Result.ok(result)
return Result.ok(info=result)
except Exception as e:
return Result.fail(f"移除插件失败: {type(e)}: {e}")
@@ -4,10 +4,11 @@ from pathlib import Path
import aiofiles
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from zhenxun.utils._build_image import BuildImage
from ....base_model import Result
from ....base_model import Result, SystemFolderSize
from ....utils import authentication, get_system_disk
from .model import AddFile, DirFile, SaveFile, DeleteFile, RenameFile
@@ -17,9 +18,13 @@ IMAGE_TYPE = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"]
@router.get(
"/get_dir_list", dependencies=[authentication()], description="获取文件列表"
"/get_dir_list",
dependencies=[authentication()],
response_model=Result[list[DirFile]],
response_class=JSONResponse,
description="获取文件列表",
)
async def _(path: str | None = None) -> Result:
async def _(path: str | None = None) -> Result[list[DirFile]]:
base_path = Path(path) if path else Path()
data_list = []
for file in os.listdir(base_path):
@@ -37,13 +42,23 @@ async def _(path: str | None = None) -> Result:
@router.get(
"/get_resources_size", dependencies=[authentication()], description="获取文件列表"
"/get_resources_size",
dependencies=[authentication()],
response_model=Result[SystemFolderSize],
response_class=JSONResponse,
description="获取文件列表",
)
async def _(full_path: str | None = None) -> Result:
async def _(full_path: str | None = None) -> Result[SystemFolderSize]:
return Result.ok(await get_system_disk(full_path))
@router.post("/delete_file", dependencies=[authentication()], description="删除文件")
@router.post(
"/delete_file",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="删除文件",
)
async def _(param: DeleteFile) -> Result:
path = Path(param.full_path)
if not path or not path.exists():
@@ -56,7 +71,11 @@ async def _(param: DeleteFile) -> Result:
@router.post(
"/delete_folder", dependencies=[authentication()], description="删除文件夹"
"/delete_folder",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="删除文件夹",
)
async def _(param: DeleteFile) -> Result:
path = Path(param.full_path)
@@ -69,7 +88,13 @@ async def _(param: DeleteFile) -> Result:
return Result.warning_(f"删除失败: {e!s}")
@router.post("/rename_file", dependencies=[authentication()], description="重命名文件")
@router.post(
"/rename_file",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="重命名文件",
)
async def _(param: RenameFile) -> Result:
path = (
(Path(param.parent) / param.old_name) if param.parent else Path(param.old_name)
@@ -84,7 +109,11 @@ async def _(param: RenameFile) -> Result:
@router.post(
"/rename_folder", dependencies=[authentication()], description="重命名文件夹"
"/rename_folder",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="重命名文件夹",
)
async def _(param: RenameFile) -> Result:
path = (
@@ -100,7 +129,13 @@ async def _(param: RenameFile) -> Result:
return Result.warning_(f"重命名失败: {e!s}")
@router.post("/add_file", dependencies=[authentication()], description="新建文件")
@router.post(
"/add_file",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="新建文件",
)
async def _(param: AddFile) -> Result:
path = (Path(param.parent) / param.name) if param.parent else Path(param.name)
if path.exists():
@@ -112,7 +147,13 @@ async def _(param: AddFile) -> Result:
return Result.warning_(f"新建文件失败: {e!s}")
@router.post("/add_folder", dependencies=[authentication()], description="新建文件夹")
@router.post(
"/add_folder",
dependencies=[authentication()],
response_model=Result,
response_class=JSONResponse,
description="新建文件夹",
)
async def _(param: AddFile) -> Result:
path = (Path(param.parent) / param.name) if param.parent else Path(param.name)
if path.exists():
@@ -124,7 +165,13 @@ async def _(param: AddFile) -> Result:
return Result.warning_(f"新建文件夹失败: {e!s}")
@router.get("/read_file", dependencies=[authentication()], description="读取文件")
@router.get(
"/read_file",
dependencies=[authentication()],
response_model=Result[str],
response_class=JSONResponse,
description="读取文件",
)
async def _(full_path: str) -> Result:
path = Path(full_path)
if not path.exists():
@@ -136,8 +183,14 @@ async def _(full_path: str) -> Result:
return Result.warning_(f"读取文件失败: {e!s}")
@router.post("/save_file", dependencies=[authentication()], description="读取文件")
async def _(param: SaveFile) -> Result:
@router.post(
"/save_file",
dependencies=[authentication()],
response_model=Result[str],
response_class=JSONResponse,
description="读取文件",
)
async def _(param: SaveFile) -> Result[str]:
path = Path(param.full_path)
try:
async with aiofiles.open(path, "w", encoding="utf-8") as f:
@@ -147,8 +200,14 @@ async def _(param: SaveFile) -> Result:
return Result.warning_(f"保存文件失败: {e!s}")
@router.get("/get_image", dependencies=[authentication()], description="读取图片base64")
async def _(full_path: str) -> Result:
@router.get(
"/get_image",
dependencies=[authentication()],
response_model=Result[str],
response_class=JSONResponse,
description="读取图片base64",
)
async def _(full_path: str) -> Result[str]:
path = Path(full_path)
if not path.exists():
return Result.warning_("文件不存在...")
+9 -6
View File
@@ -1,11 +1,12 @@
from datetime import datetime
from typing_extensions import Self
from typing import Any, Generic, TypeVar
from pydantic import BaseModel, validator
T = TypeVar("T")
RT = TypeVar("RT")
class User(BaseModel):
username: str
@@ -17,7 +18,7 @@ class Token(BaseModel):
token_type: str
class Result(BaseModel):
class Result(Generic[RT], BaseModel):
"""
总体返回
"""
@@ -30,19 +31,21 @@ class Result(BaseModel):
"""info"""
warning: str | None = None
"""警告信息"""
data: Any = None
data: RT = None
"""返回数据"""
@classmethod
def warning_(cls, info: str, code: int = 200) -> Self:
def warning_(cls, info: str, code: int = 200) -> "Result[RT]":
return cls(suc=True, warning=info, code=code)
@classmethod
def fail(cls, info: str = "异常错误", code: int = 500) -> Self:
def fail(cls, info: str = "异常错误", code: int = 500) -> "Result[RT]":
return cls(suc=False, info=info, code=code)
@classmethod
def ok(cls, data: Any = None, info: str = "操作成功", code: int = 200) -> Self:
def ok(
cls, data: Any = None, info: str = "操作成功", code: int = 200
) -> "Result[RT]":
return cls(suc=True, info=info, code=code, data=data)
+13
View File
@@ -15,6 +15,19 @@ class BotSetting(BaseModel):
"""数据库链接"""
platform_superusers: dict[str, list[str]] = {}
"""平台超级用户"""
qbot_id_data: dict[str, str] = {}
"""官bot id:账号id"""
def get_qbot_uid(self, qbot_id: str) -> str | None:
"""获取官bot账号id
参数:
qbot_id: 官bot id
返回:
str: 账号id
"""
return self.qbot_id_data.get(qbot_id)
def get_superuser(self, platform: str) -> list[str]:
"""获取超级用户
+16 -21
View File
@@ -1,15 +1,14 @@
import time
from tortoise import fields
from typing_extensions import Self
from zhenxun.services.db_context import Model
from tortoise import fields
from zhenxun.services.log import logger
from zhenxun.services.db_context import Model
from zhenxun.utils.exception import UserAndGroupIsNone
class BanConsole(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255, null=True)
@@ -25,9 +24,9 @@ class BanConsole(Model):
operator = fields.CharField(255)
"""使用Ban命令的用户"""
class Meta:
class Meta: # type: ignore
table = "ban_console"
table_description = ".ban/b了 封禁人员/群组数据表"
table_description = "封禁人员/群组数据表"
@classmethod
async def _get_data(cls, user_id: str | None, group_id: str | None) -> Self | None:
@@ -45,16 +44,14 @@ class BanConsole(Model):
"""
if not user_id and not group_id:
raise UserAndGroupIsNone()
user = None
if user_id:
if group_id:
user = await cls.get_or_none(user_id=user_id, group_id=group_id)
else:
user = await cls.get_or_none(user_id=user_id, group_id__isnull=True)
return (
await cls.get_or_none(user_id=user_id, group_id=group_id)
if group_id
else await cls.get_or_none(user_id=user_id, group_id__isnull=True)
)
else:
if group_id:
user = await cls.get_or_none(user_id__isnull=True, group_id=group_id)
return user
return await cls.get_or_none(user_id="", group_id=group_id)
@classmethod
async def check_ban_level(
@@ -91,7 +88,7 @@ class BanConsole(Model):
返回:
int: ban剩余时长,-1时为永久ban,0表示未被ban
"""
logger.debug(f"获取用户ban时长", target=f"{group_id}:{user_id}")
logger.debug("获取用户ban时长", target=f"{group_id}:{user_id}")
user = await cls._get_data(user_id, group_id)
if not user and user_id:
user = await cls._get_data(user_id, None)
@@ -99,9 +96,7 @@ class BanConsole(Model):
if user.duration == -1:
return -1
_time = time.time() - (user.ban_time + user.duration)
if _time > 0:
return 0
return int(time.time() - user.ban_time - user.duration)
return 0 if _time > 0 else int(time.time() - user.ban_time - user.duration)
return 0
@classmethod
@@ -114,7 +109,7 @@ class BanConsole(Model):
返回:
bool: 是否被ban
"""
logger.debug(f"检测是否被ban", target=f"{group_id}:{user_id}")
logger.debug("检测是否被ban", target=f"{group_id}:{user_id}")
if await cls.check_ban_time(user_id, group_id):
return True
else:
@@ -143,8 +138,8 @@ class BanConsole(Model):
f"封禁用户/群组,等级:{ban_level},时长: {duration}",
target=f"{group_id}:{user_id}",
)
user = await cls._get_data(user_id, group_id)
if user:
target = await cls._get_data(user_id, group_id)
if target:
await cls.unban(user_id, group_id)
await cls.create(
user_id=user_id,
+22
View File
@@ -0,0 +1,22 @@
from tortoise import fields
from zhenxun.services.db_context import Model
class BotConnectLog(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
bot_id = fields.CharField(255, description="Bot id")
"""Bot id"""
platform = fields.CharField(255, null=True, description="平台")
"""平台"""
connect_time = fields.DatetimeField(description="连接时间")
"""日期"""
type = fields.IntField(null=True, description="1: 连接, 0: 断开")
"""1: 连接, 0: 断开"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
class Meta: # type: ignore
table = "bot_connect_log"
table_description = "bot连接表"
+170 -12
View File
@@ -9,7 +9,6 @@ from zhenxun.services.db_context import Model
class GroupConsole(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
group_id = fields.CharField(255, description="群组id")
@@ -34,8 +33,14 @@ class GroupConsole(Model):
"""群认证标记"""
block_plugin = fields.TextField(default="", description="禁用插件")
"""禁用插件"""
superuser_block_plugin = fields.TextField(
default="", description="超级用户禁用插件"
)
"""超级用户禁用插件"""
block_task = fields.TextField(default="", description="禁用插件")
"""禁用插件"""
superuser_block_task = fields.TextField(default="", description="超级用户禁用被动")
"""超级用户禁用被动"""
platform = fields.CharField(255, default="qq", description="所属平台")
"""所属平台"""
@@ -53,6 +58,7 @@ class GroupConsole(Model):
if modules := await TaskInfo.filter(default_status=False).values_list(
"module", flat=True
):
modules = [f"<{module}" for module in modules]
group.block_task = ",".join(modules) + "," # type: ignore
await group.save(using_db=using_db, update_fields=["block_task"])
return group
@@ -73,6 +79,7 @@ class GroupConsole(Model):
"module", flat=True
)
):
modules = [f"<{module}" for module in modules]
group.block_task = ",".join(modules) + "," # type: ignore
await group.save(using_db=using_db, update_fields=["block_task"])
return group, is_create
@@ -93,6 +100,7 @@ class GroupConsole(Model):
"module", flat=True
)
):
modules = [f"<{module}" for module in modules]
group.block_task = ",".join(modules) + "," # type: ignore
await group.save(using_db=using_db, update_fields=["block_task"])
return group, is_create
@@ -127,27 +135,94 @@ class GroupConsole(Model):
return group.is_super if (group := await cls.get_group(group_id)) else False
@classmethod
async def is_super_block_plugin(
cls, group_id: str, module: str, channel_id: str | None = None
) -> bool:
async def is_superuser_block_plugin(cls, group_id: str, module: str) -> bool:
"""查看群组是否超级用户禁用功能
参数:
group_id: 群组id
module: 模块名称
channel_id: 频道id
返回:
bool: 是否禁用被动
"""
return await cls.exists(
group_id=group_id,
channel_id=channel_id,
block_plugin__contains=f"super:{module},",
superuser_block_plugin__contains=f"<{module},",
)
@classmethod
async def is_block_plugin(
async def is_block_plugin(cls, group_id: str, module: str) -> bool:
"""查看群组是否禁用插件
参数:
group_id: 群组id
plugin: 插件名称
返回:
bool: 是否禁用插件
"""
return await cls.exists(
group_id=group_id, block_plugin__contains=f"<{module},"
) or await cls.exists(
group_id=group_id, superuser_block_plugin__contains=f"<{module},"
)
@classmethod
async def set_block_plugin(
cls,
group_id: str,
module: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
is_superuser: 是否为超级用户
platform: 平台
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if f"<{module}," not in group.superuser_block_plugin:
group.superuser_block_plugin += f"<{module},"
elif f"<{module}," not in group.block_plugin:
group.block_plugin += f"<{module},"
await group.save(update_fields=["block_plugin", "superuser_block_plugin"])
@classmethod
async def set_unblock_plugin(
cls,
group_id: str,
module: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
is_superuser: 是否为超级用户
platform: 平台
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if f"<{module}," in group.superuser_block_plugin:
group.superuser_block_plugin = group.superuser_block_plugin.replace(
f"<{module},", ""
)
elif f"<{module}," in group.block_plugin:
group.block_plugin = group.block_plugin.replace(f"<{module},", "")
await group.save(update_fields=["block_plugin", "superuser_block_plugin"])
@classmethod
async def is_normal_block_plugin(
cls, group_id: str, module: str, channel_id: str | None = None
) -> bool:
"""查看群组是否禁用功能
@@ -163,7 +238,23 @@ class GroupConsole(Model):
return await cls.exists(
group_id=group_id,
channel_id=channel_id,
block_plugin__contains=f"{module},",
block_plugin__contains=f"<{module},",
)
@classmethod
async def is_superuser_block_task(cls, group_id: str, task: str) -> bool:
"""查看群组是否超级用户禁用被动
参数:
group_id: 群组id
task: 模块名称
返回:
bool: 是否禁用被动
"""
return await cls.exists(
group_id=group_id,
superuser_block_task__contains=f"<{task},",
)
@classmethod
@@ -184,12 +275,79 @@ class GroupConsole(Model):
return await cls.exists(
group_id=group_id,
channel_id__isnull=True,
block_task__contains=f"{task},",
block_task__contains=f"<{task},",
) or await cls.exists(
group_id=group_id,
channel_id__isnull=True,
superuser_block_task__contains=f"<{task},",
)
return await cls.exists(
group_id=group_id, channel_id=channel_id, block_task__contains=f"{task},"
group_id=group_id, channel_id=channel_id, block_task__contains=f"<{task},"
) or await cls.exists(
group_id=group_id,
channel_id__isnull=True,
superuser_block_task__contains=f"<{task},",
)
@classmethod
async def set_block_task(
cls,
group_id: str,
task: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
is_superuser: 是否为超级用户
platform: 平台
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if f"<{task}," not in group.superuser_block_task:
group.superuser_block_task += f"<{task},"
elif f"<{task}," not in group.block_task:
group.block_task += f"<{task},"
await group.save(update_fields=["block_task", "superuser_block_task"])
@classmethod
async def set_unblock_task(
cls,
group_id: str,
task: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
is_superuser: 是否为超级用户
platform: 平台
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if f"<{task}," in group.superuser_block_task:
group.superuser_block_task = group.superuser_block_task.replace(
f"<{task},", ""
)
elif f"<{task}," in group.block_task:
group.block_task = group.block_task.replace(f"<{task},", "")
await group.save(update_fields=["block_task", "superuser_block_task"])
@classmethod
def _run_script(cls):
return []
return [
"ALTER TABLE group_console ADD superuser_block_plugin"
" character varying(255) NOT NULL DEFAULT '';",
"ALTER TABLE group_console ADD superuser_block_task"
" character varying(255) NOT NULL DEFAULT '';",
]
+72 -3
View File
@@ -23,7 +23,7 @@ class GroupInfo(Model):
platform = fields.CharField(255, default="qq", description="所属平台")
"""所属平台"""
class Meta:
class Meta: # type: ignore
table = "group_info"
table_description = "群聊信息表"
@@ -40,12 +40,81 @@ class GroupInfo(Model):
"""
return await cls.exists(group_id=group_id, block_task__contains=f"{task},")
@classmethod
async def is_block_plugin(cls, group_id: str, module: str) -> bool:
"""查看群组是否禁用插件
参数:
group_id: 群组id
plugin: 插件名称
返回:
bool: 是否禁用插件
"""
return await cls.exists(
group_id=group_id, block_plugin__contains=f"{module},"
) or await cls.exists(
group_id=group_id, superuser_block_plugin__contains=f"{module},"
)
@classmethod
async def set_block_plugin(
cls,
group_id: str,
module: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if "module," not in group.superuser_block_plugin:
group.superuser_block_plugin += f"{module},"
elif "module," not in group.block_plugin:
group.block_plugin += f"{module},"
await group.save(update_fields=["block_plugin", "superuser_block_plugin"])
@classmethod
async def set_unblock_plugin(
cls,
group_id: str,
module: str,
is_superuser: bool = False,
platform: str | None = None,
):
"""禁用群组插件
参数:
group_id: 群组id
task: 任务模块
"""
group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform}
)
if is_superuser:
if "module," in group.superuser_block_plugin:
group.superuser_block_plugin = group.superuser_block_plugin.replace(
f"{module},", ""
)
elif "module," in group.block_plugin:
group.block_plugin = group.block_plugin.replace(f"{module},", "")
await group.save(update_fields=["block_plugin", "superuser_block_plugin"])
@classmethod
def _run_script(cls):
return [
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;", # group_info表添加一个group_flag
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
# group_info表添加一个group_flag
"ALTER TABLE group_info ALTER COLUMN group_id TYPE character varying(255);",
"ALTER TABLE group_info ADD block_plugin Text NOT NULL DEFAULT '';",
"ALTER TABLE group_info ADD block_task Text NOT NULL DEFAULT '';",
"ALTER TABLE group_info ADD platform character varying(255) NOT NULL DEFAULT 'qq';",
"ALTER TABLE group_info ADD platform character varying(255) NOT NULL"
" DEFAULT 'qq';",
]
+6 -3
View File
@@ -4,7 +4,6 @@ from zhenxun.services.db_context import Model
class Statistics(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
@@ -15,15 +14,19 @@ class Statistics(Model):
"""插件名称"""
create_time = fields.DatetimeField(auto_now=True)
"""添加日期"""
bot_id = fields.CharField(255, null=True)
"""Bot Id"""
class Meta:
class Meta: # type: ignore
table = "statistics"
table_description = "插件调用统计数据库"
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE statistics RENAME COLUMN user_qq TO user_id;",
# 将user_qq改为user_id
"ALTER TABLE statistics ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE statistics ALTER COLUMN group_id TYPE character varying(255);",
"ALTER TABLE statistics ADD bot_id Text DEFAULT '';",
]
-2
View File
@@ -1,2 +0,0 @@
from .db_context import *
from .log import *
-2
View File
@@ -4,13 +4,11 @@ from tortoise.models import Model as Model_
from nonebot.utils import is_coroutine_callable
from zhenxun.configs.config import BotConfig
from zhenxun.configs.path_config import DATA_PATH
from .log import logger
SCRIPT_METHOD = []
MODELS: list[str] = []
DATABASE_SETTING_FILE = DATA_PATH / "database.json"
class Model(Model_):
+93 -18
View File
@@ -1,12 +1,13 @@
from typing import Any, overload
from datetime import datetime, timedelta
from typing import Any, Dict, overload
from nonebot import require
require("nonebot_plugin_session")
from loguru import logger as logger_
from nonebot.log import default_filter, default_format
from nonebot_plugin_session import Session
from nonebot.log import default_filter, default_format
from nonebot_plugin_uninfo import Session as uninfoSession
from zhenxun.configs.path_config import LOG_PATH
@@ -33,8 +34,10 @@ class logger:
TEMPLATE_A = "Adapter[{}] {}"
TEMPLATE_B = "Adapter[{}] [<u><c>{}</c></u>]: {}"
TEMPLATE_C = "Adapter[{}] 用户[<u><e>{}</e></u>] 触发 [<u><c>{}</c></u>]: {}"
TEMPLATE_D = "Adapter[{}] 群聊[<u><e>{}</e></u>] 用户[<u><e>{}</e></u>] 触发 [<u><c>{}</c></u>]: {}"
TEMPLATE_E = "Adapter[{}] 群聊[<u><e>{}</e></u>] 用户[<u><e>{}</e></u>] 触发 [<u><c>{}</c></u>] [Target](<u><e>{}</e></u>): {}"
TEMPLATE_D = "Adapter[{}] 群聊[<u><e>{}</e></u>] 用户[<u><e>{}</e></u>] 触发"
" [<u><c>{}</c></u>]: {}"
TEMPLATE_E = "Adapter[{}] 群聊[<u><e>{}</e></u>] 用户[<u><e>{}</e></u>] 触发"
" [<u><c>{}</c></u>] [Target](<u><e>{}</e></u>): {}"
TEMPLATE_ADAPTER = "Adapter[<m>{}</m>] "
TEMPLATE_USER = "用户[<u><e>{}</e></u>] "
@@ -75,21 +78,32 @@ class logger:
platform: str | None = None,
): ...
@overload
@classmethod
def info(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | None = None,
session: uninfoSession | None = None,
target: Any = None,
platform: str | None = None,
): ...
@classmethod
def info(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | uninfoSession | None = None,
group_id: int | str | None = None,
adapter: str | None = None,
target: Any = None,
platform: str | None = None,
):
user_id: str | None = session # type: ignore
group_id = None
if type(session) == Session:
if isinstance(session, Session):
user_id = session.id1
adapter = session.bot_type
if session.id3:
@@ -97,12 +111,18 @@ class logger:
elif session.id2:
group_id = f"{session.id2}"
platform = platform or session.platform
elif isinstance(session, uninfoSession):
user_id = session.user.id
adapter = session.adapter
if session.group:
group_id = session.group.id
platform = session.basic["scope"]
template = cls.__parser_template(
info, command, user_id, group_id, adapter, target, platform
)
try:
logger_.opt(colors=True).info(template)
except Exception as e:
except Exception:
logger_.info(template)
@classmethod
@@ -110,7 +130,7 @@ class logger:
cls,
info: str,
command: str,
param: Dict[str, Any] | None = None,
param: dict[str, Any] | None = None,
result: str = "",
):
param_str = ""
@@ -149,13 +169,27 @@ class logger:
e: Exception | None = None,
): ...
@overload
@classmethod
def warning(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | None = None,
session: uninfoSession | None = None,
adapter: str | None = None,
target: Any = None,
platform: str | None = None,
e: Exception | None = None,
): ...
@classmethod
def warning(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | uninfoSession | None = None,
group_id: int | str | None = None,
adapter: str | None = None,
target: Any = None,
@@ -163,8 +197,7 @@ class logger:
e: Exception | None = None,
):
user_id: str | None = session # type: ignore
group_id = None
if type(session) == Session:
if isinstance(session, Session):
user_id = session.id1
adapter = session.bot_type
if session.id3:
@@ -172,6 +205,12 @@ class logger:
elif session.id2:
group_id = f"{session.id2}"
platform = platform or session.platform
elif isinstance(session, uninfoSession):
user_id = session.user.id
adapter = session.adapter
if session.group:
group_id = session.group.id
platform = session.basic["scope"]
template = cls.__parser_template(
info, command, user_id, group_id, adapter, target, platform
)
@@ -210,13 +249,26 @@ class logger:
e: Exception | None = None,
): ...
@overload
@classmethod
def error(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | None = None,
session: uninfoSession | None = None,
target: Any = None,
platform: str | None = None,
e: Exception | None = None,
): ...
@classmethod
def error(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | uninfoSession | None = None,
group_id: int | str | None = None,
adapter: str | None = None,
target: Any = None,
@@ -224,8 +276,7 @@ class logger:
e: Exception | None = None,
):
user_id: str | None = session # type: ignore
group_id = None
if type(session) == Session:
if isinstance(session, Session):
user_id = session.id1
adapter = session.bot_type
if session.id3:
@@ -233,6 +284,12 @@ class logger:
elif session.id2:
group_id = f"{session.id2}"
platform = platform or session.platform
elif isinstance(session, uninfoSession):
user_id = session.user.id
adapter = session.adapter
if session.group:
group_id = session.group.id
platform = session.basic["scope"]
template = cls.__parser_template(
info, command, user_id, group_id, adapter, target, platform
)
@@ -271,13 +328,26 @@ class logger:
e: Exception | None = None,
): ...
@overload
@classmethod
def debug(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | None = None,
session: uninfoSession | None = None,
target: Any = None,
platform: str | None = None,
e: Exception | None = None,
): ...
@classmethod
def debug(
cls,
info: str,
command: str | None = None,
*,
session: int | str | Session | uninfoSession | None = None,
group_id: int | str | None = None,
adapter: str | None = None,
target: Any = None,
@@ -285,8 +355,7 @@ class logger:
e: Exception | None = None,
):
user_id: str | None = session # type: ignore
group_id = None
if type(session) == Session:
if isinstance(session, Session):
user_id = session.id1
adapter = session.bot_type
if session.id3:
@@ -294,6 +363,12 @@ class logger:
elif session.id2:
group_id = f"{session.id2}"
platform = platform or session.platform
elif isinstance(session, uninfoSession):
user_id = session.user.id
adapter = session.adapter
if session.group:
group_id = session.group.id
platform = session.basic["scope"]
template = cls.__parser_template(
info, command, user_id, group_id, adapter, target, platform
)
+19 -3
View File
@@ -1,3 +1,6 @@
from nonebot.adapters import Bot
from nonebot_plugin_uninfo import Uninfo, Session, SupportScope, get_interface
from zhenxun.services.log import logger
from zhenxun.configs.config import BotConfig
from zhenxun.models.task_info import TaskInfo
@@ -6,9 +9,10 @@ from zhenxun.models.group_console import GroupConsole
class CommonUtils:
@classmethod
async def task_is_block(cls, module: str, group_id: str | None) -> bool:
async def task_is_block(
cls, session: Uninfo | Bot, module: str, group_id: str | None = None
) -> bool:
"""判断被动技能是否可以发送
参数:
@@ -18,6 +22,19 @@ class CommonUtils:
返回:
bool: 是否可以发送
"""
if isinstance(session, Bot):
if interface := get_interface(session):
info = interface.basic_info()
if info["scope"] == SupportScope.qq_api:
logger.info("q官bot放弃所有被动技能发言...")
"""q官bot放弃所有被动技能发言"""
return False
if session.scene == SupportScope.qq_api:
"""q官bot放弃所有被动技能发言"""
logger.info("q官bot放弃所有被动技能发言...")
return False
if not group_id and isinstance(session, Session):
group_id = session.group.id if session.group else None
if task := await TaskInfo.get_or_none(module=module):
"""被动全局状态"""
if not task.status:
@@ -39,7 +56,6 @@ class CommonUtils:
class SqlUtils:
@classmethod
def random(cls, query, limit: int = 1) -> str:
db_class_name = BotConfig.get_sql_type()
+5 -7
View File
@@ -1,10 +1,10 @@
from typing import Any
from nonebot.internal.params import Depends
from nonebot.matcher import Matcher
from nonebot.params import Command
from nonebot.matcher import Matcher
from nonebot_plugin_uninfo import Uninfo
from nonebot.internal.params import Depends
from nonebot_plugin_session import EventSession
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
from zhenxun.configs.config import Config
from zhenxun.utils.message import MessageUtils
@@ -49,10 +49,8 @@ def UserName():
用户名称
"""
async def dependency(user_info: UserInfo = EventUserInfo()):
return (
user_info.user_displayname or user_info.user_remark or user_info.user_name
) or ""
async def dependency(user_info: Uninfo):
return user_info.user.nick or user_info.user.name or ""
return Depends(dependency)
+12 -4
View File
@@ -1,22 +1,30 @@
import os
import random
from nonebot_plugin_htmlrender import template_to_pic
from zhenxun.configs.path_config import TEMPLATE_PATH
from zhenxun.utils._build_image import BuildImage
from zhenxun.configs.path_config import TEMPLATE_PATH
from .models import Barh
BACKGROUND_PATH = TEMPLATE_PATH / "bar_chart" / "background"
class ChartUtils:
@classmethod
async def barh(cls, data: Barh) -> BuildImage:
"""横向统计图"""
to_json = data.dict()
to_json["background_image"] = (
f"./background/{random.choice(os.listdir(BACKGROUND_PATH))}"
)
pic = await template_to_pic(
template_path=str((TEMPLATE_PATH / "bar_chart").absolute()),
template_name="main.html",
templates={"data": data},
templates={"data": to_json},
pages={
"viewport": {"width": 1000, "height": 500},
"viewport": {"width": 1000, "height": 1000},
"base_url": f"file://{TEMPLATE_PATH}",
},
wait=2,
+2 -1
View File
@@ -2,8 +2,9 @@ from pydantic import BaseModel
class Barh(BaseModel):
category_data: list[str]
"""坐标轴数据"""
data: list[int | float]
"""实际数据"""
title: str
"""标题"""
+1 -1
View File
@@ -1,6 +1,6 @@
from collections.abc import Generator
from .consts import GITHUB_REPO_URL_PATTERN
from .const import GITHUB_REPO_URL_PATTERN
from .func import get_fastest_raw_formats, get_fastest_archive_formats
from .models import RepoAPI, RepoInfo, GitHubStrategy, JsdelivrStrategy
+35
View File
@@ -0,0 +1,35 @@
import re
GITHUB_REPO_URL_PATTERN = re.compile(
r"^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)(/tree/(?P<branch>[^/]+))?$"
)
"""github仓库地址正则"""
JSD_PACKAGE_API_FORMAT = (
"https://data.jsdelivr.com/v1/packages/gh/{owner}/{repo}@{branch}"
)
"""jsdelivr包地址格式"""
GIT_API_TREES_FORMAT = (
"https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1"
)
"""git api trees地址格式"""
CACHED_API_TTL = 300
"""缓存api ttl"""
RAW_CONTENT_FORMAT = "https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}"
"""raw content格式"""
ARCHIVE_URL_FORMAT = "https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip"
"""archive url格式"""
RELEASE_ASSETS_FORMAT = (
"https://github.com/{owner}/{repo}/releases/download/{version}/{filename}"
)
"""release assets格式"""
RELEASE_SOURCE_FORMAT = (
"https://codeload.github.com/{owner}/{repo}/legacy.{compress}/refs/tags/{version}"
)
"""release 源码格式"""
+1 -1
View File
@@ -1,7 +1,7 @@
from aiocache import cached
from ..http_utils import AsyncHttpx
from .consts import (
from .const import (
ARCHIVE_URL_FORMAT,
RAW_CONTENT_FORMAT,
RELEASE_ASSETS_FORMAT,
+6 -4
View File
@@ -5,7 +5,7 @@ from strenum import StrEnum
from pydantic import BaseModel
from ..http_utils import AsyncHttpx
from .consts import CACHED_API_TTL, GIT_API_TREES_FORMAT, JSD_PACKAGE_API_FORMAT
from .const import CACHED_API_TTL, GIT_API_TREES_FORMAT, JSD_PACKAGE_API_FORMAT
from .func import (
get_fastest_raw_formats,
get_fastest_archive_formats,
@@ -199,13 +199,15 @@ class GitHubStrategy:
body: TreeInfo
def export_files(self, module_path: str) -> list[str]:
def export_files(self, module_path: str, is_dir: bool) -> list[str]:
"""导出文件路径"""
tree_info = self.body
return [
file.path
for file in tree_info.tree
if file.type == TreeType.FILE and file.path.startswith(module_path)
if file.type == TreeType.FILE
and file.path.startswith(module_path)
and (not is_dir or file.path[len(module_path)] == "/")
]
@classmethod
@@ -229,4 +231,4 @@ class GitHubStrategy:
def get_files(self, module_path: str, is_dir: bool = True) -> list[str]:
"""获取文件路径"""
return self.export_files(module_path)
return self.export_files(module_path, is_dir)
+13 -15
View File
@@ -101,7 +101,7 @@ class AsyncHttpx:
) -> Response:
if not headers:
headers = get_user_agent()
_proxy = proxy if proxy else cls.proxy if use_proxy else None
_proxy = proxy or (cls.proxy if use_proxy else None)
async with httpx.AsyncClient(proxies=_proxy, verify=verify) as client: # type: ignore
return await client.get(
url,
@@ -140,7 +140,7 @@ class AsyncHttpx:
"""
if not headers:
headers = get_user_agent()
_proxy = proxy if proxy else cls.proxy if use_proxy else None
_proxy = proxy or (cls.proxy if use_proxy else None)
async with httpx.AsyncClient(proxies=_proxy, verify=verify) as client: # type: ignore
return await client.head(
url,
@@ -187,7 +187,7 @@ class AsyncHttpx:
"""
if not headers:
headers = get_user_agent()
_proxy = proxy if proxy else cls.proxy if use_proxy else None
_proxy = proxy or (cls.proxy if use_proxy else None)
async with httpx.AsyncClient(proxies=_proxy, verify=verify) as client: # type: ignore
return await client.post(
url,
@@ -202,6 +202,11 @@ class AsyncHttpx:
**kwargs,
)
@classmethod
async def get_content(cls, url: str, **kwargs) -> bytes | None:
res = await cls.get(url, **kwargs)
return res.content if res and res.status_code == 200 else None
@classmethod
async def download_file(
cls,
@@ -259,13 +264,10 @@ class AsyncHttpx:
async with aiofiles.open(path, "wb") as wf:
await wf.write(content)
logger.info(f"下载 {u} 成功.. Path:{path.absolute()}")
return True
else:
if not headers:
headers = get_user_agent()
_proxy = (
proxy if proxy else cls.proxy if use_proxy else None
)
_proxy = proxy or (cls.proxy if use_proxy else None)
async with httpx.AsyncClient(
proxies=_proxy, # type: ignore
verify=verify,
@@ -298,7 +300,7 @@ class AsyncHttpx:
) as progress:
download_task = progress.add_task(
"Download",
total=total if total else None,
total=total or None,
)
async for chunk in response.aiter_bytes():
await wf.write(chunk)
@@ -311,11 +313,10 @@ class AsyncHttpx:
f"下载 {u} 成功.. "
f"Path:{path.absolute()}"
)
return True
return True
except (TimeoutError, ConnectTimeout, HTTPStatusError):
logger.warning(f"下载 {u} 失败.. 尝试下一个地址..")
else:
logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}")
logger.error(f"下载 {url} 下载超时.. Path:{path.absolute()}")
except Exception as e:
logger.error(f"下载 {url} 错误 Path:{path.absolute()}", e=e)
return False
@@ -479,10 +480,7 @@ class AsyncPlaywright:
if isinstance(path, str):
path = Path(path)
wait_time = wait_time * 1000 if wait_time else None
if isinstance(element, str):
element_list = [element]
else:
element_list = element
element_list = [element] if isinstance(element, str) else element
async with cls.new_page(
viewport=viewport_size,
user_agent=user_agent,
+61 -29
View File
@@ -7,20 +7,24 @@ import nonebot
from pydantic import BaseModel
from nonebot.adapters import Bot
from nonebot.utils import is_coroutine_callable
from nonebot_plugin_alconna import SupportScope
from nonebot.adapters.dodo import Bot as DodoBot
from nonebot.adapters.onebot.v11 import Bot as v11Bot
from nonebot.adapters.onebot.v12 import Bot as v12Bot
from nonebot.adapters.discord import Bot as DiscordBot
from nonebot_plugin_uninfo import Uninfo, get_interface
from nonebot.adapters.kaiheila import Bot as KaiheilaBot
from nonebot_plugin_alconna.uniseg import Target, Receipt, UniMessage
from zhenxun.services.log import logger
from zhenxun.configs.config import BotConfig
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.models.friend_user import FriendUser
from zhenxun.utils.exception import NotFindSuperuser
from zhenxun.models.group_console import GroupConsole
driver = nonebot.get_driver()
class UserData(BaseModel):
name: str
@@ -40,6 +44,20 @@ class UserData(BaseModel):
class PlatformUtils:
@classmethod
def is_qbot(cls, session: Uninfo | Bot) -> bool:
"""判断bot是否为qq官bot
参数:
session: Uninfo
返回:
bool: 是否为官bot
"""
if isinstance(session, Bot):
return bool(BotConfig.get_qbot_uid(session.self_id))
return session.scope == SupportScope.qq_api
@classmethod
async def ban_user(cls, bot: Bot, user_id: str, group_id: str, duration: int):
"""禁言
@@ -244,30 +262,27 @@ class PlatformUtils:
return None
@classmethod
async def get_user_avatar(cls, user_id: str, platform: str) -> bytes | None:
async def get_user_avatar(
cls, user_id: str, platform: str, appid: str | None = None
) -> bytes | None:
"""快捷获取用户头像
参数:
user_id: 用户id
platform: 平台
"""
url = None
if platform == "qq":
url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
async with httpx.AsyncClient() as client:
for _ in range(3):
try:
return (await client.get(url)).content
except Exception:
logger.error(
"获取用户头像错误",
"Util",
target=user_id,
platform=platform,
)
return None
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"
return await AsyncHttpx.get_content(url) if url else None
@classmethod
def get_user_avatar_url(cls, user_id: str, platform: str) -> str | None:
def get_user_avatar_url(
cls, user_id: str, platform: str, appid: str | None = None
) -> str | None:
"""快捷获取用户头像url
参数:
@@ -275,8 +290,13 @@ class PlatformUtils:
platform: 平台
"""
if platform == "qq":
return f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=160"
return None
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:
return None
@classmethod
async def get_group_avatar(cls, gid: str, platform: str) -> bytes | None:
@@ -371,7 +391,7 @@ class PlatformUtils:
return len(create_list)
@classmethod
def get_platform(cls, bot: Bot) -> str | None:
def get_platform(cls, t: Bot | Uninfo) -> str:
"""获取平台
参数:
@@ -380,13 +400,15 @@ class PlatformUtils:
返回:
str | None: 平台
"""
if isinstance(bot, v11Bot | v12Bot):
return "qq"
if isinstance(bot, DodoBot):
return "dodo"
if isinstance(bot, KaiheilaBot):
return "kaiheila"
return "discord" if isinstance(bot, DiscordBot) else None
if isinstance(t, Bot):
if interface := get_interface(t):
info = interface.basic_info()
platform = info["scope"].lower()
return "qq" if platform.startswith("qq") else platform
else:
platform = t.basic["scope"].lower()
return "qq" if platform.startswith("qq") else platform
return "unknown"
@classmethod
async def get_group_list(cls, bot: Bot) -> tuple[list[GroupConsole], str]:
@@ -550,7 +572,7 @@ async def broadcast_group(
bot: Bot | list[Bot] | None = None,
bot_id: str | set[str] | None = None,
ignore_group: set[int] | None = None,
check_func: Callable[[str], Awaitable] | None = None,
check_func: Callable[[Bot, str], Awaitable] | None = None,
log_cmd: str | None = None,
platform: Literal["qq", "dodo", "kaiheila"] | None = None,
):
@@ -604,14 +626,24 @@ async def broadcast_group(
or group.channel_id in ignore_group
)
) or key in _used_group:
logger.debug(
"广播方法群组重复, 已跳过...",
log_cmd,
group_id=group.group_id,
)
continue
is_run = False
if check_func:
if is_coroutine_callable(check_func):
is_run = await check_func(group.group_id)
is_run = await check_func(_bot, group.group_id)
else:
is_run = check_func(group.group_id)
is_run = check_func(_bot, group.group_id)
if not is_run:
logger.debug(
"广播方法检测运行方法为 False, 已跳过...",
log_cmd,
group_id=group.group_id,
)
continue
target = PlatformUtils.get_target(
_bot, None, group.group_id, group.channel_id
-1
View File
@@ -2,7 +2,6 @@ from pydantic import BaseModel
class CommonSql(BaseModel):
sql: str
"""sql语句"""
remark: str
+39 -9
View File
@@ -1,10 +1,12 @@
from nonebot.adapters import Bot, Event
from nonebot.internal.rule import Rule
from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER
from nonebot_plugin_session import EventSession, SessionLevel
from nonebot_plugin_uninfo import Uninfo
from nonebot_plugin_session import EventSession
from zhenxun.configs.config import Config
from zhenxun.models.level_user import LevelUser
from zhenxun.utils.platform import PlatformUtils
def admin_check(a: int | str, key: str | None = None) -> Rule:
@@ -19,33 +21,38 @@ def admin_check(a: int | str, key: str | None = None) -> Rule:
Rule: Rule
"""
async def _rule(bot: Bot, event: Event, session: EventSession) -> bool:
async def _rule(bot: Bot, event: Event, session: Uninfo) -> bool:
if await SUPERUSER(bot, event):
return True
if session.id1 and session.id2:
if PlatformUtils.is_qbot(session):
"""官bot接口,放弃所有权限检查"""
return False
if session.id and session.group:
level = a
if type(a) == str and key:
if isinstance(a, str) and key:
level = Config.get_config(a, key)
if level is not None:
return bool(
await LevelUser.check_level(session.id1, session.id2, int(level))
await LevelUser.check_level(
session.id, session.group.id, int(level)
)
)
return False
return Rule(_rule)
def ensure_group(session: EventSession) -> bool:
def ensure_group(session: Uninfo) -> bool:
"""
是否在群聊中
参数:
session: session
session: Uninfo
返回:
bool: bool
"""
return session.level in [SessionLevel.LEVEL2, SessionLevel.LEVEL3]
return bool(session.group)
def ensure_private(session: EventSession) -> bool:
@@ -59,3 +66,26 @@ def ensure_private(session: EventSession) -> bool:
bool: bool
"""
return not session.id3 and not session.id2
def notice_rule(event_type: type | list[type]) -> Rule:
"""
Notice限制
参数:
event_type: Event类型
返回:
Rule: Rule
"""
async def _rule(event: Event) -> bool:
if isinstance(event_type, list):
for et in event_type:
if isinstance(event, et):
return True
else:
return isinstance(event, event_type)
return False
return Rule(_rule)