数据库中所有user_qq改名以及user_id和group_id改为字符串

This commit is contained in:
HibiKier 2023-05-22 20:56:42 +08:00
parent 0d3c6d8684
commit 848de7f4fe
68 changed files with 1103 additions and 645 deletions

View File

@ -331,6 +331,15 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
## 更新
### 2023/5/22
* 群聊中B站订阅所有管理员共享增删操作
* 数据库中所有user_qq改名以及user_id和group_id改为字符串
### 2023/5/16
* 修复因明日方舟新增“中坚寻访”导致抽卡模拟不可用的问题 [@pull/1418](https://github.com/HibiKier/zhenxun_bot/pull/1418)
### 2023/4/16
* 修复开箱更新未登录时没有停止更新

View File

@ -17,14 +17,16 @@ from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.message_builder import image
from utils.typing import BLOCK_TYPE
from utils.utils import get_matchers
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
CUSTOM_WELCOME_FILE = Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
CUSTOM_WELCOME_FILE.parent.mkdir(parents=True, exist_ok=True)
ICON_PATH = IMAGE_PATH / "other"
GROUP_HELP_PATH = DATA_PATH / "group_help"
async def group_current_status(group_id: int) -> str:
"""
@ -66,14 +68,14 @@ async def group_current_status(group_id: int) -> str:
async def custom_group_welcome(
msg: str, img_list: List[str], user_id: int, group_id: int
msg: str, img_list: List[str], user_id: str, group_id: str
) -> Union[str, Message]:
"""
说明:
替换群欢迎消息
参数:
:param msg: 欢迎消息文本
:param img_list: 欢迎消息图片只取第一张
:param img_list: 欢迎消息图片
:param user_id: 用户id用于log记录
:param group_id: 群号
"""
@ -84,18 +86,16 @@ async def custom_group_welcome(
if msg_image.exists():
msg_image.unlink()
data = {}
if not custom_welcome_msg_json.exists():
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
else:
try:
data = json.load(open(custom_welcome_msg_json, "r"))
except FileNotFoundError:
pass
if CUSTOM_WELCOME_FILE.exists():
data = json.load(CUSTOM_WELCOME_FILE.open("r", encoding="utf8"))
try:
if msg:
data[str(group_id)] = str(msg)
data[group_id] = msg
json.dump(
data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False
data,
CUSTOM_WELCOME_FILE.open("w", encoding="utf8"),
indent=4,
ensure_ascii=False,
)
logger.info(f"更换群欢迎消息 {msg}", "更换群欢迎信息", user_id, group_id)
result += msg
@ -151,12 +151,12 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False) -
参数:
:param cmd: 功能名称
:param group_id: 群号
:param is_super: 是否超级用户超级用户用于私聊开关功能状态
:param is_super: 是否超级用户超级用户用于私聊开关功能状态
"""
global task_data
if not task_data:
task_data = group_manager.get_task_data()
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png"
help_path = GROUP_HELP_PATH / f"{group_id}.png"
status = cmd[:2]
cmd = cmd[2:]
type_ = "plugin"
@ -169,8 +169,8 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False) -
else:
if group_manager.check_group_task_status(group_id, task):
group_manager.close_group_task(group_id, task)
if group_help_file.exists():
group_help_file.unlink()
if help_path.exists():
help_path.unlink()
return f"{status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
@ -179,8 +179,8 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False) -
else:
group_manager.block_plugin(f, group_id, False)
group_manager.save()
if group_help_file.exists():
group_help_file.unlink()
if help_path.exists():
help_path.unlink()
return f"{status} 全部功能!"
if cmd.lower() in [task_data[x].lower() for x in task_data.keys()]:
type_ = "task"
@ -206,27 +206,28 @@ async def change_group_switch(cmd: str, group_id: int, is_super: bool = False) -
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id)
if group_help_file.exists():
group_help_file.unlink()
if help_path.exists():
help_path.unlink()
if is_super:
for file in os.listdir(DATA_PATH / "group_help"):
file = DATA_PATH / "group_help" / file
for file in os.listdir(GROUP_HELP_PATH):
file = GROUP_HELP_PATH / file
file.unlink()
else:
_help_image = DATA_PATH / "group_help" / f"{group_id}.png"
if _help_image.exists():
_help_image.unlink()
if help_path.exists():
help_path.unlink()
return f"{status} {cmd} 功能!"
def set_plugin_status(cmd: str, block_type: str = "all"):
def set_plugin_status(cmd: str, block_type: BLOCK_TYPE = "all"):
"""
说明:
设置插件功能状态超级用户使用
参数:
:param cmd: 功能名称
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
:param block_type: 限制类型, 'all': 全局, 'private': 私聊, 'group': 群聊
"""
if block_type not in ["all", "private", "group"]:
raise TypeError("block_type类型错误, 可选值: ['all', 'private', 'group']")
status = cmd[:2]
cmd = cmd[2:]
module = plugins2settings_manager.get_plugin_module(cmd)
@ -234,8 +235,8 @@ def set_plugin_status(cmd: str, block_type: str = "all"):
plugins_manager.unblock_plugin(module)
else:
plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(DATA_PATH / "group_help"):
file = DATA_PATH / "group_help" / file
for file in os.listdir(GROUP_HELP_PATH):
file = GROUP_HELP_PATH / file
file.unlink()
@ -306,7 +307,9 @@ async def update_member_info(
if user_info["role"] in [
"owner",
"admin",
] and not await LevelUser.is_group_flag(user_info["user_id"], str(group_id)):
] and not await LevelUser.is_group_flag(
user_info["user_id"], str(group_id)
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
@ -356,7 +359,9 @@ async def update_member_info(
)
if _del_member_list:
for del_user in _del_member_list:
await GroupInfoUser.filter(user_id=str(del_user), group_id=str(group_id)).delete()
await GroupInfoUser.filter(
user_id=str(del_user), group_id=str(group_id)
).delete()
logger.info(f"删除已退群用户", "更新群组成员信息", del_user, group_id)
if _error_member_list and remind_superuser:
result = ""

View File

@ -16,10 +16,10 @@ admin_notice = on_notice(priority=5)
@admin_notice.handle()
async def _(event: GroupAdminNoticeEvent):
if user := await GroupInfoUser.filter(
if user := await GroupInfoUser.get_or_none(
user_id=str(event.user_id), group_id=str(event.group_id)
).first():
nickname = user.nickname
):
nickname = user.user_name
else:
nickname = event.user_id
if event.sub_type == "set":
@ -31,7 +31,10 @@ async def _(event: GroupAdminNoticeEvent):
admin_default_auth,
)
logger.info(
f"为新晋管理员 {nickname}({event.user_id}) " f"添加权限等级:{admin_default_auth}"
f"成为管理员,添加权限: {admin_default_auth}",
"群管理员变动监测",
event.user_id,
event.group_id,
)
else:
logger.warning(
@ -39,4 +42,4 @@ async def _(event: GroupAdminNoticeEvent):
)
elif event.sub_type == "unset":
await LevelUser.delete_level(event.user_id, event.group_id)
logger.info(f"将非管理员 {nickname}({event.user_id}) 取消权限等级")
logger.info("撤销管理员,,取消权限等级", "群管理员变动监测", event.user_id, event.group_id)

View File

@ -16,8 +16,8 @@ __plugin_usage__ = """
usage
指令
自定义进群欢迎消息 ?[文本] ?[图片]
示例自定义进群欢迎消息 欢迎新人[图片]
Note可以通过[at]来确认是否艾特新成员
示例自定义进群欢迎消息 欢迎新人[图片]
示例自定义进群欢迎消息 欢迎你[at]
""".strip()
__plugin_des__ = "简易的自定义群欢迎消息"
@ -51,10 +51,12 @@ async def _(
await custom_welcome.finish(__plugin_usage__)
try:
await custom_welcome.send(
await custom_group_welcome(msg, img, event.user_id, event.group_id),
await custom_group_welcome(
msg, img, str(event.user_id), str(event.group_id)
),
at_sender=True,
)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}")
logger.info(f"自定义群欢迎消息:{msg}", cmd, event.user_id, event.group_id)
except Exception as e:
logger.error(
f"自定义进群欢迎消息发生错误", cmd, event.user_id, getattr(event, "group_id", None), e=e

View File

@ -9,7 +9,7 @@ from nonebot.adapters.onebot.v11 import (
MessageEvent,
PrivateMessageEvent,
)
from nonebot.params import RegexGroup
from nonebot.params import CommandArg, RegexGroup
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
@ -111,7 +111,7 @@ async def _(
block_type = "all" if block_type == "a" else block_type
block_type = "private" if block_type == "p" else block_type
block_type = "group" if block_type == "g" else block_type
set_plugin_status(_cmd, block_type)
set_plugin_status(_cmd, block_type) # type: ignore
if block_type == "all":
await switch_rule_matcher.send(f"{_cmd[:2]}功能:{_cmd[2:]}")
elif block_type == "private":

View File

@ -6,6 +6,8 @@ from nonebot.adapters.onebot.v11 import (
GroupMessageEvent,
)
from services.log import logger
from ._data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
@ -32,9 +34,11 @@ refresh_member_group = on_command(
@refresh_member_group.handle()
async def _(bot: Bot, event: GroupMessageEvent):
if await update_member_info(bot, event.group_id):
await refresh_member_group.finish("更新群员信息成功!", at_sender=True)
await refresh_member_group.send("更新群员信息成功!", at_sender=True)
logger.info("更新群员信息成功!", "更新群组成员列表", event.user_id, event.group_id)
else:
await refresh_member_group.finish("更新群员信息失败!", at_sender=True)
await refresh_member_group.send("更新群员信息失败!", at_sender=True)
logger.info("更新群员信息失败!", "更新群组成员列表", event.user_id, event.group_id)
group_increase_handle = on_notice(priority=1, block=False)
@ -42,5 +46,6 @@ group_increase_handle = on_notice(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if event.user_id == int(bot.self_id):
if str(event.user_id) == bot.self_id:
await update_member_info(bot, event.group_id)
logger.info("{NICKNAME}加入群聊更新群组信息", "更新群组成员列表", event.user_id, event.group_id)

View File

@ -10,7 +10,6 @@ from utils.manager.models import PluginType
driver: Driver = nonebot.get_driver()
background = IMAGE_PATH / "background" / "0.png"
ADMIN_HELP_IMAGE = IMAGE_PATH / "admin_help_img.png"

View File

@ -118,10 +118,15 @@ async def _():
bot = bots[key]
fl = await bot.get_friend_list()
for f in fl:
await FriendUser.create(user_id=str(f["user_id"]), user_name=f["nickname"])
logger.debug(f"更新好友信息成功", "自动更新好友", f["user_id"])
if FriendUser.exists(user_id=str(f["user_id"])):
await FriendUser.create(
user_id=str(f["user_id"]), user_name=f["nickname"]
)
logger.debug(f"更新好友信息成功", "自动更新好友", f["user_id"])
else:
logger.debug(f"好友信息已存在", "自动更新好友", f["user_id"])
except Exception as e:
logger.error(f"自动更新群组信息错误", e=e)
logger.error(f"自动更新好友信息错误", "自动更新好友", e=e)
logger.info("自动更新好友信息成功...")

View File

@ -56,7 +56,7 @@ __plugin_configs__ = {
"value": 5,
"help": "ban/unban所需要的管理员权限等级",
"default_value": 5,
"type": int
"type": int,
}
}

View File

@ -30,7 +30,7 @@ def parse_ban_time(msg: str) -> Union[int, str]:
return int(msg_split[0]) * 60 * 60 + int(msg_split[1]) * 60
except ValueError as e:
logger.error("解析ban时长错误", ".ban", e=e)
return "长不可以带小数点"
return "间解析错误"
async def a_ban(
@ -56,7 +56,7 @@ async def a_ban(
return "未查询到ban级用户权限"
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"将 [Target]({qq})封禁 时长 {time / 60} 分钟", ".ban", event.user_id, group_id
f"封禁 时长 {time / 60} 分钟", ".ban", event.user_id, group_id, qq
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:

View File

@ -37,12 +37,12 @@ async def _(event: MessageEvent, msg: str = PlaintText()):
if isinstance(event, GroupMessageEvent):
group_id = str(event.group_id)
TEMP_LIST.append(
{
"user_id": str(event.user_id),
"group_id": group_id,
"text": str(event.get_message()),
"plain_text": msg,
}
ChatHistory(
user_id=str(event.user_id),
group_id=group_id,
text=str(event.get_message()),
plain_text=msg,
)
)
@ -55,8 +55,7 @@ async def _():
message_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if message_list:
model_list = [ChatHistory(**x) for x in message_list]
await ChatHistory.bulk_create(model_list)
await ChatHistory.bulk_create(message_list)
logger.debug(f"批量添加聊天记录 {len(message_list)}", "定时任务")
except Exception as e:
logger.error(f"定时批量添加聊天记录", "定时任务", e=e)

View File

@ -69,7 +69,7 @@ async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup())
num_str = "发言次数:\n\n"
idx = 1
for uid, num in rank_data:
if user := await GroupInfoUser.filter(user_id=str(uid), group_id=str(gid)).first():
if user := await GroupInfoUser.filter(user_id=uid, group_id=gid).first():
user_name = user.user_name
else:
user_name = uid

View File

@ -139,7 +139,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
group_id=event.group_id, user_id=event.user_id
)
await GroupInfoUser.update_or_create(
user_qq=str(user_info["user_id"]),
user_id=str(user_info["user_id"]),
group_id=str(user_info["group_id"]),
defaults={"user_name": user_info["nickname"], "user_join_time": join_time},
)
@ -187,7 +187,7 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
group_id = event.group_id
operator_id = event.operator_id
if user := await GroupInfoUser.get_or_none(
user_qq=str(event.operator_id), group_id=str(event.group_id)
user_id=str(event.operator_id), group_id=str(event.group_id)
):
operator_name = user.user_name
else:
@ -207,12 +207,14 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
group_manager.delete_group(event.group_id)
return
if user := await GroupInfoUser.get_or_none(
user_qq=str(event.user_id), group_id=str(event.group_id)
user_id=str(event.user_id), group_id=str(event.group_id)
):
user_name = user.user_name
else:
user_name = f"{event.user_id}"
await GroupInfoUser.filter(user_id=str(event.user_id), group_id=str(event.group_id)).delete()
await GroupInfoUser.filter(
user_id=str(event.user_id), group_id=str(event.group_id)
).delete()
logger.info(
f"名称: {user_name} 退出群聊",
"group_decrease_handle",

View File

@ -19,25 +19,25 @@ def init_plugins_settings():
for matcher in get_matchers(True):
try:
if matcher.plugin_name not in plugins2settings_manager.keys():
_plugin = matcher.plugin
try:
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
if plugin_settings := plugin_data.plugin_setting:
if (name := _module.__getattribute__("__zx_plugin_name__")) not in plugin_settings.cmd:
plugin_settings.cmd.append(name)
# 管理员命令
if plugin_data.plugin_type == PluginType.ADMIN:
admin_manager.add_admin_plugin_settings(
matcher.plugin_name, plugin_settings.cmd, plugin_settings.level
)
else:
plugins2settings_manager.add_plugin_settings(
matcher.plugin_name, plugin_settings
)
if _plugin := matcher.plugin:
try:
_module = _plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
if plugin_settings := plugin_data.plugin_setting:
if (name := _module.__getattribute__("__zx_plugin_name__")) not in plugin_settings.cmd:
plugin_settings.cmd.append(name)
# 管理员命令
if plugin_data.plugin_type == PluginType.ADMIN:
admin_manager.add_admin_plugin_settings(
matcher.plugin_name, plugin_settings.cmd, plugin_settings.level
)
else:
plugins2settings_manager.add_plugin_settings(
matcher.plugin_name, plugin_settings
)
except Exception as e:
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}{e}')
plugins2settings_manager.save()

View File

@ -98,26 +98,6 @@ async def _(bot: Bot):
logger.info(f"移除不存在的群聊信息", group_id=group_id)
# async def __database_script(_flag: List[str]):
# # bag_user 将文本转为字典格式
# if "bag_users" in _flag:
# for x in await BagUser.get_all_users():
# props = {}
# if x.props:
# for prop in [p for p in x.props.split(",") if p]:
# if props.get(prop):
# props[prop] += 1
# else:
# props[prop] = 1
# logger.info(
# f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
# )
# await x.update(
# property=props,
# props="",
# ).apply()
# 自动更新城市列表
@scheduler.scheduled_job(
"cron",

View File

@ -9,6 +9,7 @@ from nonebot.typing import T_State
from configs.config import Config
from models.level_user import LevelUser
from services.log import logger
from utils.depends import GetConfig
from utils.image_utils import text2image
from utils.manager import group_manager
from utils.message_builder import image
@ -89,7 +90,12 @@ async def _():
@add_sub.handle()
@del_sub.handle()
async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
async def _(
event: MessageEvent,
state: T_State,
arg: Message = CommandArg(),
sub_level: Optional[int] = GetConfig(config="GROUP_BILIBILI_SUB_LEVEL"),
):
msg = arg.extract_plain_text().strip().split()
if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...")
@ -99,10 +105,10 @@ async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
if not await LevelUser.check_level(
event.user_id,
event.group_id,
Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"), # type: ignore
sub_level, # type: ignore
):
await add_sub.finish(
f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..",
f"您的权限不足,群内订阅的需要 {sub_level} 级权限..",
at_sender=True,
)
sub_user = f"{event.user_id}:{event.group_id}"
@ -155,9 +161,10 @@ async def _(
else:
await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加订阅:{sub_type} -> {sub_user} -> {id_}"
f"添加订阅:{sub_type} -> {sub_user} -> {id_}",
"添加订阅",
event.user_id,
getattr(event, "group_id", None),
)
@ -171,20 +178,27 @@ async def _(
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["主播", "直播"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "live")
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user, "live")
elif sub_type.lower() in ["up", "用户"]:
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user, "up")
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user, "up")
else:
result = await BilibiliSub.delete_bilibili_sub(int(id_), sub_user)
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user)
if result:
await del_sub.send(f"删除订阅id{id_} 成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
f"删除订阅 {id_}",
"删除订阅",
event.user_id,
getattr(event, "group_id", None),
)
else:
await del_sub.send(f"删除订阅id{id_} 失败...")
logger.info(
f"删除订阅 {id_} 失败",
"删除订阅",
event.user_id,
getattr(event, "group_id", None),
)
@show_sub_info.handle()
@ -236,18 +250,18 @@ async def _():
bot = get_bot()
sub = None
if bot:
# try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.debug(f"Bilibili订阅开始检测{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst or "", sub, bot) # type: ignore
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
try:
logger.debug(f"Bilibili订阅开始检测{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst or "", sub, bot) # type: ignore
# except Exception as e:
# logger.error(f"B站订阅推送发生错误 sub_id{sub.sub_id if sub else 0} {type(e)}{e}")
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst or "", sub, bot) # type: ignore
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id{sub.sub_id}", e=e)
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
@ -284,4 +298,4 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id{sub.sub_id} {type(e)}{e}")
logger.error(f"B站订阅推送发生错误 sub_id{sub.sub_id}", e=e)

View File

@ -38,12 +38,16 @@ async def add_live_sub(live_id: str, sub_user: str) -> str:
:return:
"""
try:
if await BilibiliSub.exists(
sub_type="live", sub_id=live_id, sub_users__contains=sub_user + ","
):
return "该订阅Id已存在..."
try:
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(live_id)
except ResponseCodeError:
return f"未找到房间号Id{live_id} 的信息请检查Id是否正确"
uid = live_info["uid"]
uid = str(live_info["uid"])
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
@ -56,7 +60,10 @@ async def add_live_sub(live_id: str, sub_user: str) -> str:
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(room_id)
try:
await _get_up_status(room_id)
except Exception as e:
logger.error(f"获取主播UP信息失败: {live_id} 错误", e=e)
if data := await BilibiliSub.get_or_none(sub_id=room_id):
uname = data.uname
return (
@ -66,11 +73,9 @@ async def add_live_sub(live_id: str, sub_user: str) -> str:
f"\tlive_id{room_id}\n"
f"\tuid{uid}"
)
return "添加订阅失败..."
else:
return "添加订阅失败..."
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id{live_id} 发生了错误 {type(e)}{e}")
logger.error(f"订阅主播live_id: {live_id} 错误", e=e)
return "添加订阅失败..."
@ -80,7 +85,14 @@ async def add_up_sub(uid: str, sub_user: str) -> str:
:param uid: UP uid
:param sub_user: 订阅用户
"""
uname = uid
dynamic_upload_time = 0
latest_video_created = 0
try:
if await BilibiliSub.exists(
sub_type="up", sub_id=uid, sub_users__contains=sub_user + ","
):
return "该订阅Id已存在..."
try:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await get_user_card(uid)
@ -89,29 +101,26 @@ async def add_up_sub(uid: str, sub_user: str) -> str:
uname = user_info["name"]
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(int(uid))
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(int(uid))
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.sub_handle(
uid,
"up",
sub_user,
uid=int(uid),
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP\n" f"\tname: {uname}\n" f"\tuid{uid}"
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅Up uid{uid} 发生了错误 {type(e)}{e}")
return "添加订阅失败..."
logger.error(f"订阅Up uid: {uid} 错误", e=e)
if await BilibiliSub.sub_handle(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP\n" f"\tname: {uname}\n" f"\tuid{uid}"
else:
return "添加订阅失败..."
async def add_season_sub(media_id: str, sub_user: str) -> str:
@ -121,6 +130,10 @@ async def add_season_sub(media_id: str, sub_user: str) -> str:
:param sub_user: 订阅用户
"""
try:
if await BilibiliSub.exists(
sub_type="season", sub_id=media_id, sub_users__contains=sub_user + ","
):
return "该订阅Id已存在..."
try:
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
season_info = await get_meta(media_id)
@ -145,7 +158,7 @@ async def add_season_sub(media_id: str, sub_user: str) -> str:
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id{media_id} 发生了错误 {type(e)}{e}")
logger.error(f"订阅番剧 media_id: {media_id} 错误", e=e)
return "添加订阅失败..."
@ -155,7 +168,7 @@ async def delete_sub(sub_id: str, sub_user: str) -> str:
:param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
"""
if await BilibiliSub.delete_bilibili_sub(int(sub_id), sub_user):
if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user):
return f"已成功取消订阅:{sub_id}"
else:
return f"取消订阅:{sub_id} 失败请检查是否订阅过该Id...."
@ -204,12 +217,11 @@ async def get_sub_status(id_: str, sub_type: str) -> Optional[str]:
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeError as msg:
logger.info(f"Id{id_} 获取信息失败...{msg}")
return None
except ResponseCodeError as e:
logger.error(f"Id{id_} 获取信息失败...", e=e)
# return f"Id{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_{id_} {type(e)}{e}")
except Exception as e:
logger.error(f"获取订阅状态发生预料之外的错误 Id_{id_}", e=e)
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@ -250,7 +262,7 @@ async def _get_up_status(id_: str) -> Optional[str]:
user_info = await get_user_card(_user.uid)
uname = user_info["name"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
video_info = await get_videos(_user.uid)
video_info = await get_videos(int(_user.uid))
latest_video_created = 0
video = None
dividing_line = "\n-------------\n"
@ -308,7 +320,7 @@ async def _get_season_status(id_: str) -> Optional[str]:
async def get_user_dynamic(
uid: int, local_user: BilibiliSub
uid: str, local_user: BilibiliSub
) -> Tuple[Optional[MessageSegment], int, str]:
"""
获取用户动态
@ -317,7 +329,7 @@ async def get_user_dynamic(
:return: 最新动态截图与时间
"""
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(uid)
dynamic_info = await dynamic.get_user_dynamics(int(uid))
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
dynamic_id = dynamic_info["cards"][0]["desc"]["dynamic_id"]

View File

@ -21,7 +21,7 @@ class BilibiliSub(Model):
"""直播短id"""
live_status = fields.IntField(null=True)
"""直播状态 0: 停播 1: 直播"""
uid = fields.BigIntField(null=True)
uid = fields.CharField(255, null=True)
"""主播/UP UID"""
uname = fields.CharField(255, null=True)
"""主播/UP 名称"""
@ -53,7 +53,7 @@ class BilibiliSub(Model):
live_short_id: Optional[str] = None,
live_status: Optional[int] = None,
dynamic_upload_time: int = 0,
uid: Optional[int] = None,
uid: Optional[str] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
@ -80,59 +80,63 @@ class BilibiliSub(Model):
:param season_update_time: 番剧更新时间
"""
sub_id = str(sub_id)
# try:
data = {
"sub_type": sub_type,
"sub_user": sub_user,
"live_short_id": live_short_id,
"live_status": live_status,
"dynamic_upload_time": dynamic_upload_time,
"uid": uid,
"uname": uname,
"latest_video_created": latest_video_created,
"season_name": season_name,
"season_id": season_id,
"season_current_episode": season_current_episode,
"season_update_time": season_update_time,
}
if sub_user:
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
sub = None
if sub_type:
sub = await cls.get_or_none(sub_id=sub_id, sub_type=sub_type)
else:
sub = await cls.get_or_none(sub_id=sub_id)
if sub:
sub_users = sub.sub_users + sub_user
data["sub_type"] = sub_type or sub.sub_type
data["sub_users"] = sub_users
data["live_short_id"] = live_short_id or sub.live_short_id
data["live_status"] = (
live_status if live_status is not None else sub.live_status
)
data["dynamic_upload_time"] = dynamic_upload_time or sub.dynamic_upload_time
data["uid"] = uid or sub.uid
data["uname"] = uname or sub.uname
data["latest_video_created"] = (
latest_video_created or sub.latest_video_created
)
data["season_name"] = season_name or sub.season_name
data["season_id"] = season_id or sub.season_id
data["season_current_episode"] = (
season_current_episode or sub.season_current_episode
)
data["season_update_time"] = season_update_time or sub.season_update_time
else:
await cls.create(sub_id=sub_id, sub_type=sub_type, sub_users=sub_user)
await cls.update_or_create(sub_id=sub_id, defaults=data)
return True
# except Exception as e:
# logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
# return False
try:
data = {
"sub_type": sub_type,
"sub_user": sub_user,
"live_short_id": live_short_id,
"live_status": live_status,
"dynamic_upload_time": dynamic_upload_time,
"uid": uid,
"uname": uname,
"latest_video_created": latest_video_created,
"season_name": season_name,
"season_id": season_id,
"season_current_episode": season_current_episode,
"season_update_time": season_update_time,
}
if sub_user:
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
sub = None
if sub_type:
sub = await cls.get_or_none(sub_id=sub_id, sub_type=sub_type)
else:
sub = await cls.get_or_none(sub_id=sub_id)
if sub:
sub_users = sub.sub_users + sub_user
data["sub_type"] = sub_type or sub.sub_type
data["sub_users"] = sub_users
data["live_short_id"] = live_short_id or sub.live_short_id
data["live_status"] = (
live_status if live_status is not None else sub.live_status
)
data["dynamic_upload_time"] = (
dynamic_upload_time or sub.dynamic_upload_time
)
data["uid"] = uid or sub.uid
data["uname"] = uname or sub.uname
data["latest_video_created"] = (
latest_video_created or sub.latest_video_created
)
data["season_name"] = season_name or sub.season_name
data["season_id"] = season_id or sub.season_id
data["season_current_episode"] = (
season_current_episode or sub.season_current_episode
)
data["season_update_time"] = (
season_update_time or sub.season_update_time
)
else:
await cls.create(sub_id=sub_id, sub_type=sub_type, sub_users=sub_user)
await cls.update_or_create(sub_id=sub_id, defaults=data)
return True
except Exception as e:
logger.error(f"添加订阅 Id: {sub_id} 错误", e=e)
return False
@classmethod
async def delete_bilibili_sub(
cls, sub_id: int, sub_user: str, sub_type: Optional[str] = None
cls, sub_id: str, sub_user: str, sub_type: Optional[str] = None
) -> bool:
"""
说明:
@ -142,24 +146,34 @@ class BilibiliSub(Model):
:param sub_user: 删除此条目的用户
"""
try:
group_id = None
contains_str = sub_user
if ":" in sub_user:
group_id = sub_user.split(":")[1]
contains_str = f":{group_id}"
if sub_type:
sub = await cls.filter(
sub_id=sub_id, sub_type=sub_type, sub_users__contains=sub_user
).first()
sub = await cls.get_or_none(
sub_id=sub_id, sub_type=sub_type, sub_users__contains=contains_str
)
else:
sub = await cls.filter(
sub_id=sub_id, sub_users__contains=sub_user
).first()
sub = await cls.get_or_none(
sub_id=sub_id, sub_users__contains=contains_str
)
if not sub:
return False
sub.sub_users = sub.sub_users.replace(f"{sub_user},", "")
if group_id:
sub.sub_users = ",".join(
[s for s in sub.sub_users.split(",") if f":{group_id}" not in s]
)
else:
sub.sub_users = sub.sub_users.replace(f"{sub_user},", "")
if sub.sub_users.strip():
await sub.save(update_fields=["sub_users"])
else:
await sub.delete()
return True
except Exception as e:
logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}")
logger.error(f"bilibili_sub 删除订阅错误", target=sub_id, e=e)
return False
@classmethod
@ -189,4 +203,5 @@ class BilibiliSub(Model):
"ALTER TABLE bilibili_sub ALTER COLUMN season_update_time TYPE timestamp with time zone USING season_update_time::timestamp with time zone;",
"alter table bilibili_sub alter COLUMN sub_id type varchar(255);", # 将sub_id字段改为字符串
"alter table bilibili_sub alter COLUMN live_short_id type varchar(255);", # 将live_short_id字段改为字符串
"alter table bilibili_sub alter COLUMN uid type varchar(255);", # 将live_short_id字段改为字符串
]

View File

@ -96,7 +96,7 @@ Config.add_plugin_config(
"BAN_3_DURATION",
7,
help_="Union[int, List[int, int]]Ban时长三级惩罚可以为指定数字或指定列表区间(随机),例如 [7, 30]",
default_value=360,
default_value=7,
type=int,
)
@ -171,8 +171,8 @@ async def _(
and event.is_tome()
and not msg.startswith("原神绑定")
):
# if str(event.user_id) not in bot.config.superusers:
# return logger.debug(f"超级用户跳过黑名单词汇检查 Message: {msg}", target=event.user_id)
if str(event.user_id) in bot.config.superusers:
return logger.debug(f"超级用户跳过黑名单词汇检查 Message: {msg}", target=event.user_id)
if (
event.is_tome()
and matcher.plugin_name == "black_word"
@ -184,8 +184,8 @@ async def _(
and group_manager.get_group_level(event.group_id) < 0
):
return
user_id = event.user_id
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
user_id = str(event.user_id)
group_id = str(event.group_id) if isinstance(event, GroupMessageEvent) else None
msg = get_message_text(event.json())
if await black_word_manager.check(
user_id, group_id, msg
@ -206,8 +206,8 @@ async def _(bot: Bot, reg_group: Tuple[Any, ...] = RegexGroup()):
await show_black.finish("日期格式错误,需要:年-月-日")
pic = await show_black_text_image(
bot,
int(user_id.split(":")[1]) if user_id else None,
int(group_id.split(":")[1]) if group_id else None,
user_id.split(":")[1] if user_id else None,
group_id.split(":")[1] if group_id else None,
date,
date_type,
)
@ -266,11 +266,13 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
or not is_number(msg[2])
):
await set_punish.finish("参数错误,请查看帮助...", at_sender=True)
uid = int(msg[0])
uid = msg[0]
id_ = int(msg[1])
punish_level = int(msg[2])
rst = await set_user_punish(uid, id_, punish_level)
await set_punish.send(rst)
logger.info(
f"USER {event.user_id} 设置惩罚 uid{uid} id_{id_} punish_level:{punish_level} --> {rst}"
f"设置惩罚 uid{uid} id_{id_} punish_level:{punish_level} --> {rst}",
"设置惩罚",
event.user_id,
)

View File

@ -1,16 +1,19 @@
from nonebot.adapters.onebot.v11 import Bot
from utils.image_utils import BuildImage, text2image
from services.log import logger
from typing import Optional
from datetime import datetime
from typing import Optional
from nonebot.adapters.onebot.v11 import Bot
from services.log import logger
from utils.image_utils import BuildImage, text2image
from .model import BlackWord
from .utils import _get_punish, Config
from .utils import Config, _get_punish
async def show_black_text_image(
bot: Bot,
user: Optional[int],
group_id: Optional[int],
user_id: Optional[str],
group_id: Optional[str],
date: Optional[datetime],
data_type: str = "=",
) -> BuildImage:
@ -23,7 +26,7 @@ async def show_black_text_image(
:param data_type: 日期搜索类型
:return:
"""
data = await BlackWord.get_black_data(user, group_id, date, data_type)
data = await BlackWord.get_black_data(user_id, group_id, date, data_type)
A = BuildImage(0, 0, color="#f9f6f2", font_size=20)
image_list = []
friend_str = await bot.get_friend_list()
@ -41,21 +44,21 @@ async def show_black_text_image(
if x.group_id:
user_name = (
await bot.get_group_member_info(
group_id=x.group_id, user_id=x.user_qq
group_id=int(x.group_id), user_id=int(x.user_id)
)
)["card"]
else:
user_name = [
u["nickname"] for u in friend_str if u["user_id"] == x.user_qq
u["nickname"] for u in friend_str if u["user_id"] == int(x.user_id)
][0]
except Exception as e:
logger.warning(
f"show_black_text_image 获取 USER {x.user_qq} user_name 失败 {type(e)}{e}"
f"show_black_text_image 获取 USER {x.user_id} user_name 失败", e=e
)
user_name = x.user_qq
user_name = x.user_id
id_str += f"{i}\n"
uname_str += f"{user_name}\n"
uid_str += f"{x.user_qq}\n"
uid_str += f"{x.user_id}\n"
gid_str += f"{x.group_id}\n"
plant_text = " ".join(x.plant_text.split("\n"))
if A.getsize(plant_text)[0] > 200:
@ -97,7 +100,7 @@ async def show_black_text_image(
return A
async def set_user_punish(user_id: int, id_: int, punish_level: int) -> str:
async def set_user_punish(user_id: str, id_: int, punish_level: int) -> str:
"""
设置惩罚
:param user_id: 用户id

View File

@ -7,13 +7,12 @@ from services.db_context import Model
class BlackWord(Model):
# __tablename__ = "black_word"
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField(null=True)
group_id = fields.CharField(255, null=True)
"""群聊id"""
plant_text = fields.TextField()
"""检测文本"""
@ -33,7 +32,7 @@ class BlackWord(Model):
@classmethod
async def set_user_punish(
cls,
user_qq: int,
user_id: str,
punish: str,
black_word: Optional[str] = None,
id_: Optional[int] = None,
@ -42,22 +41,22 @@ class BlackWord(Model):
说明:
设置处罚
参数:
:param user_qq: 用户id
:param user_id: 用户id
:param punish: 处罚
:param black_word: 黑名单词汇
:param id_: 记录下标
"""
user = None
if (not black_word and not id_) or not punish:
if (not black_word and id_ is None) or not punish:
return False
if black_word:
user = (
await cls.filter(user_qq=user_qq, black_word=black_word)
await cls.filter(user_id=user_id, black_word=black_word, punish="")
.order_by("id")
.first()
)
elif id_:
user_list = await cls.filter(user_qq=user_qq).order_by("id").all()
elif id_ is not None:
user_list = await cls.filter(user_id=user_id).order_by("id").all()
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
return False
user = user_list[id_]
@ -69,18 +68,18 @@ class BlackWord(Model):
@classmethod
async def get_user_count(
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
cls, user_id: str, days: int = 7, punish_level: Optional[int] = None
) -> int:
"""
说明:
获取用户规定周期内的犯事次数
参数:
:param user_qq: 用户qq
:param user_id: 用户id
:param days: 周期天数
:param punish_level: 惩罚等级
"""
query = cls.filter(
user_qq=user_qq,
user_id=user_id,
create_time__gte=datetime.now() - timedelta(days=days),
punish_level__not_in=[-1],
)
@ -89,17 +88,17 @@ class BlackWord(Model):
return await query.count()
@classmethod
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
async def get_user_punish_level(cls, user_id: str, days: int = 7) -> Optional[int]:
"""
说明:
获取用户最近一次的惩罚记录等级
参数:
:param user_qq: 用户qq
:param user_id: 用户id
:param days: 周期天数
"""
if (
user := await cls.filter(
user_qq=user_qq,
user_id=user_id,
create_time__gte=datetime.now() - timedelta(days=days),
)
.order_by("id")
@ -111,8 +110,8 @@ class BlackWord(Model):
@classmethod
async def get_black_data(
cls,
user_qq: Optional[int],
group_id: Optional[int],
user_id: Optional[str],
group_id: Optional[str],
date: Optional[datetime],
date_type: str = "=",
) -> List["BlackWord"]:
@ -120,14 +119,14 @@ class BlackWord(Model):
说明:
通过指定条件查询数据
参数:
:param user_qq: 用户qq
:param user_id: 用户id
:param group_id: 群号
:param date: 日期
:param date_type: 日期查询类型
"""
query = cls
if user_qq:
query = query.filter(user_qq=user_qq)
if user_id:
query = query.filter(user_id=user_id)
if group_id:
query = query.filter(group_id=group_id)
if date:
@ -139,4 +138,12 @@ class BlackWord(Model):
query = query.filter(create_time__gte=date)
elif date_type == "<":
query = query.filter(create_time__lte=date)
return await query.order_by("id").all()
return await query.all().order_by("id") # type: ignore
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE black_word RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE black_word ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE black_word ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -83,7 +83,7 @@ class BlackWordManager:
)
async def check(
self, user_id: int, group_id: Optional[int], message: str
self, user_id: str, group_id: Optional[str], message: str
) -> Optional[Union[str, bool]]:
"""
检查是否包含黑名单词汇
@ -91,6 +91,7 @@ class BlackWordManager:
:param group_id: 群号
:param message: 消息
"""
logger.debug(f"检查文本是否含有黑名单词汇: {message}", "敏感词检测", user_id, group_id)
if data := self._check(message):
if data[0]:
await _add_user_black_word(
@ -117,7 +118,7 @@ class BlackWordManager:
for x in [self._word_list, self._py_list]:
for level in x:
if message in x[level] or py_msg in x[level]:
return message if message in x[level] else py_msg, level
return message if message in x[level] else py_msg, int(level)
# 模糊匹配
for x in [self._word_list, self._py_list]:
for level in x:
@ -128,8 +129,8 @@ class BlackWordManager:
async def _add_user_black_word(
user_id: int,
group_id: Optional[int],
user_id: str,
group_id: Optional[str],
black_word: str,
message: str,
punish_level: int,
@ -144,13 +145,17 @@ async def _add_user_black_word(
"""
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
add_punish_level_to_count = Config.get_config(
"black_word", "ADD_PUNISH_LEVEL_TO_COUNT"
)
# 周期内超过次数直接提升惩罚
if Config.get_config(
"black_word", "AUTO_ADD_PUNISH_LEVEL"
) and user_count > Config.get_config("black_word", "ADD_PUNISH_LEVEL_TO_COUNT"):
if (
Config.get_config("black_word", "AUTO_ADD_PUNISH_LEVEL")
and add_punish_level_to_count
):
punish_level -= 1
await BlackWord.create(
user_qq=user_id,
user_id=user_id,
group_id=group_id,
plant_text=message,
black_word=black_word,
@ -165,7 +170,7 @@ async def _add_user_black_word(
async def _punish_handle(
user_id: int, group_id: Optional[int], punish_level: int, black_word: str
user_id: str, group_id: Optional[str], punish_level: int, black_word: str
):
"""
惩罚措施级别越低惩罚越严
@ -218,7 +223,7 @@ async def _punish_handle(
async def _get_punish(
id_: int, user_id: int, group_id: Optional[int] = None
id_: int, user_id: str, group_id: Optional[str] = None
) -> Optional[Union[int, str]]:
"""
通过id_获取惩罚
@ -230,12 +235,12 @@ async def _get_punish(
# 忽略的群聊
# _ignore_group = Config.get_config("black_word", "IGNORE_GROUP")
# 处罚 id 4 ban 时间intList[int]
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION")
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION") or 7
# 处罚 id 4 ban 时间intList[int]
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION")
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION") or 360
# 口头警告内容
warning_result = Config.get_config("black_word", "WARNING_RESULT")
if user := await GroupInfoUser.get_or_none(user_id=str(user_id), group_id=str(group_id)):
if user := await GroupInfoUser.get_or_none(user_id=user_id, group_id=group_id):
uname = user.user_name
else:
uname = user_id
@ -285,28 +290,30 @@ async def _get_punish(
# 口头警告
elif id_ == 5:
if group_id:
await bot.send_group_msg(group_id=group_id, message=warning_result)
await bot.send_group_msg(group_id=int(group_id), message=warning_result)
else:
await bot.send_private_msg(user_id=user_id, message=warning_result)
await bot.send_private_msg(user_id=int(user_id), message=warning_result)
logger.info(f"BlackWord 口头警告 USER {user_id}")
return warning_result
return None
async def send_msg(user_id: int, group_id: Optional[int], message: str):
async def send_msg(
user_id: Union[str, int], group_id: Optional[Union[str, int]], message: str
):
"""
发送消息
:param user_id: user_id
:param group_id: group_id
:param message: message
"""
bot = get_bot()
if not user_id:
user_id = int(list(bot.config.superusers)[0])
if group_id:
await bot.send_group_msg(group_id=group_id, message=message)
else:
await bot.send_private_msg(user_id=user_id, message=message)
if bot := get_bot():
if not user_id:
user_id = list(bot.config.superusers)[0]
if group_id:
await bot.send_group_msg(group_id=int(group_id), message=message)
else:
await bot.send_private_msg(user_id=int(user_id), message=message)
async def check_text(text: str) -> bool:

View File

@ -3,7 +3,7 @@ from bs4 import BeautifulSoup
from configs.config import Config
from utils.http_utils import AsyncHttpx
url = "http://www.eclzz.love"
url = "http://www.eclzz.ink"
async def get_bt_info(keyword: str, page: int):
@ -17,7 +17,7 @@ async def get_bt_info(keyword: str, page: int):
return
soup = BeautifulSoup(text, "lxml")
item_lst = soup.find_all("div", {"class": "search-item"})
bt_max_num = Config.get_config("bt", "BT_MAX_NUM")
bt_max_num = Config.get_config("bt", "BT_MAX_NUM") or 10
bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst)
for item in item_lst[:bt_max_num]:
divs = item.find_all("div")

View File

@ -13,7 +13,7 @@ class Genshin(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
uid = fields.BigIntField()
"""uid"""
@ -43,7 +43,7 @@ class Genshin(Model):
class Meta:
table = "genshin"
table_description = "原神数据表"
unique_together = ("user_qq", "uid")
unique_together = ("user_id", "uid")
@classmethod
async def random_sign_time(cls, uid: int) -> Optional[datetime]:
@ -103,4 +103,6 @@ class Genshin(Model):
"ALTER TABLE genshin ADD login_ticket VARCHAR(255) DEFAULT '';",
"ALTER TABLE genshin ADD stuid VARCHAR(255) DEFAULT '';",
"ALTER TABLE genshin ADD stoken VARCHAR(255) DEFAULT '';",
"ALTER TABLE genshin RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE genshin ALTER COLUMN user_id TYPE character varying(255);",
]

View File

@ -55,7 +55,7 @@ bbs_Cookie_url2 = (
@bind.handle()
async def _(event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
user = await Genshin.get_or_none(user_qq=event.user_id)
user = await Genshin.get_or_none(user_id=str(event.user_id))
if cmd in ["原神绑定uid", "原神绑定米游社id"]:
if not is_number(msg):
await bind.finish("uid/id必须为纯数字", at_senders=True)
@ -63,9 +63,9 @@ async def _(event: MessageEvent, cmd: str = OneCommand(), arg: Message = Command
if cmd == "原神绑定uid":
if user:
await bind.finish(f"您已绑定过uid{user.uid}如果希望更换uid请先发送原神解绑")
if await Genshin.get_or_none(user_qq=event.user_id, uid=msg):
if await Genshin.get_or_none(user_id=str(event.user_id), uid=msg):
await bind.finish("添加失败该uid可能已存在...")
user = await Genshin.create(user_qq=event.user_id, uid=msg)
user = await Genshin.create(user_id=str(event.user_id), uid=msg)
_x = f"已成功添加原神uid{msg}"
elif cmd == "原神绑定米游社id":
if not user:
@ -144,7 +144,7 @@ async def _(event: MessageEvent, cmd: str = OneCommand(), arg: Message = Command
@unbind.handle()
async def _(event: MessageEvent):
await Genshin.filter(user_qq=event.user_id).delete()
await Genshin.filter(user_id=str(event.user_id)).delete()
await unbind.send("用户数据删除成功...")
logger.info(
f"(USER {event.user_id}, GROUP "

View File

@ -43,7 +43,7 @@ genshin_matcher = on_command(
@genshin_matcher.handle()
async def _(event: MessageEvent, cmd: str = OneCommand()):
user = await Genshin.get_or_none(user_qq=event.user_id)
user = await Genshin.get_or_none(user_id=str(event.user_id))
if not user:
await genshin_matcher.finish("请先绑定user.uid...")
if cmd == "查看我的cookie":

View File

@ -31,11 +31,11 @@ async def _():
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
id=f"genshin_auto_sign_{u.uid}_{u.user_id}_0",
args=[u.user_id, u.uid, 0],
)
logger.info(
f"genshin_sign add_jobUSER{u.user_qq} UID{u.uid} "
f"genshin_sign add_jobUSER{u.user_id} UID{u.uid} "
f"{date} 原神自动签到"
)

View File

@ -47,7 +47,7 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
async def mihoyobbs_sign(user_id):
user = await Genshin.get_or_none(user_qq=user_id)
user = await Genshin.get_or_none(user_id=str(user_id))
if not user or not user.uid or not user.cookie:
await mihoyobbs_matcher.finish("请先绑定uid和cookie", at_sender=True)
bbs = mihoyobbs.Mihoyobbs(stuid=user.stuid, stoken=user.stoken, cookie=user.cookie)

View File

@ -35,7 +35,7 @@ query_memo_matcher = on_command(
@query_memo_matcher.handle()
async def _(event: MessageEvent):
user = await Genshin.get_or_none(user_qq=event.user_id)
user = await Genshin.get_or_none(user_id=str(event.user_id))
if not user or not user.uid or not user.cookie:
await query_memo_matcher.finish("请先绑定uid和cookie")
if isinstance(event, GroupMessageEvent):

View File

@ -44,7 +44,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
await query_role_info_matcher.finish("查询uid必须为数字")
msg = int(msg)
uid = None
user = await Genshin.get_or_none(user_qq=event.user_id)
user = await Genshin.get_or_none(user_id=str(event.user_id))
if not msg and user:
uid = user.uid
else:

View File

@ -52,7 +52,7 @@ resin_remind = on_command("开原神树脂提醒", aliases={"关原神树脂提
@resin_remind.handle()
async def _(event: MessageEvent, cmd: str = OneCommand()):
user = await Genshin.get_or_none(user_qq=event.user_id)
user = await Genshin.get_or_none(user_id=str(event.user_id))
if not user or not user.uid or not user.cookie:
await resin_remind.finish("请先绑定uid和cookie")
try:

View File

@ -88,35 +88,27 @@ async def _():
if u.resin_recovery_time and u.resin_recovery_time > datetime.now(
pytz.timezone("Asia/Shanghai")
):
# date = await Genshin.get_user_resin_recovery_time(u.uid) # 不能要,因为可能在这期间用户使用了树脂
add_job(u.user_qq, u.uid)
# scheduler.add_job(
# _remind,
# "date",
# run_date=date.replace(microsecond=0),
# id=f"genshin_resin_remind_{u.uid}_{u.user_qq}",
# args=[u.user_qq, u.uid],
# )
add_job(u.user_id, u.uid)
logger.info(
f"genshin_resin_remind add_jobUSER{u.user_qq} UID{u.uid}启动原神树脂提醒 "
f"genshin_resin_remind add_jobUSER{u.user_id} UID{u.uid}启动原神树脂提醒 "
)
else:
u.resin_recovery_time = None # type: ignore
update_list.append(u)
add_job(u.user_qq, u.uid)
add_job(u.user_id, u.uid)
logger.info(
f"genshin_resin_remind add_job CHECKUSER{u.user_qq} UID{u.uid}启动原神树脂提醒 "
f"genshin_resin_remind add_job CHECKUSER{u.user_id} UID{u.uid}启动原神树脂提醒 "
)
else:
add_job(u.user_qq, u.uid)
add_job(u.user_id, u.uid)
logger.info(
f"genshin_resin_remind add_job CHECKUSER{u.user_qq} UID{u.uid}启动原神树脂提醒 "
f"genshin_resin_remind add_job CHECKUSER{u.user_id} UID{u.uid}启动原神树脂提醒 "
)
if update_list:
await Genshin.bulk_update(update_list, ["resin_recovery_time"])
def add_job(user_id: int, uid: int):
def add_job(user_id: str, uid: int):
# 移除
try:
scheduler.remove_job(f"genshin_resin_remind_{uid}_{user_id}")
@ -136,7 +128,7 @@ def add_job(user_id: int, uid: int):
async def _remind(user_id: int, uid: str):
user = await Genshin.get_or_none(user_qq=user_id, uid=int(uid))
user = await Genshin.get_or_none(user_id=str(user_id), uid=int(uid))
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"

View File

@ -1,33 +1,36 @@
from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import (
Bot,
ActionFailed,
GroupMessageEvent,
PokeNotifyEvent,
Message
)
from .data_source import (
check_gold,
generate_send_redbag_pic,
open_redbag,
generate_open_redbag_pic,
return_gold,
)
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.matcher import Matcher
from utils.utils import is_number, scheduler
from utils.message_builder import image
from services.log import logger
from configs.path_config import IMAGE_PATH
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from datetime import datetime, timedelta
from configs.config import NICKNAME
from apscheduler.jobstores.base import JobLookupError
from nonebot.params import CommandArg
import random
import time
from datetime import datetime, timedelta
from apscheduler.jobstores.base import JobLookupError
from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
GroupMessageEvent,
Message,
PokeNotifyEvent,
)
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.matcher import Matcher
from nonebot.message import IgnoredException, run_preprocessor
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.config import NICKNAME
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.message_builder import image
from utils.utils import is_number, scheduler
from .data_source import (
check_gold,
generate_open_redbag_pic,
generate_send_redbag_pic,
open_redbag,
return_gold,
)
__zx_plugin_name__ = "金币红包"
__plugin_usage__ = """
@ -72,7 +75,9 @@ gold_redbag = on_command(
"塞红包", aliases={"金币红包"}, priority=5, block=True, permission=GROUP
)
open_ = on_command("", aliases={""}, priority=5, block=True, permission=GROUP, rule=rule)
open_ = on_command(
"", aliases={""}, priority=5, block=True, permission=GROUP, rule=rule
)
poke_ = on_notice(priority=6, block=False)
@ -89,7 +94,10 @@ festive_redbag_data = {}
# 阻断其他poke
@run_preprocessor
async def _(matcher: Matcher, event: PokeNotifyEvent, ):
async def _(
matcher: Matcher,
event: PokeNotifyEvent,
):
try:
if matcher.type == "notice" and event.self_id == event.target_id:
flag = check_on_gold_red(event)
@ -109,10 +117,12 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
try:
if time.time() - redbag_data[event.group_id]["time"] > 60:
amount = (
redbag_data[event.group_id]["amount"]
- redbag_data[event.group_id]["open_amount"]
redbag_data[event.group_id]["amount"]
- redbag_data[event.group_id]["open_amount"]
)
await return_gold(
redbag_data[event.group_id]["user_id"], str(event.group_id), amount
)
await return_gold(redbag_data[event.group_id]["user_id"], event.group_id, amount)
await gold_redbag.send(
f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...'
)
@ -128,18 +138,18 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
msg = msg.split()
if len(msg) == 1:
flag, amount = await check_gold(event.user_id, event.group_id, msg[0])
flag, amount = await check_gold(str(event.user_id), str(event.group_id), msg[0])
if not flag:
await gold_redbag.finish(amount)
await gold_redbag.finish(str(amount))
num = 5
else:
amount = msg[0]
num = msg[1]
if not is_number(num) or int(num) < 1:
await gold_redbag.finish("红包个数给我输正确啊!", at_sender=True)
flag, amount = await check_gold(event.user_id, event.group_id, amount)
flag, amount = await check_gold(str(event.user_id), str(event.group_id), amount)
if not flag:
await gold_redbag.finish(amount, at_sender=True)
await gold_redbag.finish(str(amount), at_sender=True)
group_member_num = (await bot.get_group_info(group_id=event.group_id))[
"member_count"
]
@ -149,7 +159,12 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
num = group_member_num
nickname = event.sender.card or event.sender.nickname
flag, result = init_redbag(
event.user_id, event.group_id, nickname, amount, num, int(bot.self_id)
str(event.user_id),
str(event.group_id),
nickname or str(event.user_id),
amount,
num,
int(bot.self_id),
)
if not flag:
await gold_redbag.finish(result, at_sender=True)
@ -158,13 +173,11 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
f"{nickname}发起了金币红包\n金额:{amount}\n数量:{num}\n"
+ image(
b64=await generate_send_redbag_pic(
redbag_data[event.group_id]["user_id"]
redbag_data[str(event.group_id)]["user_id"]
)
)
)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 塞入 {num} 个红包,共 {amount} 金币"
)
logger.info(f"塞入 {num} 个红包,共 {amount} 金币", "金币红包", event.user_id, event.group_id)
@open_.handle()
@ -173,20 +186,20 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
msg = (
msg.replace("!", "")
.replace("", "")
.replace(",", "")
.replace("", "")
.replace(".", "")
.replace("", "")
.replace("", "")
.replace(",", "")
.replace("", "")
.replace(".", "")
.replace("", "")
)
if msg:
if "红包" not in msg:
return
try:
await open_.send(
image(b64=await get_redbag_img(event.user_id, event.group_id)),
image(b64=await get_redbag_img(str(event.user_id), str(event.group_id))),
at_sender=True,
)
)
except KeyError:
await open_.finish("真贪心,明明已经开过这个红包了的说...", at_sender=True)
@ -199,7 +212,7 @@ async def _poke_(event: PokeNotifyEvent):
if not flag:
return
await poke_.send(
image(b64=await get_redbag_img(event.user_id, event.group_id)),
image(b64=await get_redbag_img(str(event.user_id), str(event.group_id))),
at_sender=True,
)
@ -217,8 +230,8 @@ async def _(event: GroupMessageEvent):
at_sender=True,
)
await return_gold(
event.user_id,
event.group_id,
str(event.user_id),
str(event.group_id),
redbag_data[event.group_id]["amount"]
- redbag_data[event.group_id]["open_amount"],
)
@ -272,9 +285,7 @@ async def _(bot: Bot, arg: Message = CommandArg()):
await end_festive_redbag(bot, g)
except JobLookupError:
pass
init_redbag(
int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1
)
init_redbag(bot.self_id, g, f"{NICKNAME}", amount, num, int(bot.self_id), 1)
scheduler.add_job(
end_festive_redbag,
"date",
@ -286,7 +297,7 @@ async def _(bot: Bot, arg: Message = CommandArg()):
await bot.send_group_msg(
group_id=g,
message=f"{NICKNAME}发起了金币红包\n金额:{amount}\n数量:{num}\n"
+ image(
+ image(
b64=await generate_send_redbag_pic(int(bot.self_id), greetings)
),
)
@ -297,13 +308,13 @@ async def _(bot: Bot, arg: Message = CommandArg()):
# 红包数据初始化
def init_redbag(
user_id: int,
group_id: int,
nickname: str,
amount: int,
num: int,
bot_self_id: int,
mode: int = 0,
user_id: str,
group_id: str,
nickname: str,
amount: int,
num: int,
bot_self_id: int,
mode: int = 0,
):
global redbag_data, festive_redbag_data
data = redbag_data if mode == 0 else festive_redbag_data
@ -341,7 +352,7 @@ def random_redbag(amount: int, num: int) -> list:
# 返回开红包图片
async def get_redbag_img(user_id: int, group_id: int):
async def get_redbag_img(user_id: str, group_id: str):
global redbag_data, festive_redbag_data
data = redbag_data
mode = 0
@ -389,17 +400,14 @@ def check_on_gold_red(event) -> bool:
flag1 = True
flag2 = True
try:
if festive_redbag_data[event.group_id]["user_id"]:
if (
event.user_id
in festive_redbag_data[event.group_id]["open_user"]
):
if festive_redbag_data[str(event.group_id)]["user_id"]:
if event.user_id in festive_redbag_data[str(event.group_id)]["open_user"]:
flag1 = False
except KeyError:
flag1 = False
try:
if redbag_data[event.group_id]["user_id"]:
if event.user_id in redbag_data[event.group_id]["open_user"]:
if redbag_data[str(event.group_id)]["user_id"]:
if event.user_id in redbag_data[str(event.group_id)]["open_user"]:
flag2 = False
except KeyError:
flag2 = False

View File

@ -1,87 +1,108 @@
from models.bag_user import BagUser
from utils.utils import is_number, get_user_avatar
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
from .model import RedbagUser
import random
import os
from io import BytesIO
import asyncio
import os
import random
from io import BytesIO
from typing import Union
from configs.path_config import IMAGE_PATH
from models.bag_user import BagUser
from utils.image_utils import BuildImage
from utils.utils import get_user_avatar, is_number
from .model import RedbagUser
# 检查金币数量合法性,并添加记录数据
async def check_gold(user_id: int, group_id: int, amount: str):
async def check_gold(user_id: str, group_id: str, amount: Union[str, int]):
if is_number(amount):
amount = int(amount)
user_gold = await BagUser.get_gold(user_id, group_id)
if amount < 1:
return False, '小气鬼,要别人倒贴金币给你嘛!'
return False, "小气鬼,要别人倒贴金币给你嘛!"
if user_gold < amount:
return False, '没有金币的话请不要发红包...'
return False, "没有金币的话请不要发红包..."
await BagUser.spend_gold(user_id, group_id, amount)
await RedbagUser.add_redbag_data(user_id, group_id, 'send', amount)
await RedbagUser.add_redbag_data(user_id, group_id, "send", amount)
return True, amount
else:
return False, '给我好好的输入红包里金币的数量啊喂!'
return False, "给我好好的输入红包里金币的数量啊喂!"
# 金币退回
async def return_gold(user_id: int, group_id: int, amount: int):
async def return_gold(user_id: str, group_id: str, amount: int):
await BagUser.add_gold(user_id, group_id, amount)
# 开红包
async def open_redbag(user_id: int, group_id: int, redbag_data: dict):
amount = random.choice(redbag_data[group_id]['redbag'])
redbag_data[group_id]['redbag'].remove(amount)
redbag_data[group_id]['open_user'].append(user_id)
redbag_data[group_id]['open_amount'] += amount
await RedbagUser.add_redbag_data(user_id, group_id, 'get', amount)
async def open_redbag(user_id: str, group_id: str, redbag_data: dict):
amount = random.choice(redbag_data[group_id]["redbag"])
redbag_data[group_id]["redbag"].remove(amount)
redbag_data[group_id]["open_user"].append(user_id)
redbag_data[group_id]["open_amount"] += amount
await RedbagUser.add_redbag_data(user_id, group_id, "get", amount)
await BagUser.add_gold(user_id, group_id, amount)
return amount, redbag_data
# 随机红包图片
async def generate_send_redbag_pic(user_id: int, msg: str = '恭喜发财 大吉大利'):
async def generate_send_redbag_pic(user_id: int, msg: str = "恭喜发财 大吉大利"):
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_2"))
redbag = BuildImage(0, 0, font_size=38, background=f'{IMAGE_PATH}/prts/redbag_2/{random_redbag}')
redbag = BuildImage(
0, 0, font_size=38, background=f"{IMAGE_PATH}/prts/redbag_2/{random_redbag}"
)
ava = BuildImage(65, 65, background=BytesIO(await get_user_avatar(user_id)))
await asyncio.get_event_loop().run_in_executor(None, ava.circle)
redbag.text((int((redbag.size[0] - redbag.getsize(msg)[0]) / 2), 210), msg, (240, 218, 164))
redbag.paste(ava, (int((redbag.size[0] - ava.size[0])/2), 130), True)
redbag.text(
(int((redbag.size[0] - redbag.getsize(msg)[0]) / 2), 210), msg, (240, 218, 164)
)
redbag.paste(ava, (int((redbag.size[0] - ava.size[0]) / 2), 130), True)
return redbag.pic2bs4()
# 开红包图片
async def generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
return await asyncio.create_task(_generate_open_redbag_pic(user_id, send_user_nickname, amount, text))
async def generate_open_redbag_pic(
user_id: str, send_user_nickname: str, amount: int, text: str
):
return await asyncio.create_task(
_generate_open_redbag_pic(user_id, send_user_nickname, amount, text)
)
# 开红包图片
async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
send_user_nickname += '的红包'
amount = str(amount)
async def _generate_open_redbag_pic(
user_id: str, send_user_nickname: str, amount: int, text: str
):
send_user_nickname += "的红包"
amount_str = str(amount)
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_1"))
head = BuildImage(1000, 980, font_size=30, background=f'{IMAGE_PATH}/prts/redbag_1/{random_redbag}')
head = BuildImage(
1000,
980,
font_size=30,
background=f"{IMAGE_PATH}/prts/redbag_1/{random_redbag}",
)
size = BuildImage(0, 0, font_size=50).getsize(send_user_nickname)
# QQ头像
ava_bk = BuildImage(100 + size[0], 66, is_alpha=True, font_size=50)
ava = BuildImage(66, 66, is_alpha=True, background=BytesIO(await get_user_avatar(user_id)))
ava = BuildImage(
66, 66, is_alpha=True, background=BytesIO(await get_user_avatar(user_id))
)
ava_bk.paste(ava)
ava_bk.text((100, 7), send_user_nickname)
# ava_bk.show()
ava_bk_w, ava_bk_h = ava_bk.size
head.paste(ava_bk, (int((1000 - ava_bk_w) / 2), 300), alpha=True)
# 金额
size = BuildImage(0, 0, font_size=150).getsize(amount)
size = BuildImage(0, 0, font_size=150).getsize(amount_str)
price = BuildImage(size[0], size[1], is_alpha=True, font_size=150)
price.text((0, 0), amount, fill=(209, 171, 108))
price.text((0, 0), amount_str, fill=(209, 171, 108))
# 金币中文
head.paste(price, (int((1000 - size[0]) / 2) - 50, 460), alpha=True)
head.text((int((1000 - size[0]) / 2 + size[0]) - 50, 500 + size[1] - 70), '金币', fill=(209, 171, 108))
head.text(
(int((1000 - size[0]) / 2 + size[0]) - 50, 500 + size[1] - 70),
"金币",
fill=(209, 171, 108),
)
# 剩余数量和金额
head.text((350, 900), text, (198, 198, 198))
return head.pic2bs4()

View File

@ -9,9 +9,9 @@ class RedbagUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField()
group_id = fields.CharField(255)
"""群聊id"""
send_redbag_count = fields.IntField(default=0)
"""发送红包次数"""
@ -25,23 +25,23 @@ class RedbagUser(Model):
class Meta:
table = "redbag_users"
table_description = "红包统计数据表"
unique_together = ("user_qq", "group_id")
unique_together = ("user_id", "group_id")
@classmethod
async def add_redbag_data(
cls, user_qq: int, group_id: int, i_type: str, money: int
cls, user_id: str, group_id: str, i_type: str, money: int
):
"""
说明:
添加收发红包数据
参数:
:param user_qq: qq号
:param user_id: 用户id
:param group_id: 群号
:param i_type: 收或发
:param money: 金钱数量
"""
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
user, _ = await cls.get_or_create(user_id=user_id, group_id=group_id)
if i_type == "get":
user.get_redbag_count = user.get_redbag_count + 1
user.get_gold = user.get_gold + money
@ -56,3 +56,11 @@ class RedbagUser(Model):
"spend_gold",
]
)
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE redbag_users RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE redbag_users ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE redbag_users ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -27,12 +27,12 @@ my_level = on_command("我的权限", permission=GROUP, priority=5, block=True)
@get_my_group_info.handle()
async def _(event: GroupMessageEvent):
result = await get_member_info(event.user_id, event.group_id)
result = await get_member_info(str(event.user_id), str(event.group_id))
await get_my_group_info.finish(result)
async def get_member_info(user_qq: int, group_id: int) -> str:
if user := await GroupInfoUser.get_or_none(user_id=str(user_qq), group_id=str(group_id)):
async def get_member_info(user_id: str, group_id: str) -> str:
if user := await GroupInfoUser.get_or_none(user_id=user_id, group_id=group_id):
result = ""
result += "昵称:" + user.user_name + "\n"
result += "加群时间:" + str(user.user_join_time.date())

View File

@ -33,6 +33,7 @@ from .utils import (
CaseManager,
build_case_image,
get_skin_case,
init_skin_trends,
reset_count_daily,
update_skin_data,
)
@ -135,6 +136,31 @@ update_case = on_command(
show_case = on_command("查看武器箱", priority=5, block=True)
my_knifes = on_command("我的金色", priority=1, permission=GROUP, block=True)
show_skin = on_command("查看皮肤", priority=5, block=True)
price_trends = on_command("价格趋势", priority=5, block=True)
@price_trends.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().replace("武器箱", "").strip()
if not msg:
await price_trends.finish("未指定皮肤")
msg_split = msg.split()
if len(msg_split) < 3:
await price_trends.finish("参数不足, [类型名称] [皮肤名称] [磨损程度] ?[天数=7]")
abrasion = msg_split[2]
day = 7
if len(msg_split) > 3:
if not is_number(msg_split[3]):
await price_trends.finish("天数必须为数字")
day = int(msg_split[3])
if day <= 0 or day > 180:
await price_trends.finish("天数必须大于0且小于180")
result = await init_skin_trends(msg_split[0], msg_split[1], msg_split[2], day)
if not result:
await price_trends.finish("未查询到数据")
await price_trends.send(
image(await init_skin_trends(msg_split[0], msg_split[1], msg_split[2], day))
)
@reload_count.handle()

View File

@ -10,9 +10,9 @@ class OpenCasesLog(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField()
group_id = fields.CharField(255)
"""群聊id"""
case_name = fields.CharField(255)
"""箱子名称"""
@ -36,3 +36,11 @@ class OpenCasesLog(Model):
class Meta:
table = "open_cases_log"
table_description = "开箱日志表"
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE open_cases_log RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE open_cases_log ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE open_cases_log ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -7,9 +7,9 @@ class OpenCasesUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField()
group_id = fields.CharField(255)
"""群聊id"""
total_count: int = fields.IntField(default=0)
"""总开启次数"""
@ -47,11 +47,14 @@ class OpenCasesUser(Model):
class Meta:
table = "open_cases_users"
table_description = "开箱统计数据表"
unique_together = ("user_qq", "group_id")
unique_together = ("user_id", "group_id")
@classmethod
async def _run_script(cls):
return [
"alter table open_cases_users alter COLUMN make_money type float;", # 将make_money字段改为float
"alter table open_cases_users alter COLUMN spend_money type float;", # 将spend_money字段改为float
"ALTER TABLE open_cases_users RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE open_cases_users ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE open_cases_users ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -63,17 +63,19 @@ def add_count(user: OpenCasesUser, skin: BuffSkin, case_price: float):
user.spend_money += 17 + case_price
async def get_user_max_count(user_qq: int, group_id: int) -> int:
async def get_user_max_count(user_id: str, group_id: int) -> int:
"""获取用户每日最大开箱次数
Args:
user_qq (int): 用户id
user_id (str): 用户id
group_id (int): 群号
Returns:
int: 最大开箱次数
"""
user, _ = await SignGroupUser.get_or_create(user_id=str(user_qq), group_id=str(group_id))
user, _ = await SignGroupUser.get_or_create(
user_id=str(user_id), group_id=str(group_id)
)
impression = int(user.impression)
initial_open_case_count = Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT")
each_impression_add_count = Config.get_config(
@ -82,11 +84,11 @@ async def get_user_max_count(user_qq: int, group_id: int) -> int:
return int(initial_open_case_count + impression / each_impression_add_count) # type: ignore
async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, Message]:
async def open_case(user_id: str, group_id: int, case_name: str) -> Union[str, Message]:
"""开箱
Args:
user_qq (int): 用户id
user_id (str): 用户id
group_id (int): 群号
case_name (str, optional): 武器箱名称. Defaults to "狂牙大行动".
@ -99,14 +101,14 @@ async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, M
case_name = random.choice(CaseManager.CURRENT_CASES) # type: ignore
if case_name not in CaseManager.CURRENT_CASES:
return "武器箱未收录, 当前可用武器箱:\n" + ", ".join(CaseManager.CURRENT_CASES) # type: ignore
logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_qq, group_id)
logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_id, group_id)
case = cn2py(case_name)
user = await OpenCasesUser.get_or_none(user_qq=user_qq, group_id=group_id)
user = await OpenCasesUser.get_or_none(user_id=user_id, group_id=group_id)
if not user:
user = await OpenCasesUser.create(
user_qq=user_qq, group_id=group_id, open_cases_time_last=datetime.now()
user_id=user_id, group_id=group_id, open_cases_time_last=datetime.now()
)
max_count = await get_user_max_count(user_qq, group_id)
max_count = await get_user_max_count(user_id, group_id)
# 一天次数上限
if user.today_open_total >= max_count:
return _handle_is_MAX_COUNT()
@ -129,12 +131,12 @@ async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, M
logger.info(
f"开启{case_name}武器箱获得 {skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion}) 磨损: [{rand}] 价格: {skin.sell_min_price}",
"开箱",
user_qq,
user_id,
group_id,
)
await user.save()
await OpenCasesLog.create(
user_qq=user_qq,
user_id=user_id,
group_id=group_id,
case_name=case_name,
name=skin.name,
@ -146,7 +148,7 @@ async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, M
abrasion_value=rand,
create_time=datetime.now(),
)
logger.debug(f"添加 1 条开箱日志", "开箱", user_qq, group_id)
logger.debug(f"添加 1 条开箱日志", "开箱", user_id, group_id)
over_count = max_count - user.today_open_total
img = await draw_card(skin, rand)
return (
@ -157,12 +159,12 @@ async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, M
async def open_multiple_case(
user_qq: int, group_id: int, case_name: str, num: int = 10
user_id: str, group_id: int, case_name: str, num: int = 10
):
"""多连开箱
Args:
user_qq (int): 用户id
user_id (int): 用户id
group_id (int): 群号
case_name (str): 箱子名称
num (int, optional): 数量. Defaults to 10.
@ -177,11 +179,11 @@ async def open_multiple_case(
if case_name not in CaseManager.CURRENT_CASES:
return "武器箱未收录, 当前可用武器箱:\n" + ", ".join(CaseManager.CURRENT_CASES) # type: ignore
user, _ = await OpenCasesUser.get_or_create(
user_qq=user_qq,
user_id=user_id,
group_id=group_id,
defaults={"open_cases_time_last": datetime.now()},
)
max_count = await get_user_max_count(user_qq, group_id)
max_count = await get_user_max_count(user_id, group_id)
if user.today_open_total >= max_count:
return _handle_is_MAX_COUNT()
if max_count - user.today_open_total < num:
@ -189,7 +191,7 @@ async def open_multiple_case(
f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)"
f"\n剩余开箱次数:{max_count - user.today_open_total}"
)
logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_qq, group_id)
logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_id, group_id)
case = cn2py(case_name)
skin_count = {}
img_list = []
@ -219,12 +221,12 @@ async def open_multiple_case(
logger.info(
f"开启{case_name}武器箱获得 {skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion}) 磨损: [{rand:.11f}] 价格: {skin.sell_min_price}",
"开箱",
user_qq,
user_id,
group_id,
)
log_list.append(
OpenCasesLog(
user_qq=user_qq,
user_id=user_id,
group_id=group_id,
case_name=case_name,
name=skin.name,
@ -240,7 +242,7 @@ async def open_multiple_case(
await user.save()
if log_list:
await OpenCasesLog.bulk_create(log_list, 10)
logger.debug(f"添加 {len(log_list)} 条开箱日志", "开箱", user_qq, group_id)
logger.debug(f"添加 {len(log_list)} 条开箱日志", "开箱", user_id, group_id)
img_w += 10
img_h += 10
w = img_w * 5
@ -273,8 +275,8 @@ def _handle_is_MAX_COUNT() -> str:
return f"今天已达开箱上限了喔,明天再来吧\n(提升好感度可以增加每日开箱数 #疯狂暗示)"
async def total_open_statistics(user_qq: int, group: int) -> str:
user, _ = await OpenCasesUser.get_or_create(user_qq=user_qq, group_id=group)
async def total_open_statistics(user_id: str, group: str) -> str:
user, _ = await OpenCasesUser.get_or_create(user_id=user_id, group_id=group)
return (
f"开箱总数:{user.total_count}\n"
f"今日开箱:{user.today_open_total}\n"
@ -294,7 +296,7 @@ async def total_open_statistics(user_qq: int, group: int) -> str:
)
async def group_statistics(group: int):
async def group_statistics(group: str):
user_list = await OpenCasesUser.filter(group_id=group).all()
# lan zi fen hong jin pricei
uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0]
@ -330,19 +332,19 @@ async def group_statistics(group: int):
)
async def get_my_knifes(user_id: int, group_id: int) -> Union[str, MessageSegment]:
async def get_my_knifes(user_id: str, group_id: str) -> Union[str, MessageSegment]:
"""获取我的金色
Args:
user_id (int): 用户id
group_id (int): 群号
user_id (str): 用户id
group_id (str): 群号
Returns:
Union[str, MessageSegment]: 回复消息或图片
"""
data_list = await get_old_knife(user_id, group_id)
data_list += await OpenCasesLog.filter(
user_qq=user_id, group_id=group_id, color="KNIFE"
user_id=user_id, group_id=group_id, color="KNIFE"
).all()
if not data_list:
return "您木有开出金色级别的皮肤喔"
@ -377,17 +379,17 @@ async def get_my_knifes(user_id: int, group_id: int) -> Union[str, MessageSegmen
return image(A)
async def get_old_knife(user_id: int, group_id: int) -> List[OpenCasesLog]:
async def get_old_knife(user_id: str, group_id: str) -> List[OpenCasesLog]:
"""获取旧数据字段
Args:
user_id (int): 用户id
group_id (int): 群号
user_id (str): 用户id
group_id (str): 群号
Returns:
List[OpenCasesLog]: 旧数据兼容
"""
user, _ = await OpenCasesUser.get_or_create(user_qq=user_id, group_id=group_id)
user, _ = await OpenCasesUser.get_or_create(user_id=user_id, group_id=group_id)
knifes_name = user.knifes_name
data_list = []
if knifes_name:
@ -406,7 +408,7 @@ async def get_old_knife(user_id: int, group_id: int) -> List[OpenCasesLog]:
name = name.replace("StatTrak™", "")
data_list.append(
OpenCasesLog(
user_qq=user_id,
user_id=user_id,
group_id=group_id,
name=name.strip(),
case_name=case_name_py.strip(),

View File

@ -3,7 +3,7 @@ import os
import random
import re
import time
from datetime import datetime
from datetime import datetime, timedelta
from typing import List, Optional, Tuple, Union
import nonebot
@ -13,7 +13,7 @@ from configs.config import Config
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage
from utils.image_utils import BuildImage, BuildMat
from utils.utils import broadcast_group, cn2py
from .build_image import generate_skin
@ -547,6 +547,49 @@ async def get_skin_case(id_: str) -> Optional[List[str]]:
return None
async def init_skin_trends(
name: str, skin: str, abrasion: str, day: int = 7
) -> Optional[BuildMat]:
date = datetime.now() - timedelta(days=day)
log_list = (
await BuffSkinLog.filter(
name__contains=name.upper(),
skin_name=skin,
abrasion__contains=abrasion,
create_time__gt=date,
is_stattrak=False,
)
.order_by("create_time")
.limit(day * 5)
.all()
)
if not log_list:
return None
date_list = []
price_list = []
for log in log_list:
date = str(log.create_time.date())
if date not in date_list:
date_list.append(date)
price_list.append(log.sell_min_price)
bar_graph = BuildMat(
y=price_list,
mat_type="line",
title=f"{name}({skin})价格趋势({day})",
x_index=date_list,
x_min_spacing=90,
display_num=True,
x_rotate=30,
background=[
f"{IMAGE_PATH}/background/create_mat/{x}"
for x in os.listdir(f"{IMAGE_PATH}/background/create_mat")
],
bar_color=["*"],
)
await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph)
return bar_graph
async def reset_count_daily():
"""
重置每日开箱

View File

@ -6,14 +6,12 @@ from services.db_context import Model
class PixivKeywordUser(Model):
__tablename__ = "pixiv_keyword_users"
__table_args__ = {"extend_existing": True}
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField()
group_id = fields.CharField(255)
"""群聊id"""
keyword = fields.CharField(255, unique=True)
"""关键词"""
@ -46,9 +44,17 @@ class PixivKeywordUser(Model):
获取黑名单PID
"""
black_pid = []
keyword_list = await cls.filter(user_qq=114514).values_list(
keyword_list = await cls.filter(user_id="114514").values_list(
"keyword", flat=True
)
for image in keyword_list:
black_pid.append(image[6:])
return black_pid
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE pixiv_keyword_users RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE pixiv_keyword_users ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE pixiv_keyword_users ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -54,8 +54,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
# ):
if not await PixivKeywordUser.exists(keyword=msg):
await PixivKeywordUser.create(
user_qq=event.user_id,
group_id=group_id,
user_id=str(event.user_id),
group_id=str(group_id),
keyword=msg,
is_pass=str(event.user_id) in bot.config.superusers,
)
@ -104,8 +104,8 @@ async def _(
# ):
if not await PixivKeywordUser.exists(keyword=msg):
await PixivKeywordUser.create(
user_qq=event.user_id,
group_id=group_id,
user_id=str(event.user_id),
group_id=str(group_id),
keyword=msg,
is_pass=str(event.user_id) in bot.config.superusers,
)
@ -138,7 +138,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
keyword=f"black:{pid}{f'_p{img_p}' if img_p else ''}"
):
await PixivKeywordUser.create(
user_qq=114514,
user_id=114514,
group_id=114514,
keyword=f"black:{pid}{f'_p{img_p}' if img_p else ''}",
is_pass=str(event.user_id) in bot.config.superusers,

View File

@ -110,8 +110,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
keyword=f"black:{pid}{f'_p{img_p}' if img_p else ''}"
):
await PixivKeywordUser.create(
user_qq=114514,
group_id=114514,
user_id="114514",
group_id="114514",
keyword=f"black:{pid}{f'_p{img_p}' if img_p else ''}",
is_pass=False,
)
@ -168,7 +168,7 @@ async def _(
if data:
data.is_pass = flag
await data.save(update_fields=["is_pass"])
user_id, group_id = data.user_qq, data.group_id
user_id, group_id = data.user_id, data.group_id
if not user_id:
await pass_keyword.send(f"未找到关键词/UID{x},请检查关键词/UID是否存在...")
continue

View File

@ -37,7 +37,7 @@ show_pix = on_command("查看pix图库", priority=1, block=True)
@my_keyword.handle()
async def _(event: MessageEvent):
data = await PixivKeywordUser.filter(user_qq=event.user_id).values_list(
data = await PixivKeywordUser.filter(user_id=str(event.user_id)).values_list(
"keyword", flat=True
)
if not data:

View File

@ -14,6 +14,7 @@ from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.message_builder import at, image
from utils.utils import get_message_at, is_number
from .data_source import rank
from .model import RussianUser
@ -445,10 +446,10 @@ async def end_game(bot: Bot, event: GroupMessageEvent):
await BagUser.add_gold(win_user_id, event.group_id, money - fee)
await BagUser.spend_gold(lose_user_id, event.group_id, money)
win_user, _ = await RussianUser.get_or_create(
user_qq=win_user_id, group_id=event.group_id
user_id=str(win_user_id), group_id=str(event.group_id)
)
lose_user, _ = await RussianUser.get_or_create(
user_qq=lose_user_id, group_id=event.group_id
user_id=str(lose_user_id), group_id=str(event.group_id)
)
bullet_str = ""
for x in rs_player[event.group_id]["bullet"]:
@ -476,7 +477,7 @@ async def end_game(bot: Bot, event: GroupMessageEvent):
@record.handle()
async def _(event: GroupMessageEvent):
user, _ = await RussianUser.get_or_create(
user_qq=event.user_id, group_id=event.group_id
user_id=str(event.user_id), group_id=str(event.group_id)
)
await record.send(
f"俄罗斯轮盘\n"

View File

@ -8,7 +8,7 @@ from .model import RussianUser
async def rank(group_id: int, itype: str, num: int) -> Optional[BuildMat]:
all_users = await RussianUser.filter(group_id=group_id).all()
all_user_id = [user.user_qq for user in all_users]
all_user_id = [user.user_id for user in all_users]
if itype == "win_rank":
rank_name = "胜场排行榜"
all_user_data = [user.win_count for user in all_users]

View File

@ -1,4 +1,3 @@
from tortoise import fields
from services.db_context import Model
@ -8,9 +7,9 @@ class RussianUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField()
group_id = fields.CharField(255)
"""群聊id"""
win_count = fields.IntField(default=0)
"""胜利次数"""
@ -32,19 +31,19 @@ class RussianUser(Model):
class Meta:
table = "russian_users"
table_description = "俄罗斯轮盘数据表"
unique_together = ("user_qq", "group_id")
unique_together = ("user_id", "group_id")
@classmethod
async def add_count(cls, user_qq: int, group_id: int, itype: str):
async def add_count(cls, user_id: str, group_id: str, itype: str):
"""
说明:
添加用户输赢次数
说明:
:param user_qq: qq号
:param user_id: qq号
:param group_id: 群号
:param itype: 输或赢 'win' or 'lose'
"""
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
user, _ = await cls.get_or_create(user_id=user_id, group_id=group_id)
if itype == "win":
_max = (
user.max_winning_streak
@ -83,19 +82,27 @@ class RussianUser(Model):
)
@classmethod
async def money(cls, user_qq: int, group_id: int, itype: str, count: int) -> bool:
async def money(cls, user_id: str, group_id: str, itype: str, count: int) -> bool:
"""
说明:
添加用户输赢金钱
参数:
:param user_qq: qq号
:param user_id: qq号
:param group_id: 群号
:param itype: 输或赢 'win' or 'lose'
:param count: 金钱数量
"""
user, _ = await cls.get_or_create(user_qq=user_qq, group_id=group_id)
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=group_id)
if itype == "win":
user.make_money = user.make_money + count
elif itype == "lose":
user.lose_money = user.lose_money + count
await user.save(update_fields=["make_money", "lose_money"])
@classmethod
async def _run_script(cls):
return [
"ALTER TABLE russian_users RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE russian_users ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE russian_users ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -23,46 +23,48 @@ from .utils import SIGN_TODAY_CARD_PATH, get_card
async def group_user_check_in(
nickname: str, user_qq: int, group: int
nickname: str, user_id: int, group: int
) -> MessageSegment:
"Returns string describing the result of checking in"
present = datetime.now()
# 取得相应用户
user, is_create = await SignGroupUser.get_or_create(user_id=str(user_qq), group_id=str(group))
user, is_create = await SignGroupUser.get_or_create(
user_id=str(user_id), group_id=str(group)
)
# 如果同一天签到过,特殊处理
if not is_create and (
user.checkin_time_last.date() >= present.date()
or f"{user}_{group}_sign_{datetime.now().date()}"
in os.listdir(SIGN_TODAY_CARD_PATH)
):
gold = await BagUser.get_gold(user_qq, group)
gold = await BagUser.get_gold(user_id, group)
return await get_card(user, nickname, -1, gold, "")
return await _handle_check_in(nickname, user_qq, group, present) # ok
return await _handle_check_in(nickname, user_id, group, present) # ok
async def check_in_all(nickname: str, user_qq: int):
async def check_in_all(nickname: str, user_id: str):
"""
说明:
签到所有群
参数:
:param nickname: 昵称
:param user_qq: 用户qq
:param user_id: 用户id
"""
present = datetime.now()
for u in await SignGroupUser.filter(user_id=str(user_qq)).all():
for u in await SignGroupUser.filter(user_id=user_id).all():
group = u.group_id
if not (
u.checkin_time_last.date() >= present.date()
or f"{u}_{group}_sign_{datetime.now().date()}"
in os.listdir(SIGN_TODAY_CARD_PATH)
):
await _handle_check_in(nickname, user_qq, group, present)
await _handle_check_in(nickname, user_id, group, present)
async def _handle_check_in(
nickname: str, user_qq: int, group: int, present: datetime
nickname: str, user_id: str, group: str, present: datetime
) -> MessageSegment:
user, _ = await SignGroupUser.get_or_create(user_id=str(user_qq), group_id=str(group))
user, _ = await SignGroupUser.get_or_create(user_id=user_id, group_id=group)
impression_added = (secrets.randbelow(99) + 1) / 100
critx2 = random.random()
add_probability = float(user.add_probability)
@ -75,11 +77,11 @@ async def _handle_check_in(
gold = random.randint(1, 100)
gift, gift_type = random_event(float(user.impression))
if gift_type == "gold":
await BagUser.add_gold(user_qq, group, gold + gift)
await BagUser.add_gold(user_id, group, gold + gift)
gift = f"额外金币 + {gift}"
else:
await BagUser.add_gold(user_qq, group, gold)
await BagUser.add_property(user_qq, group, gift)
await BagUser.add_gold(user_id, group, gold)
await BagUser.add_property(user_id, group, gift)
gift += " + 1"
logger.info(
@ -93,10 +95,12 @@ async def _handle_check_in(
return await get_card(user, nickname, impression_added, gold, gift)
async def group_user_check(nickname: str, user_qq: int, group: int) -> MessageSegment:
async def group_user_check(nickname: str, user_id: str, group: str) -> MessageSegment:
# heuristic: if users find they have never checked in they are probable to check in
user, _ = await SignGroupUser.get_or_create(user_id=str(user_qq), group_id=str(group))
gold = await BagUser.get_gold(user_qq, group)
user, _ = await SignGroupUser.get_or_create(
user_id=str(user_id), group_id=str(group)
)
gold = await BagUser.get_gold(user_id, group)
return await get_card(user, nickname, None, gold, "", is_card_view=True)
@ -171,7 +175,9 @@ async def _pst(users: list, impressions: list, groups: list):
impressions.pop(index)
users.pop(index)
groups.pop(index)
if user_ := await GroupInfoUser.get_or_none(user_id=str(user), group_id=str(group)):
if user_ := await GroupInfoUser.get_or_none(
user_id=str(user), group_id=str(group)
):
user_name = user_.user_name
else:
user_name = f"我名字呢?"

View File

@ -9,9 +9,9 @@ class Statistics(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField(null=True)
group_id = fields.CharField(255, null=True)
"""群聊id"""
plugin_name = fields.CharField(255)
"""插件名称"""
@ -21,3 +21,11 @@ class Statistics(Model):
class Meta:
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 ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE statistics ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -102,7 +102,7 @@ async def _(
and matcher.plugin_name not in ["update_info", "statistics_handle"]
):
await Statistics.create(
user_qq=event.user_id,
user_id=str(event.user_id),
group_id=getattr(event, "group_id", None),
plugin_name=matcher.plugin_name,
create_time=datetime.now(),

View File

@ -1,15 +1,21 @@
import asyncio
import nonebot
from fastapi import APIRouter, FastAPI
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.log import default_filter, default_format
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.typing import T_State
from configs.config import Config as gConfig
from services.log import logger
from services.log import logger, logger_
from utils.manager import plugins2settings_manager
from .api.base_info import router as base_info_routes
from .api.group import router as group_routes
from .api.logs import router as ws_routes
from .api.logs.log_manager import LOG_STORAGE
from .api.plugins import router as plugin_routes
from .api.request import router as request_routes
from .api.system import router as system_routes
@ -31,10 +37,20 @@ BaseApiRouter.include_router(plugin_routes)
BaseApiRouter.include_router(group_routes)
BaseApiRouter.include_router(request_routes)
BaseApiRouter.include_router(system_routes)
BaseApiRouter.include_router(base_info_routes)
@driver.on_startup
def _():
loop = asyncio.get_running_loop()
def log_sink(message: str):
loop.create_task(LOG_STORAGE.add(message.rstrip("\n")))
logger_.add(log_sink, colorize=True, filter=default_filter, format=default_format)
app: FastAPI = nonebot.get_app()
app.include_router(BaseApiRouter)
app.include_router(ws_routes)
logger.info("<g>API启动成功</g>", "Web UI")

View File

@ -0,0 +1,75 @@
from datetime import datetime, timedelta
from typing import List, Optional
import nonebot
from fastapi import APIRouter
from configs.config import Config
from models.chat_history import ChatHistory
from services.log import logger
from utils.manager import plugin_data_manager, plugins2settings_manager, plugins_manager
from utils.manager.models import PluginData, PluginType
from ..models.model import BotInfo, Result
from ..models.params import UpdateConfig, UpdatePlugin
from ..utils import authentication
AVA_URL = "http://q1.qlogo.cn/g?b=qq&nk={}&s=160"
router = APIRouter()
@router.get("/get_bot_info", dependencies=[authentication()])
async def _(self_id: Optional[str] = None) -> Result:
"""
获取Bot基础信息
Args:
qq (Optional[str], optional): qq号. Defaults to None.
Returns:
Result: 获取指定bot信息与bot列表
"""
bot_list: List[BotInfo] = []
if bots := nonebot.get_bots():
select_bot: BotInfo
for key, bot in bots.items():
bot_list.append(
BotInfo(
bot=bot, # type: ignore
self_id=bot.self_id,
nickname="可爱的小真寻",
ava_url=AVA_URL.format(bot.self_id),
)
)
if _bl := [b for b in bot_list if b.self_id == self_id]:
select_bot = _bl[0]
else:
select_bot = bot_list[0]
select_bot.is_select = True
now = datetime.now()
select_bot.received_messages = await ChatHistory.filter(
bot_id=int(select_bot.self_id)
).count()
select_bot.received_messages_day = await ChatHistory.filter(
bot_id=int(select_bot.self_id),
create_time__gte=now - timedelta(hours=now.hour),
).count()
select_bot.received_messages_week = await ChatHistory.filter(
bot_id=int(select_bot.self_id),
create_time__gte=now - timedelta(days=7),
).count()
select_bot.group_count = len(await select_bot.bot.get_group_list())
select_bot.friend_count = len(await select_bot.bot.get_friend_list())
for bot in bot_list:
bot.bot = None # type: ignore
# 插件加载数量
select_bot.plugin_count = len(plugins2settings_manager)
pm_data = plugins_manager.get_data()
select_bot.fail_plugin_count = len([pd for pd in pm_data if pm_data[pd].error])
select_bot.success_plugin_count = (
select_bot.plugin_count - select_bot.fail_plugin_count
)
return Result.ok(bot_list, "已获取操作列表")
return Result.fail("无Bot连接")

View File

@ -0,0 +1 @@
from .logs import *

View File

@ -0,0 +1,45 @@
import asyncio
import re
from typing import Awaitable, Callable, ClassVar, Dict, Generic, List, Set, TypeVar
from urllib.parse import urlparse
PATTERN = r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))"
_T = TypeVar("_T")
LogListener = Callable[[_T], Awaitable[None]]
class LogStorage(Generic[_T]):
def __init__(self, rotation: float = 5 * 60):
self.count, self.rotation = 0, rotation
self.logs: Dict[int, str] = {}
self.listeners: Set[LogListener[str]] = set()
async def add(self, log: str):
log = re.sub(PATTERN, "", log)
log_split = log.split()
time = log_split[0] + " " + log_split[1]
level = log_split[2]
main = log_split[3]
type_ = None
log_ = " ".join(log_split[3:])
if "Calling API" in log_:
sp = log_.split("|")
type_ = sp[1]
log_ = "|".join(log_[1:])
data = {"time": time, "level": level, "main": main, "type": type_, "log": log_}
seq = self.count = self.count + 1
self.logs[seq] = log
asyncio.get_running_loop().call_later(self.rotation, self.remove, seq)
await asyncio.gather(
*map(lambda listener: listener(log), self.listeners),
return_exceptions=True,
)
return seq
def remove(self, seq: int):
del self.logs[seq]
return
LOG_STORAGE = LogStorage[str]()

View File

@ -0,0 +1,37 @@
from typing import List
from fastapi import APIRouter, WebSocket
from loguru import logger
from nonebot.utils import escape_tag, run_sync
from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
from .log_manager import LOG_STORAGE
router = APIRouter()
@router.get("/logs", response_model=List[str])
async def system_logs_history(reverse: bool = False):
return LOG_STORAGE.list(reverse=reverse)
@router.websocket("/logs")
async def system_logs_realtime(websocket: WebSocket):
await websocket.accept()
async def log_listener(log: str):
await websocket.send_text(log)
LOG_STORAGE.listeners.add(log_listener)
try:
while websocket.client_state == WebSocketState.CONNECTED:
recv = await websocket.receive()
logger.trace(
f"{system_logs_realtime.__name__!r} received "
f"<e>{escape_tag(repr(recv))}</e>"
)
except WebSocketDisconnect:
pass
finally:
LOG_STORAGE.listeners.remove(log_listener)
return

View File

@ -1,6 +1,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional, TypeVar, Union
from nonebot.adapters.onebot.v11 import Bot
from pydantic import BaseModel
from configs.utils import Config
@ -180,3 +181,43 @@ class SystemResult(BaseModel):
network: SystemNetwork
disk: SystemFolderSize
check_time: datetime
class BotInfo(BaseModel):
"""
Bot基础信息
"""
bot: Bot
"""Bot"""
self_id: str
"""SELF ID"""
nickname: str
"""昵称"""
ava_url: str
"""头像url"""
friend_count: int = 0
"""好友数量"""
group_count: int = 0
"""群聊数量"""
received_messages: int = 0
"""累计接收消息"""
received_messages_day: int = 0
"""今日累计接收消息"""
received_messages_week: int = 0
"""一周内累计接收消息"""
received_messages_month: int = 0
"""一月内累计接收消息"""
plugin_count: int = 0
"""加载插件数量"""
success_plugin_count: int = 0
"""加载成功插件数量"""
fail_plugin_count: int = 0
"""加载失败插件数量"""
is_select: bool = False
"""当前选择"""
class Config:
arbitrary_types_allowed = True

View File

@ -1,16 +1,17 @@
import random
import time
from pathlib import Path
from typing import Any, List, Optional, Tuple, Union
import nonebot
from nonebot.adapters.onebot.v11 import Message, MessageSegment
from services import logger
from utils.image_utils import text2image
from utils.message_builder import image
from ._model import WordBank
from typing import Optional, Tuple, Union, List, Any
from utils.utils import is_number
import nonebot
from ._model import WordBank
driver = nonebot.get_driver()
@ -197,7 +198,7 @@ async def _():
new_answer_path.mkdir(exist_ok=True, parents=True)
for word in word_list:
problem: str = word.problem
user_id = word.user_qq
user_id = word.user_id
group_id = word.group_id
format_ = word.format
answer = word.answer

View File

@ -31,9 +31,9 @@ class WordBank(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_qq = fields.BigIntField()
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.BigIntField(null=True)
group_id = fields.CharField(255, null=True)
"""群聊id"""
word_scope = fields.IntField(default=0)
"""生效范围 0: 全局 1: 群聊 2: 私聊"""
@ -63,8 +63,8 @@ class WordBank(Model):
@classmethod
async def exists(
cls,
user_id: Optional[int],
group_id: Optional[int],
user_id: Optional[str],
group_id: Optional[str],
problem: str,
answer: Optional[str],
word_scope: Optional[int] = None,
@ -83,7 +83,7 @@ class WordBank(Model):
"""
query = cls.filter(problem=problem)
if user_id:
query = query.filter(user_qq=user_id)
query = query.filter(user_id=user_id)
if group_id:
query = query.filter(group_id=group_id)
if answer:
@ -97,8 +97,8 @@ class WordBank(Model):
@classmethod
async def add_problem_answer(
cls,
user_id: int,
group_id: Optional[int],
user_id: str,
group_id: Optional[str],
word_scope: int,
word_type: int,
problem: Union[str, Message],
@ -133,7 +133,7 @@ class WordBank(Model):
user_id, group_id, str(problem), answer, word_scope, word_type
):
await cls.create(
user_qq=user_id,
user_id=user_id,
group_id=group_id,
word_scope=word_scope,
word_type=word_type,
@ -149,7 +149,7 @@ class WordBank(Model):
@classmethod
async def _answer2format(
cls, answer: Union[str, Message], user_id: int, group_id: Optional[int]
cls, answer: Union[str, Message], user_id: str, group_id: Optional[str]
) -> Tuple[str, List[Any]]:
"""
说明:
@ -213,7 +213,7 @@ class WordBank(Model):
if not query:
query = await cls.get_or_none(
problem=problem,
user_qq=user_id,
user_id=user_id,
group_id=group_id,
answer=answer,
)
@ -311,7 +311,7 @@ class WordBank(Model):
await cls._format2answer(
random_answer.problem,
random_answer.answer,
random_answer.user_qq,
random_answer.user_id,
random_answer.group_id,
random_answer,
)
@ -351,7 +351,7 @@ class WordBank(Model):
async def delete_group_problem(
cls,
problem: str,
group_id: int,
group_id: str,
index: Optional[int] = None,
word_scope: int = 1,
):
@ -386,7 +386,7 @@ class WordBank(Model):
cls,
problem: str,
replace_str: str,
group_id: int,
group_id: str,
index: Optional[int] = None,
word_scope: int = 1,
):
@ -419,7 +419,7 @@ class WordBank(Model):
@classmethod
async def get_group_all_problem(
cls, group_id: int
cls, group_id: str
) -> List[Tuple[Any, Union[MessageSegment, str]]]:
"""
说明:
@ -480,8 +480,8 @@ class WordBank(Model):
@classmethod
async def _move(
cls,
user_id: int,
group_id: Optional[int],
user_id: str,
group_id: Optional[str],
problem: Union[str, Message],
answer: Union[str, Message],
placeholder: str,
@ -503,7 +503,7 @@ class WordBank(Model):
user_id, group_id, problem, answer, word_scope, word_type
):
await cls.create(
user_qq=user_id,
user_id=user_id,
group_id=group_id,
word_scope=word_scope,
word_type=word_type,
@ -522,4 +522,7 @@ class WordBank(Model):
"ALTER TABLE word_bank2 ADD to_me varchar(255);", # 添加 to_me 字段
"ALTER TABLE word_bank2 ALTER COLUMN create_time TYPE timestamp with time zone USING create_time::timestamp with time zone;",
"ALTER TABLE word_bank2 ALTER COLUMN update_time TYPE timestamp with time zone USING update_time::timestamp with time zone;",
"ALTER TABLE word_bank2 RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE word_bank2 ALTER COLUMN user_id TYPE character varying(255);",
"ALTER TABLE word_bank2 ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -1,11 +1,13 @@
from services import logger
from ._rule import check
from ._model import WordBank
from configs.path_config import DATA_PATH
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from nonebot.typing import T_State
from configs.path_config import DATA_PATH
from services import logger
from ._model import WordBank
from ._rule import check
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
data_dir = DATA_PATH / "word_bank"
@ -20,7 +22,8 @@ async def _(event: MessageEvent, state: T_State):
if msg := await WordBank.get_answer(event, problem):
await message_handle.send(msg)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 触发词条 {problem}"
f" 触发词条 {problem}",
"词条检测",
event.user_id,
getattr(event, "group_id", None),
)

View File

@ -196,11 +196,11 @@ async def _(
if problem and bot.config.nickname:
nickname = [nk for nk in bot.config.nickname if problem.startswith(nk)]
await WordBank.add_problem_answer(
event.user_id,
event.group_id
str(event.user_id),
str(event.group_id)
if isinstance(event, GroupMessageEvent)
and (not word_scope or word_scope == "私聊")
else 0,
else "0",
scope2int[word_scope] if word_scope else 1,
type2int[word_type] if word_type else 0,
problem or problem_image,
@ -211,16 +211,16 @@ async def _(
if isinstance(e, FinishedException):
await add_word.finish()
logger.error(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加词条 {problem} 发生错误 {type(e)}: {e} "
f"添加词条 {problem} 错误...",
"添加词条",
event.user_id,
getattr(event, "group_id", None),
e=e,
)
await add_word.finish(f"添加词条 {problem} 发生错误!")
await add_word.send("添加词条 " + (problem or problem_image) + " 成功!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加词条 {problem} 成功!"
f"添加词条 {problem} 成功!", "添加词条", event.user_id, getattr(event, "group_id", None)
)
@ -230,7 +230,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
await delete_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
result = await delete_word(msg, event.group_id)
await delete_word_matcher.send(result)
logger.info(f"(USER {event.user_id}, GROUP " f"{event.group_id})" f" 删除词条:" + msg)
logger.info(f"删除词条:" + msg, "删除词条", event.user_id, event.group_id)
@delete_word_matcher.handle()
@ -246,7 +246,7 @@ async def _(
await delete_word_matcher.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
result = await delete_word(msg, word_scope=2 if cmd[0] == "删除词条" else 0)
await delete_word_matcher.send(result)
logger.info(f"(USER {event.user_id})" f" 删除词条:" + msg)
logger.info(f"删除词条:" + msg, "删除词条", event.user_id)
@update_word_matcher.handle()
@ -257,7 +257,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
await update_word_matcher.finish("此命令需要两个参数,请查看帮助")
result = await update_word(msg, event.group_id)
await update_word_matcher.send(result)
logger.info(f"(USER {event.user_id}, GROUP " f"{event.group_id})" f" 更新词条词条:" + msg)
logger.info(f"更新词条词条:" + msg, "更新词条", event.user_id, event.group_id)
@update_word_matcher.handle()
@ -275,7 +275,7 @@ async def _(
await update_word_matcher.finish("此命令需要两个参数,请查看帮助")
result = await update_word(msg, word_scope=2 if cmd[0] == "修改词条" else 0)
await update_word_matcher.send(result)
logger.info(f"(USER {event.user_id})" f" 更新词条词条:" + msg)
logger.info(f"更新词条词条:" + msg, "更新词条", event.user_id)
@show_word_matcher.handle()
@ -288,7 +288,8 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
if (
not is_number(id_)
or int(id_) < 0
or int(id_) >= len(await WordBank.get_group_all_problem(event.group_id))
or int(id_)
>= len(await WordBank.get_group_all_problem(str(event.group_id)))
):
await show_word_matcher.finish("id必须为数字且在范围内")
id_ = int(id_)
@ -311,9 +312,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
group_id=event.group_id, messages=custom_forward_msg(msg_list, bot.self_id)
)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看词条回答:" + problem
f"查看词条回答:" + problem, "显示词条", event.user_id, getattr(event, "group_id", None)
)
@ -352,4 +351,6 @@ async def _(event: PrivateMessageEvent, arg: Message = CommandArg()):
for msg in msg_list:
t += msg + "\n"
await show_word_matcher.send(t[:-1])
logger.info(f"(USER {event.user_id}, GROUP " f"private)" f" 发送查看词条回答:" + problem)
logger.info(
f"查看词条回答:" + problem, "显示词条", event.user_id, getattr(event, "group_id", None)
)

110
poetry.lock generated
View File

@ -194,7 +194,7 @@ reference = "ali"
[[package]]
name = "bilireq"
version = "0.2.3.post0"
version = "0.2.4"
description = ""
category = "main"
optional = false
@ -472,7 +472,7 @@ reference = "ali"
[[package]]
name = "fastapi"
version = "0.88.0"
version = "0.95.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
@ -480,13 +480,13 @@ python-versions = ">=3.7"
[package.dependencies]
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
starlette = "0.22.0"
starlette = ">=0.26.1,<0.27.0"
[package.extras]
all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.7.0)"]
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<7.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<=1.4.41)", "types-orjson (==3.6.2)", "types-ujson (==5.5.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"]
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"]
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
[package.source]
type = "legacy"
@ -1148,7 +1148,7 @@ reference = "ali"
[[package]]
name = "pydantic"
version = "1.10.2"
version = "1.10.7"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
@ -1156,7 +1156,7 @@ python-versions = ">=3.7"
[package.dependencies]
python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""}
typing-extensions = ">=4.1.0"
typing-extensions = ">=4.2.0"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
@ -1631,7 +1631,7 @@ reference = "ali"
[[package]]
name = "starlette"
version = "0.22.0"
version = "0.26.1"
description = "The little ASGI library that shines."
category = "main"
optional = false
@ -1766,7 +1766,7 @@ reference = "ali"
[[package]]
name = "uvicorn"
version = "0.20.0"
version = "0.22.0"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@ -1909,7 +1909,7 @@ reference = "ali"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "30f718b1d834abeaf0be2239ac66ebf40d71ca72b28ecea5b13d64aedeaaa6a9"
content-hash = "4cb77a7a5a9777d704c3862467937cd307d119bdd8825a0278fdd063b8695851"
[metadata.files]
aiofiles = [
@ -2037,8 +2037,8 @@ beautifulsoup4 = [
{file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"},
]
bilireq = [
{file = "bilireq-0.2.3.post0-py3-none-any.whl", hash = "sha256:8d1f98bb8fb59c0ce1dec226329353ce51e2efaad0a6b4d240437b6132648322"},
{file = "bilireq-0.2.3.post0.tar.gz", hash = "sha256:3185c3952a2becc7d31b0c01a12fda463fa477253504a68f81ea871594887ab4"},
{file = "bilireq-0.2.4-py3-none-any.whl", hash = "sha256:b5396164b27657cac8008c6a2d9bf6a8b5414d92d2b27cb5bee074f1fd0b5b0b"},
{file = "bilireq-0.2.4.tar.gz", hash = "sha256:0c52c7c1fbe1f1b9d8b53ab9a6d4fbae363f6d6dc35c6a7cf1cc8652a10e2e00"},
]
black = [
{file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
@ -2177,8 +2177,8 @@ exceptiongroup = [
{file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
]
fastapi = [
{file = "fastapi-0.88.0-py3-none-any.whl", hash = "sha256:263b718bb384422fe3d042ffc9a0c8dece5e034ab6586ff034f6b4b1667c3eee"},
{file = "fastapi-0.88.0.tar.gz", hash = "sha256:915bf304180a0e7c5605ec81097b7d4cd8826ff87a02bb198e336fb9f3b5ff02"},
{file = "fastapi-0.95.0-py3-none-any.whl", hash = "sha256:daf73bbe844180200be7966f68e8ec9fd8be57079dff1bacb366db32729e6eb5"},
{file = "fastapi-0.95.0.tar.gz", hash = "sha256:99d4fdb10e9dd9a24027ac1d0bd4b56702652056ca17a6c8721eec4ad2f14e18"},
]
feedparser = [
{file = "feedparser-6.0.10-py3-none-any.whl", hash = "sha256:79c257d526d13b944e965f6095700587f27388e50ea16fd245babe4dfae7024f"},
@ -2914,42 +2914,42 @@ pyasn1 = [
{file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
]
pydantic = [
{file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
{file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
{file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
{file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
{file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
{file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
{file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
{file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
{file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
{file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
{file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
{file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
{file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
{file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
{file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
{file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
{file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
{file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
{file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
{file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
{file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
{file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
{file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
{file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
{file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
{file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
{file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
{file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
{file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
{file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
{file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
{file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
{file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
{file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
{file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
{file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
{file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"},
{file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"},
{file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"},
{file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"},
{file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"},
{file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"},
{file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"},
{file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"},
{file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"},
{file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"},
{file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"},
{file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"},
{file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"},
{file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"},
{file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"},
{file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"},
{file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"},
{file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"},
{file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"},
{file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"},
{file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"},
{file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"},
{file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"},
{file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"},
{file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"},
{file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"},
{file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"},
{file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"},
{file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"},
{file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"},
{file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"},
{file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"},
{file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"},
{file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"},
{file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"},
{file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"},
]
pyee = [
{file = "pyee-9.0.4-py2.py3-none-any.whl", hash = "sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5"},
@ -3253,8 +3253,8 @@ soupsieve = [
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
]
starlette = [
{file = "starlette-0.22.0-py3-none-any.whl", hash = "sha256:b5eda991ad5f0ee5d8ce4c4540202a573bb6691ecd0c712262d0bc85cf8f2c50"},
{file = "starlette-0.22.0.tar.gz", hash = "sha256:b092cbc365bea34dd6840b42861bdabb2f507f8671e642e8272d2442e08ea4ff"},
{file = "starlette-0.26.1-py3-none-any.whl", hash = "sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e"},
{file = "starlette-0.26.1.tar.gz", hash = "sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
@ -3348,8 +3348,8 @@ ujson = [
{file = "ujson-5.6.0.tar.gz", hash = "sha256:f881e2d8a022e9285aa2eab6ba8674358dbcb2b57fa68618d88d62937ac3ff04"},
]
uvicorn = [
{file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"},
{file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"},
{file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"},
{file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"},
]
uvloop = [
{file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"},

View File

@ -46,6 +46,7 @@ nonebot-plugin-htmlrender = "^0.2.0"
cachetools = "^5.2.0"
tortoise-orm = {extras = ["asyncpg"], version = "^0.19.3"}
cattrs = "^22.2.0"
starlette = "^0.26.1"
[tool.poetry.dev-dependencies]

View File

@ -958,6 +958,7 @@ class BuildMat:
y_name: Optional[str] = None,
x_index: List[Union[str, int, float]] = None,
y_index: List[Union[str, int, float]] = None,
x_min_spacing: Optional[int] = None,
x_rotate: int = 0,
title: Optional[str] = None,
size: Tuple[int, int] = (1000, 1000),
@ -979,6 +980,7 @@ class BuildMat:
:param y_name: 纵坐标名称
:param x_index: 横坐标值
:param y_index: 纵坐标值
:param x_min_spacing: x轴最小间距
:param x_rotate: 横坐标旋转角度
:param title: 标题
:param size: 图像大小建议默认
@ -1000,6 +1002,7 @@ class BuildMat:
self.y_name = y_name
self.x_index = x_index
self.y_index = y_index
self.x_min_spacing = x_min_spacing
self.x_rotate = x_rotate
self.title = title
self.font = font
@ -1030,7 +1033,10 @@ class BuildMat:
]
if not x_index:
raise ValueError("缺少 x_index [横坐标值]...")
self._x_interval = int((self.line_length - 70) / len(x_index))
if x_min_spacing:
self._x_interval = x_min_spacing
else:
self._x_interval = int((self.line_length - 70) / len(x_index))
self._bar_width = int(30 * (1 - (len(x_index) + 10) / 100))
# 没有 y_index 时自动生成
if not y_index:
@ -1181,7 +1187,7 @@ class BuildMat:
:param y: 坐标点
:param display_num: 显示该点的值
"""
_black_point = BuildImage(7, 7, color=random.choice(self.bar_color))
_black_point = BuildImage(11, 11, color=random.choice(self.bar_color))
_black_point.circle()
x_interval = self._x_interval
current_w = self.padding_w + x_interval
@ -1196,14 +1202,6 @@ class BuildMat:
),
f"{y[i]:.2f}" if isinstance(y[i], float) else f"{y[i]}",
)
self.markImg.paste(
_black_point,
(
current_w - 3,
current_h - int(y[i] * self._p * self._deviation) - 3,
),
True,
)
if i != len(y) - 1:
self.markImg.line(
(
@ -1215,6 +1213,14 @@ class BuildMat:
fill=(0, 0, 0),
width=2,
)
self.markImg.paste(
_black_point,
(
current_w - 3,
current_h - int(y[i] * self._p * self._deviation) - 3,
),
True,
)
current_w += x_interval
def _gen_bar_graph(
@ -1319,6 +1325,11 @@ class BuildMat:
padding_h = self.padding_h
line_length = self.line_length
background = random.choice(self.background) if self.background else None
if self.x_min_spacing:
length = (len(self.x_index) + 1) * self.x_min_spacing
if 2 * padding_w + length > self.w:
self.w = 2 * padding_w + length
background = None
A = BuildImage(
self.w, self.h, font_size=font_size, font=self.font, background=background
)
@ -1341,7 +1352,7 @@ class BuildMat:
(
padding_w,
padding_h + line_length,
padding_w + line_length,
self.w - padding_w,
padding_h + line_length,
),
(0, 0, 0),

View File

@ -35,11 +35,11 @@ class StaticData(Generic[T]):
elif file.name.endswith("yaml"):
self._data = _yaml.load(f)
def set(self, key, value) -> NoReturn:
def set(self, key, value):
self._data[key] = value
self.save()
def set_module_data(self, module, key, value, auto_save: bool = True) -> NoReturn:
def set_module_data(self, module, key, value, auto_save: bool = True):
if module in self._data.keys():
self._data[module][key] = value
if auto_save:
@ -51,7 +51,7 @@ class StaticData(Generic[T]):
def keys(self) -> List[str]:
return self._data.keys()
def delete(self, key) -> NoReturn:
def delete(self, key):
if self._data.get(key) is not None:
del self._data[key]
@ -105,3 +105,6 @@ class StaticData(Generic[T]):
def __getitem__(self, key) -> T:
return self._data[key]
def __len__(self) -> int:
return len(self._data)

View File

@ -35,10 +35,17 @@ class BaseData(BaseModel):
群基本信息
"""
white_group: List[int] = [] # 白名单
close_task: List[str] = [] # 全局关闭的被动任务
group_manager: Dict[str, BaseGroup] = {} # 群组管理
task: Dict[str, str] = {} # 被动任务 【英文:中文】
white_group: List[int] = []
"""白名单"""
close_task: List[str] = []
"""全局关闭的被动任务"""
group_manager: Dict[str, BaseGroup] = {}
"""群组管理"""
task: Dict[str, str] = {}
"""被动任务 英文:中文名"""
def __len__(self) -> int:
return len(self.group_manager)
class PluginBlock(BaseModel):

View File

@ -7,11 +7,11 @@ from nonebot.adapters.onebot.v11.message import Message, MessageSegment
from configs.config import NICKNAME
from configs.path_config import IMAGE_PATH, RECORD_PATH
from services.log import logger
from utils.image_utils import BuildImage
from utils.image_utils import BuildImage, BuildMat
def image(
file: Optional[Union[str, Path, bytes, BuildImage, io.BytesIO]] = None,
file: Optional[Union[str, Path, bytes, BuildImage, io.BytesIO, BuildMat]] = None,
b64: Optional[str] = None,
) -> MessageSegment:
"""
@ -38,7 +38,7 @@ def image(
logger.warning(f"图片 {file.absolute()}缺失...")
if isinstance(file, (bytes, io.BytesIO)):
return MessageSegment.image(file)
if isinstance(file, BuildImage):
if isinstance(file, (BuildImage, BuildMat)):
return MessageSegment.image(file.pic2bs4())
return MessageSegment.image("")

3
utils/typing.py Normal file
View File

@ -0,0 +1,3 @@
from typing import Literal
BLOCK_TYPE = Literal["all", "private", "group"]