Merge pull request #1547 from HibiKier/dev

Dev
This commit is contained in:
HibiKier 2024-08-11 18:42:15 +08:00 committed by GitHub
commit 10907dfa33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
510 changed files with 34029 additions and 48288 deletions

View File

@ -10,7 +10,52 @@ NICKNAME=["真寻", "小真寻", "绪山真寻", "小寻子"]
SESSION_EXPIRE_TIMEOUT=30
DEBUG=False
# 全局图片统一使用bytes发送当真寻与协议端不在同一服务器上时为True
IMAGE_TO_BYTES = False
PLATFORM_SUPERUSERS = '
{
"qq": [""],
"dodo": [""]
}
'
DRIVER=~fastapi+~httpx+~websockets
# kook adapter toekn
# kaiheila_bots =[{"token": ""}]
# # discode adapter
# DISCORD_BOTS='
# [
# {
# "token": "",
# "intent": {
# "guild_messages": true,
# "direct_messages": true
# },
# "application_commands": {"*": ["*"]}
# }
# ]
# '
# DISCORD_PROXY=''
# # dodo adapter
# DODO_BOTS='
# [
# {
# "client_id": "",
# "token": ""
# }
# ]
# '
# application_commands的{"*": ["*"]}代表将全部应用命令注册为全局应用命令
# {"admin": ["123", "456"]}则代表将admin命令注册为id是123、456服务器的局部命令其余命令不注册
# LOG_LEVEL=DEBUG
# 服务器和端口
HOST = 127.0.0.1
PORT = 8080

3
.gitignore vendored
View File

@ -180,4 +180,5 @@ plugins/csgo_server/
plugins/activity/
!/resources/image/genshin/alc/back.png
!/data/genshin_alc/
.vscode/launch.json
.vscode/launch.json
/resources/template/my_info

29
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"C_Cpp.errorSquiggles": "enabled",
"terminal.integrated.env.linux": {
"PYTHONPATH": "${workspaceFolder}${pathSeparator}${env:PYTHONPATH}"
},
"cSpell.words": [
"aiofiles",
"Alconna",
"arclet",
"Arparma",
"displayname",
"flmt",
"getbbox",
"hibiapi",
"httpx",
"kaiheila",
"lolicon",
"nonebot",
"onebot",
"pixiv",
"Setu",
"tobytes",
"ujson",
"unban",
"userinfo",
"zhenxun"
],
"python.analysis.autoImportCompletions": true
}

113
README.md
View File

@ -1,39 +1,54 @@
<div align=center><img width="320" height="320" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/logo.png"/></div>
<div align=center><img width="320" height="320" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/tt.png"/></div>
![maven](https://img.shields.io/badge/python-3.8%2B-blue)
![maven](https://img.shields.io/badge/nonebot-2.0.0-yellow)
![maven](https://img.shields.io/badge/go--cqhttp-1.0.0-red)
<div align=center>
![maven](https://img.shields.io/badge/python-3.9%2B-blue)
![maven](https://img.shields.io/badge/nonebot-2.1.3-yellow)
</div>
<div align=center>
# 绪山真寻Bot
</div>
****
<div align=center>
“真寻是<strong>[椛椛](https://github.com/FloatTech/ZeroBot-Plugin)</strong>的好朋友!”
</div>
****
此项目基于 Nonebot2 和 go-cqhttp 开发,以 postgresql 作为数据库的QQ群娱乐机器人
## 关于
用爱发电,某些功能学习借鉴了大佬们的代码,因为绪山真寻实在太可爱了因此开发了
绪山真寻bot实现了一些对群友的娱乐功能和实用功能大概
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
如果该项目的图片等等侵犯猫豆腐老师权益请联系我删除!
讨论插件开发nonebot2开发或者有 <strong>安装使用问题</strong><strong>开发建议</strong>可以发送issues或加入[ <strong>[真寻酱的技术群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>] (在这里请不要吹水!)
希望有个地方讨论绪山真寻Bot渴望吹水聊天可以加入[ <strong>[是真寻酱哒](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) </strong>]
## 声明
此项目仅用于学习交流,请勿用于非法用途
<div align=center>
# Nonebot2
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/32db41bc55fa37e0d0085e4fd70e4e74fd34307f6bb4ebdad235bd1b0c8f4126/68747470733a2f2f6e6f6e65626f742e6465762f6c6f676f2e706e67">
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
</div>
## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/)
@ -50,15 +65,42 @@
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 这是一份扩展
## 一键安装脚本
### 0. 体验一下?
提供dev版本的zhenxun
```
Url 43.143.112.57:11451/onebot/v11/ws
AccessToken: PUBLIC_ZHENXUN_TEST
注:你无法获得超级用户权限
```
### 1. Web UI
项目地址: [Web UI](https://github.com/HibiKier/zhenxun_bot_webui)
<details>
<summary>后台示例图 </summary>
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui1.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui2.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui3.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui4.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui5.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui6.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui7.png)
</details>
### 一键安装脚本(新版未测试)
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库
### 提供符合真寻标准的插件仓库(旧版)
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
@ -67,7 +109,7 @@
* 实现了许多功能,且提供了大量功能管理命令
* 通过Config配置项将所有插件配置统计保存至config.yaml利于统一用户修改
* 方便增删插件原生nonebot2 matcher不需要额外修改仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd阻塞每日次数等限制仅仅通过简单的属性就可以生成一个限制例如`__plugin_cd_limit__`
* 提供了cd阻塞每日次数等限制仅仅通过简单的属性就可以生成一个限制例如`PluginCdBlock` 等
* **..... 更多详细请通过`传送门`查看文档!**
## 功能列表
@ -226,13 +268,10 @@
```
# 配置gocq
在 https://github.com/Mrs4s/go-cqhttp 下载Releases最新版本运行后选择反向代理
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/onebot/v11/ws
# 使用napcat或拉格朗日
# 获取代码
git clone https://github.com/HibiKier/zhenxun_bot.git
git clone https://github.com/HibiKier/zhenxun.git
# 进入目录
cd zhenxun_bot
@ -247,6 +286,9 @@ poetry install # 安装依赖
# 开始运行
poetry shell # 进入虚拟环境
python bot.py
# 运行后会在data目录下生成database.json文件请根据自身数据库配置修改
# 其他插件配置在data/config.yaml文件中需要运行一次
```
## 简单配置
@ -256,15 +298,32 @@ python bot.py
SUPERUSERS = [""] # 填写你的QQ
2.在configs/config.py文件中
* 数据库配置
PLATFORM_SUPERUSERS = '
{
"qq": [""], # 在此处填写你的qq
"dodo": [],
"kaiheila": [],
"discord": []
}
'
2.在data/database.json文件中修改数据库配置
{
"bind": "",
"sql_name": "postgres",
"user": "", # 用户们
"password": "", # 密码
"address": "", # 数据库地址ip
"port": "", # 数据库端口
"database": "" # 数据库名称
}
3.在configs/config.yaml文件中 # 该文件需要启动一次后生成
* 修改插件配置项
```
## 使用Docker
## 使用Docker (新版未测试过)
**Docker 单机版仅真寻Bot**
**点击下方的 GitHub 徽标查看教程**
@ -335,11 +394,15 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
## 更新
### 2024/1/25
### 2024/8/11
* 重构webui
* 更新dev
### 2023/12/28
<!-- ### 2024/1/25
* 重构webui -->
<!-- ### 2023/12/28
* 修复B站动态获取失败的时候会发送空消息
@ -767,7 +830,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
### 2022/8/6
* 修复了原神自动签到返回invalid request的问题新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971)
* 修复了原神自动签到返回invalid request的问题新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971) -->
<br>

View File

@ -1,35 +0,0 @@
from pathlib import Path
import nonebot
from configs.config import Config
Config.add_plugin_config(
"admin_bot_manage:custom_welcome_message",
"SET_GROUP_WELCOME_MESSAGE_LEVEL [LEVEL]",
2,
name="群管理员操作",
help_="设置群欢迎消息权限",
default_value=2,
type=int,
)
Config.add_plugin_config(
"admin_bot_manage:switch_rule",
"CHANGE_GROUP_SWITCH_LEVEL [LEVEL]",
2,
help_="开关群功能权限",
default_value=2,
type=int,
)
Config.add_plugin_config(
"admin_bot_manage",
"ADMIN_DEFAULT_AUTH",
5,
help_="默认群管理员权限",
default_value=5,
type=int,
)
nonebot.load_plugins(str(Path(__file__).parent.resolve()))

View File

@ -1,391 +0,0 @@
import asyncio
import os
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import List, Union
import ujson as json
from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment
from configs.config import Config
from configs.path_config import DATA_PATH, IMAGE_PATH
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
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_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: str) -> str:
"""
说明:
获取当前群聊所有通知的开关
参数:
:param group_id: 群号
"""
_data = group_manager.get_task_data()
image_list = []
for i, task in enumerate(_data):
name = _data[task]
name_image = BuildImage(0, 0, plain_text=f"{i+1}.{name}", font_size=20)
bk = BuildImage(
name_image.w + 200, name_image.h + 20, color=(103, 177, 109), font_size=15
)
await bk.apaste(name_image, (10, 0), True, "by_height")
a_icon = BuildImage(40, 40, background=ICON_PATH / "btn_false.png")
if group_manager.check_group_task_status(group_id, task):
a_icon = BuildImage(40, 40, background=ICON_PATH / "btn_true.png")
b_icon = BuildImage(40, 40, background=ICON_PATH / "btn_false.png")
if group_manager.check_task_super_status(task):
b_icon = BuildImage(40, 40, background=ICON_PATH / "btn_true.png")
await bk.atext((name_image.w + 20, 10), "状态")
await bk.apaste(a_icon, (name_image.w + 50, 0), True)
await bk.atext((name_image.w + 100, 10), "全局")
await bk.apaste(b_icon, (name_image.w + 130, 0), True)
image_list.append(bk)
w = max([x.w for x in image_list])
h = sum([x.h + 10 for x in image_list])
A = BuildImage(w + 20, h + 70, font_size=30, color=(119, 97, 177))
await A.atext((15, 20), "群被动状态")
curr_h = 75
for img in image_list:
# await img.acircle_corner()
await A.apaste(img, (0, curr_h), True)
curr_h += img.h + 10
return A.pic2bs4()
async def custom_group_welcome(
msg: str, img_list: List[str], user_id: str, group_id: str
) -> Union[str, Message]:
"""
说明:
替换群欢迎消息
参数:
:param msg: 欢迎消息文本
:param img_list: 欢迎消息图片
:param user_id: 用户id用于log记录
:param group_id: 群号
"""
img_result = ""
result = ""
img = img_list[0] if img_list else ""
msg_image = DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg"
if msg_image.exists():
msg_image.unlink()
data = {}
if CUSTOM_WELCOME_FILE.exists():
data = json.load(CUSTOM_WELCOME_FILE.open("r", encoding="utf8"))
try:
if msg:
data[group_id] = msg
json.dump(
data,
CUSTOM_WELCOME_FILE.open("w", encoding="utf8"),
indent=4,
ensure_ascii=False,
)
logger.info(f"更换群欢迎消息 {msg}", "更换群欢迎信息", user_id, group_id)
result += msg
if img:
await AsyncHttpx.download_file(img, msg_image)
img_result = image(msg_image)
logger.info(f"更换群欢迎消息图片", "更换群欢迎信息", user_id, group_id)
except Exception as e:
logger.error(f"替换群消息失败", "更换群欢迎信息", user_id, group_id, e=e)
return "替换群消息失败..."
return f"替换群欢迎消息成功:\n{result}" + img_result
task_data = None
def change_global_task_status(cmd: str) -> str:
"""
说明:
修改全局被动任务状态
参数:
:param cmd: 功能名称
"""
global task_data
if not task_data:
task_data = group_manager.get_task_data()
status = cmd[:2]
_cmd = cmd[4:]
if "全部被动" in cmd:
for task in task_data:
if status == "开启":
group_manager.open_global_task(task)
else:
group_manager.close_global_task(task)
group_manager.save()
return f"{status} 全局全部被动技能!"
else:
modules = [x for x in task_data if task_data[x].lower() == _cmd.lower()]
if not modules:
return "未查询到该被动任务"
if status == "开启":
group_manager.open_global_task(modules[0])
else:
group_manager.close_global_task(modules[0])
group_manager.save()
return f"{status} 全局{_cmd}"
async def change_group_switch(cmd: str, group_id: str, is_super: bool = False) -> str:
"""
说明:
修改群功能状态
参数:
:param cmd: 功能名称
:param group_id: 群号
:param is_super: 是否为超级用户超级用户用于私聊开关功能状态
"""
global task_data
if not task_data:
task_data = group_manager.get_task_data()
help_path = GROUP_HELP_PATH / f"{group_id}.png"
status = cmd[:2]
cmd = cmd[2:]
type_ = "plugin"
modules = plugins2settings_manager.get_plugin_module(cmd, True)
if cmd == "全部被动":
for task in task_data:
if status == "开启":
if not group_manager.check_group_task_status(group_id, task):
group_manager.open_group_task(group_id, task)
else:
if group_manager.check_group_task_status(group_id, task):
group_manager.close_group_task(group_id, task)
if help_path.exists():
help_path.unlink()
return f"{status} 全部被动技能!"
if cmd == "全部功能":
for f in plugins2settings_manager.get_data():
if status == "开启":
group_manager.unblock_plugin(f, group_id, False)
else:
group_manager.block_plugin(f, group_id, False)
group_manager.save()
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"
modules = [x for x in task_data.keys() if task_data[x].lower() == cmd.lower()]
for module in modules:
if is_super:
module = f"{module}:super"
if status == "开启":
if type_ == "task":
if group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
group_manager.open_group_task(group_id, module)
else:
if group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于开启状态!不要重复开启."
group_manager.unblock_plugin(module, group_id)
else:
if type_ == "task":
if not group_manager.check_group_task_status(group_id, module):
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
group_manager.close_group_task(group_id, module)
else:
if not group_manager.get_plugin_status(module, group_id):
return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
group_manager.block_plugin(module, group_id)
if help_path.exists():
help_path.unlink()
if is_super:
for file in os.listdir(GROUP_HELP_PATH):
file = GROUP_HELP_PATH / file
file.unlink()
else:
if help_path.exists():
help_path.unlink()
return f"{status} {cmd} 功能!"
def set_plugin_status(cmd: str, block_type: BLOCK_TYPE = "all"):
"""
说明:
设置插件功能状态超级用户使用
参数:
:param cmd: 功能名称
: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)
if status == "开启":
plugins_manager.unblock_plugin(module)
else:
plugins_manager.block_plugin(module, block_type=block_type)
for file in os.listdir(GROUP_HELP_PATH):
file = GROUP_HELP_PATH / file
file.unlink()
async def get_plugin_status():
"""
说明:
获取功能状态
"""
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
def _get_plugin_status() -> MessageSegment:
"""
说明:
合成功能状态图片
"""
rst = "\t功能\n"
flag_str = "状态".rjust(4) + "\n"
for matcher in get_matchers(True):
if module := matcher.plugin_name:
flag = plugins_manager.get_plugin_block_type(module)
flag = flag.upper() + " CLOSE" if flag else "OPEN"
try:
plugin_name = plugins_manager.get(module).plugin_name
if (
"[Hidden]" in plugin_name
or "[Admin]" in plugin_name
or "[Superuser]" in plugin_name
):
continue
rst += f"{plugin_name}"
except KeyError:
rst += f"{module}"
if plugins_manager.get(module).error:
rst += "[ERROR]"
rst += "\n"
flag_str += f"{flag}\n"
height = len(rst.split("\n")) * 24
a = BuildImage(250, height, font_size=20)
a.text((10, 10), rst)
b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str)
A = BuildImage(500, height)
A.paste(a)
A.paste(b, (270, 0))
return image(b64=A.pic2bs4())
async def update_member_info(
bot: Bot, group_id: int, remind_superuser: bool = False
) -> bool:
"""
说明:
更新群成员信息
参数:
:param group_id: 群号
:param remind_superuser: 失败信息提醒超级用户
"""
_group_user_list = await bot.get_group_member_list(group_id=group_id)
_error_member_list = []
_exist_member_list = []
# try:
admin_default_auth = Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH")
if admin_default_auth is not None:
for user_info in _group_user_list:
nickname = user_info["card"] or user_info["nickname"]
# 更新权限
if user_info["role"] in [
"owner",
"admin",
] 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"],
admin_default_auth,
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
user = await GroupInfoUser.get_or_none(
user_id=str(user_info["user_id"]), group_id=str(user_info["group_id"])
)
if user:
if user.user_name != nickname:
user.user_name = nickname
await user.save(update_fields=["user_name"])
logger.debug(
f"更新群昵称成功",
"更新群组成员信息",
user_info["user_id"],
user_info["group_id"],
)
_exist_member_list.append(str(user_info["user_id"]))
continue
join_time = datetime.strptime(
time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
),
"%Y-%m-%d %H:%M:%S",
)
await GroupInfoUser.update_or_create(
user_id=str(user_info["user_id"]),
group_id=str(user_info["group_id"]),
defaults={
"user_name": nickname,
"user_join_time": join_time.replace(
tzinfo=timezone(timedelta(hours=8))
),
},
)
_exist_member_list.append(str(user_info["user_id"]))
logger.debug("更新成功", "更新成员信息", user_info["user_id"], user_info["group_id"])
_del_member_list = list(
set(_exist_member_list).difference(
set(await GroupInfoUser.get_group_member_id_list(group_id))
)
)
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()
logger.info(f"删除已退群用户", "更新群组成员信息", del_user, group_id)
if _error_member_list and remind_superuser:
result = ""
for error_user in _error_member_list:
result += error_user
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
)
return True
def set_group_bot_status(group_id: str, status: bool) -> str:
"""
说明:
设置群聊bot开关状态
参数:
:param group_id: 群号
:param status: 状态
"""
if status:
if group_manager.check_group_bot_status(group_id):
return "我还醒着呢!"
group_manager.turn_on_group_bot_status(group_id)
return "呜..醒来了..."
else:
group_manager.shutdown_group_bot_status(group_id)
return "那我先睡觉了..."

View File

@ -1,45 +0,0 @@
from nonebot import on_notice
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent
from configs.config import Config
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
__zx_plugin_name__ = "群管理员变动监测 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
admin_notice = on_notice(priority=5)
@admin_notice.handle()
async def _(event: GroupAdminNoticeEvent):
if user := await GroupInfoUser.get_or_none(
user_id=str(event.user_id), group_id=str(event.group_id)
):
nickname = user.user_name
else:
nickname = event.user_id
if event.sub_type == "set":
admin_default_auth = Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH")
if admin_default_auth is not None:
await LevelUser.set_level(
event.user_id,
event.group_id,
admin_default_auth,
)
logger.info(
f"成为管理员,添加权限: {admin_default_auth}",
"群管理员变动监测",
event.user_id,
event.group_id,
)
else:
logger.warning(
f"配置项 MODULE: [<u><y>admin_bot_manage</y></u>] | KEY: [<u><y>ADMIN_DEFAULT_AUTH</y></u>] 为空"
)
elif event.sub_type == "unset":
await LevelUser.delete_level(event.user_id, event.group_id)
logger.info("撤销管理员,,取消权限等级", "群管理员变动监测", event.user_id, event.group_id)

View File

@ -1,64 +0,0 @@
from typing import List
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg
from configs.config import Config
from services.log import logger
from utils.depends import ImageList, OneCommand
from ._data_source import custom_group_welcome
__zx_plugin_name__ = "自定义进群欢迎消息 [Admin]"
__plugin_usage__ = """
usage
指令
自定义进群欢迎消息 ?[文本] ?[图片]
Note可以通过[at]来确认是否艾特新成员
示例自定义进群欢迎消息 欢迎新人[图片]
示例自定义进群欢迎消息 欢迎你[at]
""".strip()
__plugin_des__ = "简易的自定义群欢迎消息"
__plugin_cmd__ = ["自定义群欢迎消息 ?[文本] ?[图片]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config(
"admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"
),
}
custom_welcome = on_command(
"自定义进群欢迎消息",
aliases={"自定义欢迎消息", "自定义群欢迎消息", "设置群欢迎消息"},
permission=GROUP,
priority=5,
block=True,
)
@custom_welcome.handle()
async def _(
event: GroupMessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
img: List[str] = ImageList(),
):
msg = arg.extract_plain_text().strip()
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
try:
await custom_welcome.send(
await custom_group_welcome(
msg, img, str(event.user_id), str(event.group_id)
),
at_sender=True,
)
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
)
await custom_welcome.send("发生了一些未知错误...")

View File

@ -1,55 +0,0 @@
import time
from nonebot.adapters.onebot.v11 import Event
from services.log import logger
from utils.manager import group_manager, plugins2settings_manager
from utils.utils import get_message_text
cmd = []
v = time.time()
def switch_rule(event: Event) -> bool:
"""
说明:
检测文本是否是关闭功能命令
参数:
:param event: pass
"""
global cmd, v
try:
if not cmd or time.time() - v > 60 * 60:
cmd = ["关闭全部被动", "开启全部被动", "开启全部功能", "关闭全部功能"]
_data = group_manager.get_task_data()
for key in _data:
cmd.append(f"开启{_data[key]}")
cmd.append(f"关闭{_data[key]}")
cmd.append(f"开启被动{_data[key]}")
cmd.append(f"关闭被动{_data[key]}")
cmd.append(f"开启 {_data[key]}")
cmd.append(f"关闭 {_data[key]}")
_data = plugins2settings_manager.get_data()
for key in _data.keys():
try:
if isinstance(_data[key].cmd, list):
for x in _data[key].cmd:
cmd.append(f"开启{x}")
cmd.append(f"关闭{x}")
cmd.append(f"开启 {x}")
cmd.append(f"关闭 {x}")
else:
cmd.append(f"开启{key}")
cmd.append(f"关闭{key}")
cmd.append(f"开启 {key}")
cmd.append(f"关闭 {key}")
except KeyError:
pass
v = time.time()
msg = get_message_text(event.json()).split()
msg = msg[0] if msg else ""
return msg in cmd
except Exception as e:
logger.error(f"检测是否为功能开关命令发生错误", e=e)
return False

View File

@ -1,144 +0,0 @@
from typing import Any, Tuple
from nonebot import on_command, on_message, on_regex
from nonebot.adapters.onebot.v11 import (
GROUP,
Bot,
GroupMessageEvent,
Message,
MessageEvent,
PrivateMessageEvent,
)
from nonebot.params import CommandArg, RegexGroup
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
from services.log import logger
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from ._data_source import (
change_global_task_status,
change_group_switch,
get_plugin_status,
group_current_status,
set_group_bot_status,
set_plugin_status,
)
from .rule import switch_rule
__zx_plugin_name__ = "群功能开关 [Admin]"
__plugin_usage__ = """
usage
群内功能与被动技能开关
指令
开启/关闭[功能]
群被动状态
开启全部被动
关闭全部被动
醒来/休息吧
示例开启/关闭色图
""".strip()
__plugin_superuser_usage__ = """
usage:
私聊功能总开关与指定群禁用
指令
功能状态
开启/关闭[功能] [group]
开启/关闭[功能] ['private'/'group']
开启被动/关闭被动[被动名称] # 全局被动控制
""".strip()
__plugin_des__ = "群内功能开关"
__plugin_cmd__ = [
"开启/关闭[功能]",
"群被动状态",
"开启全部被动",
"关闭全部被动",
"醒来/休息吧",
"功能状态 [_superuser]",
"开启/关闭[功能] [group] [_superuser]",
"开启/关闭[功能] ['private'/'group'] [_superuser]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("admin_bot_manage", "CHANGE_GROUP_SWITCH_LEVEL"),
"cmd": ["开启功能", "关闭功能", "开关"],
}
switch_rule_matcher = on_message(rule=switch_rule, priority=4, block=True)
plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, block=True)
group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True)
group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, block=True)
@switch_rule_matcher.handle()
async def _(
bot: Bot,
event: MessageEvent,
):
msg = get_message_text(event.message).strip()
msg_split = msg.split()
_cmd = msg_split[0]
if isinstance(event, GroupMessageEvent):
await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id))
logger.info(f"使用群功能管理命令 {_cmd}", "功能管理", event.user_id, event.group_id)
else:
if str(event.user_id) in bot.config.superusers:
block_type = " ".join(msg_split[1:])
block_type = block_type if block_type else "a"
if ("关闭被动" in _cmd or "开启被动" in _cmd) and isinstance(
event, PrivateMessageEvent
):
await switch_rule_matcher.send(change_global_task_status(_cmd))
elif is_number(block_type):
if not int(block_type) in [
g["group_id"] for g in await bot.get_group_list()
]:
await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}")
await change_group_switch(_cmd, int(block_type), True)
group_name = (await bot.get_group_info(group_id=int(block_type)))[
"group_name"
]
await switch_rule_matcher.send(
f"{_cmd[:2]}群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能"
)
elif block_type in ["all", "private", "group", "a", "p", "g"]:
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) # type: ignore
if block_type == "all":
await switch_rule_matcher.send(f"{_cmd[:2]}功能:{_cmd[2:]}")
elif block_type == "private":
await switch_rule_matcher.send(f"已在私聊中{_cmd[:2]}功能:{_cmd[2:]}")
else:
await switch_rule_matcher.send(f"已在群聊中{_cmd[:2]}功能:{_cmd[2:]}")
else:
await switch_rule_matcher.finish("格式错误:关闭[功能] [group]/[p/g]")
logger.info(f"使用功能管理命令 {_cmd} | {block_type}", f"{_cmd}", event.user_id)
@plugins_status.handle()
async def _():
await plugins_status.send(await get_plugin_status())
@group_task_status.handle()
async def _(event: GroupMessageEvent):
await group_task_status.send(image(b64=await group_current_status(str(event.group_id))))
@group_status.handle()
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
cmd = reg_group[0]
if cmd == "休息吧":
msg = set_group_bot_status(str(event.group_id), False)
else:
msg = set_group_bot_status(str(event.group_id), True)
await group_status.send(msg)
logger.info(f"使用总开关命令: {cmd}", cmd, event.user_id, event.group_id)

View File

@ -1,48 +0,0 @@
from nonebot import get_bots
from services.log import logger
from utils.utils import scheduler
from ._data_source import update_member_info
__zx_plugin_name__ = "管理方面定时任务 [Hidden]"
__plugin_usage__ = ""
__plugin_des__ = "成员信息和管理权限的定时更新"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
async def update():
bot_list = get_bots()
if bot_list:
used_group = []
for key in bot_list:
bot = bot_list[key]
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl if g["group_id"] not in used_group]
for g in gl:
used_group.append(g)
try:
await update_member_info(bot, g) # type: ignore
logger.debug(f"更新群组成员信息成功", "自动更新群组成员信息", group_id=g)
except Exception as e:
logger.error(f"更新群组成员信息错误", "自动更新群组成员信息", group_id=g, e=e)
# 自动更新群员信息
@scheduler.scheduled_job(
"cron",
hour=2,
minute=1,
)
async def _():
await update()
# 快速更新群员信息以及管理员权限
@scheduler.scheduled_job(
"interval",
minutes=5,
)
async def _():
await update()

View File

@ -1,51 +0,0 @@
from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import (
GROUP,
Bot,
GroupIncreaseNoticeEvent,
GroupMessageEvent,
)
from services.log import logger
from ._data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """
usage
更新群组成员的基本信息
指令
更新群组成员列表/更新群组成员信息
""".strip()
__plugin_des__ = "更新群组成员列表"
__plugin_cmd__ = ["更新群组成员列表"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": 1,
}
refresh_member_group = on_command(
"更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True
)
@refresh_member_group.handle()
async def _(bot: Bot, event: GroupMessageEvent):
if await update_member_info(bot, event.group_id):
await refresh_member_group.send("更新群员信息成功!", at_sender=True)
logger.info("更新群员信息成功!", "更新群组成员列表", event.user_id, event.group_id)
else:
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)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
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

@ -1,27 +0,0 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.message_builder import image
from .data_source import create_help_image, ADMIN_HELP_IMAGE
__zx_plugin_name__ = '管理帮助 [Admin]'
__plugin_usage__ = '管理员帮助,在群内回复“管理员帮助”'
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"admin_level": 1,
}
admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True)
if ADMIN_HELP_IMAGE.exists():
ADMIN_HELP_IMAGE.unlink()
@admin_help.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if not ADMIN_HELP_IMAGE.exists():
await create_help_image()
await admin_help.send(image(ADMIN_HELP_IMAGE))

View File

@ -1,81 +0,0 @@
import nonebot
from nonebot import Driver
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.image_template import help_template
from utils.image_utils import BuildImage, build_sort_image, group_image, text2image
from utils.manager import group_manager, plugin_data_manager
from utils.manager.models import PluginType
driver: Driver = nonebot.get_driver()
ADMIN_HELP_IMAGE = IMAGE_PATH / "admin_help_img.png"
@driver.on_bot_connect
async def init_task():
if not group_manager.get_task_data():
group_manager.load_task()
logger.info(f"已成功加载 {len(group_manager.get_task_data())} 个被动技能.")
async def create_help_image():
"""
创建管理员帮助图片
"""
if ADMIN_HELP_IMAGE.exists():
return
plugin_data_ = plugin_data_manager.get_data()
image_list = []
task_list = []
for plugin_data in [plugin_data_[x] for x in plugin_data_]:
try:
usage = None
if plugin_data.plugin_type == PluginType.ADMIN and plugin_data.usage:
usage = await text2image(
plugin_data.usage, padding=5, color=(204, 196, 151)
)
if usage:
await usage.acircle_corner()
level = 5
if plugin_data.plugin_setting:
level = plugin_data.plugin_setting.level or level
image = await help_template(plugin_data.name + f"[{level}]", usage)
image_list.append(image)
if plugin_data.task:
for x in plugin_data.task.keys():
task_list.append(plugin_data.task[x])
except Exception as e:
logger.warning(
f"获取群管理员插件 {plugin_data.model}: {plugin_data.name} 设置失败...",
"管理员帮助",
e=e,
)
task_str = "\n".join(task_list)
task_str = "通过 开启/关闭 来控制群被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(204, 196, 151))
task_image = await help_template("被动任务", task_image)
image_list.append(task_image)
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color="#f9f6f2", padding_top=180)
await A.apaste(
BuildImage(0, 0, font="CJGaoDeGuo.otf", plain_text="群管理员帮助", font_size=50),
(50, 30),
True,
)
await A.apaste(
BuildImage(
0,
0,
font="CJGaoDeGuo.otf",
plain_text="注: * 代表可有多个相同参数 ? 代表可省略该参数",
font_size=30,
font_color="red",
),
(50, 90),
True,
)
await A.asave(ADMIN_HELP_IMAGE)
logger.info(f"已成功加载 {len(image_list)} 条管理员命令")

View File

@ -1,250 +0,0 @@
import shutil
from pathlib import Path
from typing import List
import nonebot
from nonebot import get_bots, on_message
from configs.config import NICKNAME, Config
from configs.path_config import IMAGE_PATH
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
from utils.message_builder import image
from utils.utils import broadcast_group, scheduler
__zx_plugin_name__ = "定时任务相关 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"zwa": "早晚安"}
Config.add_plugin_config(
"_task", "DEFAULT_ZWA", True, help_="被动 早晚安 进群默认开关状态", default_value=True, type=bool
)
Config.add_plugin_config(
"_backup", "BACKUP_FLAG", True, help_="是否开启文件备份", default_value=True, type=bool
)
Config.add_plugin_config(
"_backup",
"BACKUP_DIR_OR_FILE",
[
"data/black_word",
"data/configs",
"data/statistics",
"data/word_bank",
"data/manager",
"configs",
],
name="文件备份",
help_="备份的文件夹或文件",
default_value=[],
type=List[str],
)
cx = on_message(priority=9999, block=False, rule=lambda: False)
# 早上好
@scheduler.scheduled_job(
"cron",
hour=6,
minute=1,
)
async def _():
img = image(IMAGE_PATH / "zhenxun" / "zao.jpg")
await broadcast_group("[[_task|zwa]]早上好" + img, log_cmd="被动早晚安")
logger.info("每日早安发送...")
# 睡觉了
@scheduler.scheduled_job(
"cron",
hour=23,
minute=59,
)
async def _():
img = image(IMAGE_PATH / "zhenxun" / "sleep.jpg")
await broadcast_group(
f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + img, log_cmd="被动早晚安"
)
logger.info("每日晚安发送...")
# 自动更新群组信息
@scheduler.scheduled_job(
"cron",
hour=3,
minute=1,
)
async def _():
bots = nonebot.get_bots()
_used_group = []
for bot in bots.values():
try:
group_list = await bot.get_group_list()
gl = [g["group_id"] for g in group_list if g["group_id"] not in _used_group]
for g in gl:
_used_group.append(g)
group_info = await bot.get_group_info(group_id=g)
await GroupInfo.update_or_create(
group_id=str(group_info["group_id"]),
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
"group_flag": 1,
},
)
logger.debug("自动更新群组信息成功", "自动更新群组", group_id=g)
except Exception as e:
logger.error(f"Bot: {bot.self_id} 自动更新群组信息", e=e)
logger.info("自动更新群组成员信息成功...")
# 自动更新好友信息
@scheduler.scheduled_job(
"cron",
hour=3,
minute=1,
)
async def _():
bots = nonebot.get_bots()
for key in bots:
try:
bot = bots[key]
fl = await bot.get_friend_list()
for f in fl:
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.info("自动更新好友信息成功...")
# 自动备份
@scheduler.scheduled_job(
"cron",
hour=3,
minute=25,
)
async def _():
if Config.get_config("_backup", "BACKUP_FLAG"):
_backup_path = Path() / "backup"
_backup_path.mkdir(exist_ok=True, parents=True)
if backup_dir_or_file := Config.get_config("_backup", "BACKUP_DIR_OR_FILE"):
for path_file in backup_dir_or_file:
try:
path = Path(path_file)
_p = _backup_path / path_file
if path.exists():
if path.is_dir():
if _p.exists():
shutil.rmtree(_p, ignore_errors=True)
shutil.copytree(path_file, _p)
else:
if _p.exists():
_p.unlink()
shutil.copy(path_file, _p)
logger.debug(f"已完成自动备份:{path_file}", "自动备份")
except Exception as e:
logger.error(f"自动备份文件 {path_file} 发生错误", "自动备份", e=e)
logger.info("自动备份成功...", "自动备份")
# 一次性任务
# 固定时间触发,仅触发一次:
#
# from datetime import datetime
#
# @nonebot.scheduler.scheduled_job(
# 'date',
# run_date=datetime(2021, 1, 1, 0, 0),
# # timezone=None,
# )
# async def _():
# await bot.send_group_msg(group_id=123456,
# message="2021新年快乐")
# 定期任务
# 从 start_date 开始到 end_date 结束,根据类似 Cron
#
# 的规则触发任务:
#
# @nonebot.scheduler.scheduled_job(
# 'cron',
# # year=None,
# # month=None,
# # day=None,
# # week=None,
# day_of_week="mon,tue,wed,thu,fri",
# hour=7,
# # minute=None,
# # second=None,
# # start_date=None,
# # end_date=None,
# # timezone=None,
# )
# async def _():
# await bot.send_group_msg(group_id=123456,
# message="起床啦!")
# 间隔任务
#
# interval 触发器
#
# 从 start_date 开始,每间隔一段时间触发,到 end_date 结束:
#
# @nonebot.scheduler.scheduled_job(
# 'interval',
# # weeks=0,
# # days=0,
# # hours=0,
# minutes=5,
# # seconds=0,
# # start_date=time.now(),
# # end_date=None,
# )
# async def _():
# has_new_item = check_new_item()
# if has_new_item:
# await bot.send_group_msg(group_id=123456,
# message="XX有更新啦")
# 动态的计划任务
# import datetime
#
# from apscheduler.triggers.date import DateTrigger # 一次性触发器
# # from apscheduler.triggers.cron import CronTrigger # 定期触发器
# # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器
# from nonebot import on_command, scheduler
#
# @on_command('赖床')
# async def _(session: CommandSession):
# await session.send('我会在5分钟后再喊你')
#
# # 制作一个“5分钟后”触发器
# delta = datetime.timedelta(minutes=5)
# trigger = DateTrigger(
# run_date=datetime.datetime.now() + delta
# )
#
# # 添加任务
# scheduler.add_job(
# func=session.send, # 要添加任务的函数,不要带参数
# trigger=trigger, # 触发器
# args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号
# # kwargs=None,
# misfire_grace_time=60, # 允许的误差时间,建议不要省略
# # jobstore='default', # 任务储存库,在下一小节中说明
# )

View File

@ -1,179 +0,0 @@
from typing import List
from nonebot import on_command
from nonebot.adapters.onebot.v11 import (
Bot,
GroupMessageEvent,
Message,
MessageEvent,
PrivateMessageEvent,
)
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from models.level_user import LevelUser
from services.log import logger
from utils.depends import AtList, OneCommand
from utils.utils import is_number
from .data_source import a_ban, parse_ban_time
__zx_plugin_name__ = "封禁Ban用户 [Admin]"
__plugin_usage__ = """
usage
将用户拉入或拉出黑名单
指令:
.ban [at] ?[小时] ?[分钟]
.unban
示例.ban @user
示例.ban @user 6
示例.ban @user 3 10
示例.unban @user
""".strip()
__plugin_superuser_usage__ = """
usage
b了=屏蔽用户消息相当于最上级.ban
跨群ban以及跨群b了
指令
b了 [at/qq]
.ban [user_id] ?[小时] ?[分钟]
示例b了 @user
示例b了 1234567
示例.ban 12345567
""".strip()
__plugin_des__ = "你被逮捕了!丢进小黑屋!"
__plugin_cmd__ = [".ban [at] ?[小时] ?[分钟]", ".unban [at]", "b了 [at] [_superuser]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("ban", "BAN_LEVEL"),
"cmd": [".ban", ".unban", "ban", "unban"],
}
__plugin_configs__ = {
"BAN_LEVEL [LEVEL]": {
"value": 5,
"help": "ban/unban所需要的管理员权限等级",
"default_value": 5,
"type": int,
}
}
ban = on_command(
".ban",
aliases={".unban", "/ban", "/unban"},
priority=5,
block=True,
)
super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True)
@ban.handle()
async def _(
bot: Bot,
event: GroupMessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
at_list: List[int] = AtList(),
):
result = ""
if at_list:
qq = at_list[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
msg = arg.extract_plain_text().strip()
time = parse_ban_time(msg)
if isinstance(time, str):
await ban.finish(time, at_sender=True)
user_level = await LevelUser.get_user_level(event.user_id, event.group_id)
is_not_superuser = str(event.user_id) not in bot.config.superusers
if cmd in [".ban", "/ban"]:
at_user_level = await LevelUser.get_user_level(qq, event.group_id)
if user_level <= at_user_level and is_not_superuser:
await ban.finish(
f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!",
at_sender=True,
)
logger.info(f"用户封禁 时长: {time}", cmd, event.user_id, event.group_id, qq)
result = await a_ban(qq, time, user_name, event)
else:
if await BanUser.check_ban_level(qq, user_level) and is_not_superuser:
await ban.finish(
f"ban掉 {user_name} 的管理员权限比您高无法进行unban", at_sender=True
)
if await BanUser.unban(qq):
logger.info(f"解除用户封禁", cmd, event.user_id, event.group_id, qq)
result = f"已经将 {user_name} 从黑名单中删除了!"
else:
result = f"{user_name} 不在黑名单!"
else:
await ban.finish("艾特人了吗??", at_sender=True)
await ban.send(result, at_sender=True)
@ban.handle()
async def _(
bot: Bot,
event: PrivateMessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
):
msg = arg.extract_plain_text().strip()
if msg and str(event.user_id) in bot.config.superusers:
msg_split = msg.split()
if msg_split and is_number(msg_split[0]):
qq = int(msg_split[0])
param = msg_split[1:]
if cmd in [".ban", "/ban"]:
time = parse_ban_time(" ".join(param))
if isinstance(time, str):
logger.info(time, cmd, event.user_id, target=qq)
await ban.finish(time)
result = await a_ban(qq, time, str(qq), event, 9)
else:
if await BanUser.unban(qq):
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
logger.info(result, cmd, event.user_id, target=qq)
else:
await ban.send("参数不正确!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True)
@super_ban.handle()
async def _(
bot: Bot,
event: MessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
at_list: List[int] = AtList(),
):
user_name = ""
qq = None
if isinstance(event, GroupMessageEvent):
if at_list:
qq = at_list[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
else:
msg = arg.extract_plain_text().strip()
if not is_number(msg):
await super_ban.finish("对象qq必须为纯数字...")
qq = int(msg)
user_name = msg
if qq:
await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!")
logger.info(
f"已将 {user_name} 拉入黑名单!",
cmd,
event.user_id,
event.group_id if isinstance(event, GroupMessageEvent) else None,
qq,
)
else:
await super_ban.send("需要提供被super ban的对象可以使用at或者指定qq...")

View File

@ -1,77 +0,0 @@
from typing import Optional, Union
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from configs.config import NICKNAME
from models.ban_user import BanUser
from models.level_user import LevelUser
from services.log import logger
from utils.utils import is_number
def parse_ban_time(msg: str) -> Union[int, str]:
"""
解析ban时长
:param msg: 文本消息
"""
try:
if not msg:
return -1
msg_split = msg.split()
if len(msg_split) == 1:
if not is_number(msg_split[0].strip()):
return "参数必须是数字!"
return int(msg_split[0]) * 60 * 60
else:
if not is_number(msg_split[0].strip()) or not is_number(
msg_split[1].strip()
):
return "参数必须是数字!"
return int(msg_split[0]) * 60 * 60 + int(msg_split[1]) * 60
except ValueError as e:
logger.error("解析ban时长错误", ".ban", e=e)
return "时间解析错误!"
async def a_ban(
qq: int,
time: int,
user_name: str,
event: MessageEvent,
ban_level: Optional[int] = None,
) -> str:
"""
ban
:param qq: qq
:param time: ban时长
:param user_name: ban用户昵称
:param event: event
:param ban_level: ban级别
"""
group_id = None
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if not ban_level:
return "未查询到ban级用户权限"
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"封禁 时长 {time / 60} 分钟", ".ban", event.user_id, group_id, qq
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time / 60} 分钟后解封"
else:
result += f"将在 ∞ 分钟后解封"
else:
ban_time = await BanUser.check_ban_time(qq)
if isinstance(ban_time, int):
ban_time = abs(float(ban_time))
if ban_time < 60:
ban_time = str(ban_time) + ""
else:
ban_time = str(int(ban_time / 60)) + " 分钟"
else:
ban_time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {ban_time}后解封"
return result

View File

@ -1,73 +0,0 @@
import asyncio
from typing import List
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.config import Config
from services.log import logger
from utils.depends import ImageList
from utils.manager import group_manager
from utils.message_builder import image
__zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """
usage
指令
广播- ?[消息] ?[图片]
示例广播- 你们好
""".strip()
__plugin_des__ = "昭告天下!"
__plugin_cmd__ = ["广播-"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"broadcast": "广播"}
Config.add_plugin_config(
"_task",
"DEFAULT_BROADCAST",
True,
help_="被动 广播 进群默认开关状态",
default_value=True,
type=bool,
)
broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@broadcast.handle()
async def _(
bot: Bot,
event: MessageEvent,
arg: Message = CommandArg(),
img_list: List[str] = ImageList(),
):
msg = arg.extract_plain_text().strip()
rst = ""
for img in img_list:
rst += image(img)
gl = [
g["group_id"]
for g in await bot.get_group_list()
if group_manager.check_group_task_status(str(g["group_id"]), "broadcast")
]
g_cnt = len(gl)
cnt = 0
error = ""
x = 0.25
for g in gl:
cnt += 1
if cnt / g_cnt > x:
await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊")
x += 0.25
try:
await bot.send_group_msg(group_id=g, message=msg + rst)
logger.info(f"投递广播成功", "广播", group_id=g)
except Exception as e:
logger.error(f"投递广播失败", "广播", group_id=g, e=e)
error += f"GROUP {g} 投递广播失败:{type(e)}\n"
await asyncio.sleep(0.5)
await broadcast.send(f"已播报至 100% 的群聊")
if error:
await broadcast.send(f"播报时错误:{error}")

View File

@ -1,9 +0,0 @@
from nonebot.adapters.onebot.v11 import Event, MessageEvent
from configs.config import Config
def rule(event: Event) -> bool:
return bool(
Config.get_config("chat_history", "FLAG") and isinstance(event, MessageEvent)
)

View File

@ -1,72 +0,0 @@
from nonebot import on_message
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent
from configs.config import Config
from models.chat_history import ChatHistory
from services.log import logger
from utils.depends import PlaintText
from utils.utils import scheduler
from ._rule import rule
__zx_plugin_name__ = "消息存储 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
Config.add_plugin_config(
"chat_history",
"FLAG",
True,
help_="是否开启消息自从存储",
name="消息存储",
default_value=True,
type=bool,
)
chat_history = on_message(rule=rule, priority=1, block=False)
TEMP_LIST = []
@chat_history.handle()
async def _(bot: Bot, event: MessageEvent, msg: str = PlaintText()):
group_id = None
if isinstance(event, GroupMessageEvent):
group_id = str(event.group_id)
TEMP_LIST.append(
ChatHistory(
user_id=str(event.user_id),
group_id=group_id,
text=str(event.get_message()),
plain_text=msg,
bot_id=str(bot.self_id),
)
)
@scheduler.scheduled_job(
"interval",
minutes=1,
)
async def _():
try:
message_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if message_list:
await ChatHistory.bulk_create(message_list)
logger.debug(f"批量添加聊天记录 {len(message_list)}", "定时任务")
except Exception as e:
logger.error(f"定时批量添加聊天记录", "定时任务", e=e)
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))

View File

@ -1,113 +0,0 @@
from datetime import datetime, timedelta
from typing import Any, Tuple
import pytz
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.params import RegexGroup
from models.chat_history import ChatHistory
from models.group_member_info import GroupInfoUser
from utils.image_utils import BuildImage, text2image
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "消息统计"
__plugin_usage__ = """
usage
发言记录统计
regex(||)?消息排行(des|DES)?(n=[0-9]{1,2})?
指令
消息统计?(des)?(n=?)
周消息统计?(des)?(n=?)
月消息统计?(des)?(n=?)
日消息统计?(des)?(n=?)
示例
消息统计
消息统计des
消息统计DESn=15
消息统计n=15
""".strip()
__plugin_des__ = "发言消息排行"
__plugin_cmd__ = ["消息统计", "周消息统计", "月消息统计", "日消息统计"]
__plugin_type__ = ("数据统计", 1)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["消息统计"],
}
msg_handler = on_regex(
r"^(周|月|日)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True
)
@msg_handler.handle()
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
gid = event.group_id
date_scope = None
date, order, num = reg_group
num = num.split("=")[-1] if num else 10
if num and is_number(num) and 10 < int(num) < 50:
num = int(num)
time_now = datetime.now()
zero_today = time_now - timedelta(
hours=time_now.hour, minutes=time_now.minute, seconds=time_now.second
)
if date in [""]:
date_scope = (zero_today, time_now)
elif date in [""]:
date_scope = (time_now - timedelta(days=7), time_now)
elif date in [""]:
date_scope = (time_now - timedelta(days=30), time_now)
if rank_data := await ChatHistory.get_group_msg_rank(
gid, num, order or "DESC", date_scope
):
name = "昵称:\n\n"
num_str = "发言次数:\n\n"
idx = 1
for uid, num in rank_data:
if user := await GroupInfoUser.filter(user_id=uid, group_id=gid).first():
user_name = user.user_name
else:
user_name = uid
name += f"\t{idx}.{user_name} \n\n"
num_str += f"\t{num}\n\n"
idx += 1
name_img = await text2image(name.strip(), padding=10, color="#f9f6f2")
num_img = await text2image(num_str.strip(), padding=10, color="#f9f6f2")
if not date_scope:
if date_scope := await ChatHistory.get_group_first_msg_datetime(gid):
date_scope = date_scope.astimezone(
pytz.timezone("Asia/Shanghai")
).replace(microsecond=0)
else:
date_scope = time_now.replace(microsecond=0)
date_str = f"日期:{date_scope} - 至今"
else:
date_str = f"日期:{date_scope[0].replace(microsecond=0)} - {date_scope[1].replace(microsecond=0)}"
date_w = BuildImage(0, 0, font_size=15).getsize(date_str)[0]
img_w = date_w if date_w > name_img.w + num_img.w else name_img.w + num_img.w
A = BuildImage(
img_w + 15,
num_img.h + 30,
color="#f9f6f2",
font="CJGaoDeGuo.otf",
font_size=15,
)
await A.atext((10, 10), date_str)
await A.apaste(name_img, (0, 30))
await A.apaste(num_img, (name_img.w, 30))
await msg_handler.send(image(b64=A.pic2bs4()))
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))

View File

@ -1,236 +0,0 @@
import os
import random
from datetime import datetime
from pathlib import Path
import ujson as json
from nonebot import on_notice, on_request
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
GroupDecreaseNoticeEvent,
GroupIncreaseNoticeEvent,
)
from configs.config import NICKNAME, Config
from configs.path_config import DATA_PATH, IMAGE_PATH
from models.group_info import GroupInfo
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from services.log import logger
from utils.depends import GetConfig
from utils.manager import group_manager, plugins2settings_manager, requests_manager
from utils.message_builder import image
from utils.utils import FreqLimiter
__zx_plugin_name__ = "群事件处理 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"}
Config.add_plugin_config(
"invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.."
)
Config.add_plugin_config(
"invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True, type=bool
)
Config.add_plugin_config(
"invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5, type=int
)
Config.add_plugin_config(
"_task",
"DEFAULT_GROUP_WELCOME",
True,
help_="被动 进群欢迎 进群默认开关状态",
default_value=True,
type=bool,
)
Config.add_plugin_config(
"_task",
"DEFAULT_REFUND_GROUP_REMIND",
True,
help_="被动 退群提醒 进群默认开关状态",
default_value=True,
type=bool,
)
_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd") or 5)
# 群员增加处理
group_increase_handle = on_notice(priority=1, block=False)
# 群员减少处理
group_decrease_handle = on_notice(priority=1, block=False)
# (群管理)加群同意请求
add_group = on_request(priority=1, block=False)
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if event.user_id == int(bot.self_id):
group = await GroupInfo.get_or_none(group_id=str(event.group_id))
# 群聊不存在或被强制拉群,退出该群
if (not group or group.group_flag == 0) and Config.get_config(
"invite_manager", "flag"
):
try:
msg = Config.get_config("invite_manager", "message")
if msg:
await bot.send_group_msg(group_id=event.group_id, message=msg)
await bot.set_group_leave(group_id=event.group_id)
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"触发强制入群保护,已成功退出群聊 {event.group_id}...",
)
logger.info(f"强制拉群或未有群信息,退出群聊成功", "入群检测", group_id=event.group_id)
requests_manager.remove_request("group", event.group_id)
except Exception as e:
logger.info(f"强制拉群或未有群信息,退出群聊失败", "入群检测", group_id=event.group_id, e=e)
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"触发强制入群保护,退出群聊 {event.group_id} 失败...",
)
# 默认群功能开关
elif event.group_id not in group_manager.get_data().group_manager.keys():
data = plugins2settings_manager.get_data()
for plugin in data.keys():
if not data[plugin].default_status:
group_manager.block_plugin(plugin, str(event.group_id))
admin_default_auth = Config.get_config(
"admin_bot_manage", "ADMIN_DEFAULT_AUTH"
)
# 即刻刷新权限
for user_info in await bot.get_group_member_list(group_id=event.group_id):
if (
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(
user_info["user_id"], event.group_id
)
and admin_default_auth is not None
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
admin_default_auth,
)
logger.debug(
f"添加默认群管理员权限: {admin_default_auth}",
"入群检测",
user_info["user_id"],
user_info["group_id"],
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
logger.debug(
f"添加超级用户权限: 9",
"入群检测",
user_info["user_id"],
user_info["group_id"],
)
else:
join_time = datetime.now()
user_info = await bot.get_group_member_info(
group_id=event.group_id, user_id=event.user_id
)
await GroupInfoUser.update_or_create(
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},
)
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
# 群欢迎消息
if _flmt.check(event.group_id):
_flmt.start_cd(event.group_id)
msg = ""
img = ""
at_flag = False
custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
)
if custom_welcome_msg_json.exists():
data = json.load(open(custom_welcome_msg_json, "r"))
if data.get(str(event.group_id)):
msg = data[str(event.group_id)]
if "[at]" in msg:
msg = msg.replace("[at]", "")
at_flag = True
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
if msg or img:
msg = msg.strip() + img
msg = "\n" + msg if at_flag else msg
await group_increase_handle.send(
"[[_task|group_welcome]]" + msg, at_sender=at_flag
)
else:
await group_increase_handle.send(
"[[_task|group_welcome]]新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(
IMAGE_PATH
/ "qxz"
/ random.choice(os.listdir(IMAGE_PATH / "qxz"))
)
)
@group_decrease_handle.handle()
async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
# 被踢出群
if event.sub_type == "kick_me":
group_id = event.group_id
operator_id = event.operator_id
if user := await GroupInfoUser.get_or_none(
user_id=str(event.operator_id), group_id=str(event.group_id)
):
operator_name = user.user_name
else:
operator_name = "None"
group = await GroupInfo.filter(group_id=str(group_id)).first()
group_name = group.group_name if group else ""
coffee = int(list(bot.config.superusers)[0])
await bot.send_private_msg(
user_id=coffee,
message=f"****呜..一份踢出报告****\n"
f"我被 {operator_name}({operator_id})\n"
f"踢出了 {group_name}({group_id})\n"
f"日期:{str(datetime.now()).split('.')[0]}",
)
return
if event.user_id == int(bot.self_id):
group_manager.delete_group(event.group_id)
return
if user := await GroupInfoUser.get_or_none(
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()
logger.info(
f"名称: {user_name} 退出群聊",
"group_decrease_handle",
event.user_id,
event.group_id,
)
rst = ""
if event.sub_type == "leave":
rst = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = operator["card"] if operator["card"] else operator["nickname"]
rst = f"{user_name}{operator_name} 送走了."
try:
await group_decrease_handle.send(f"[[_task|refund_group_remind]]{rst}")
except ActionFailed:
return

View File

@ -1,64 +0,0 @@
import os
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.rule import to_me
from configs.path_config import DATA_PATH, IMAGE_PATH
from services.log import logger
from utils.message_builder import image
from ._data_source import create_help_img, get_plugin_help
from ._utils import GROUP_HELP_PATH
__zx_plugin_name__ = "帮助"
__plugin_configs__ = {
"TYPE": {
"value": "normal",
"help": "帮助图片样式 ['normal', 'HTML']",
"default_value": "normal",
"type": str,
}
}
simple_help_image = IMAGE_PATH / "simple_help.png"
if simple_help_image.exists():
simple_help_image.unlink()
simple_help = on_command(
"功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True
)
@simple_help.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
is_super = False
if msg:
if "-super" in msg:
if str(event.user_id) in bot.config.superusers:
is_super = True
msg = msg.replace("-super", "").strip()
img_msg = get_plugin_help(msg, is_super)
if img_msg:
await simple_help.send(image(b64=img_msg))
else:
await simple_help.send("没有此功能的帮助信息...")
logger.info(
f"查看帮助详情: {msg}", "帮助", event.user_id, getattr(event, "group_id", None)
)
else:
if isinstance(event, GroupMessageEvent):
_image_path = GROUP_HELP_PATH / f"{event.group_id}.png"
if not _image_path.exists():
await create_help_img(event.group_id)
await simple_help.send(image(_image_path))
else:
if not simple_help_image.exists():
if simple_help_image.exists():
simple_help_image.unlink()
await create_help_img(None)
await simple_help.finish(image("simple_help.png"))

View File

@ -1,56 +0,0 @@
from typing import Optional
from configs.path_config import IMAGE_PATH
from utils.image_utils import BuildImage
from utils.manager import admin_manager, plugin_data_manager, plugins2settings_manager
from ._utils import HelpImageBuild
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help"
background = IMAGE_PATH / "background" / "0.png"
async def create_help_img(group_id: Optional[int]):
"""
说明:
生成帮助图片
参数:
:param group_id: 群号
"""
await HelpImageBuild().build_image(group_id)
def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
"""
说明:
获取功能的帮助信息
参数:
:param msg: 功能cmd
:param is_super: 是否为超级用户
"""
module = plugins2settings_manager.get_plugin_module(
msg
) or admin_manager.get_plugin_module(msg)
if module and (plugin_data := plugin_data_manager.get(module)):
plugin_data.superuser_usage
if is_super:
result = plugin_data.superuser_usage
else:
result = plugin_data.usage
if result:
width = 0
for x in result.split("\n"):
_width = len(x) * 24
width = width if width > _width else _width
height = len(result.split("\n")) * 45
A = BuildImage(width, height, font_size=24)
bk = BuildImage(
width,
height,
background=IMAGE_PATH / "background" / "1.png",
)
A.paste(bk, alpha=True)
A.text((int(width * 0.048), int(height * 0.21)), result)
return A.pic2bs4()
return None

View File

@ -1,33 +0,0 @@
from configs.config import Config
Config.add_plugin_config(
"hook",
"CHECK_NOTICE_INFO_CD",
300,
name="基础hook配置",
help_="群检测个人权限检测等各种检测提示信息cd",
default_value=300,
type=int,
)
Config.add_plugin_config(
"hook",
"MALICIOUS_BAN_TIME",
30,
help_="恶意命令触发检测触发后ban的时长分钟",
default_value=30,
type=int,
)
Config.add_plugin_config(
"hook",
"MALICIOUS_CHECK_TIME",
5,
help_="恶意命令触发检测规定时间内(秒)",
default_value=5,
type=int,
)
Config.add_plugin_config(
"hook", "MALICIOUS_BAN_COUNT", 6, help_="恶意命令触发检测最大触发次数", default_value=6, type=int
)

View File

@ -1,580 +0,0 @@
import time
from nonebot.adapters.onebot.v11 import (
Bot,
Event,
GroupMessageEvent,
Message,
MessageEvent,
PokeNotifyEvent,
PrivateMessageEvent,
)
from nonebot.exception import ActionFailed, IgnoredException
from nonebot.internal.matcher import Matcher
from configs.config import Config
from models.bag_user import BagUser
from models.ban_user import BanUser
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from models.level_user import LevelUser
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.decorator import Singleton
from utils.manager import (
StaticData,
admin_manager,
group_manager,
plugin_data_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2count_manager,
plugins2settings_manager,
plugins_manager,
)
from utils.manager.models import PluginType
from utils.message_builder import at
from utils.utils import FreqLimiter
ignore_rst_module = ["ai", "poke", "dialogue"]
other_limit_plugins = ["poke"]
class StatusMessageManager(StaticData):
def __init__(self):
super().__init__(None)
def add(self, id_: int):
self._data[id_] = time.time()
def delete(self, id_: int):
if self._data.get(id_):
del self._data[id_]
def check(self, id_: int, t: int = 30) -> bool:
if self._data.get(id_):
if time.time() - self._data[id_] > t:
del self._data[id_]
return True
return False
return True
status_message_manager = StatusMessageManager()
def set_block_limit_false(event, module):
"""
设置用户block为false
:param event: event
:param module: 插件模块
"""
if plugins2block_manager.check_plugin_block_status(module):
if plugin_block_data := plugins2block_manager.get_plugin_block_data(module):
check_type = plugin_block_data.check_type
limit_type = plugin_block_data.limit_type
if not (
(isinstance(event, GroupMessageEvent) and check_type == "private")
or (isinstance(event, PrivateMessageEvent) and check_type == "group")
):
block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
plugins2block_manager.set_false(block_type_, module)
async def send_msg(msg: str, bot: Bot, event: MessageEvent):
"""
说明:
发送信息
参数:
:param msg: pass
:param bot: pass
:param event: pass
"""
if "[uname]" in msg:
uname = event.sender.card or event.sender.nickname or ""
msg = msg.replace("[uname]", uname)
if "[nickname]" in msg:
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_user_nickname(
event.user_id, event.group_id
)
else:
nickname = await FriendUser.get_user_nickname(event.user_id)
msg = msg.replace("[nickname]", nickname)
if "[at]" in msg and isinstance(event, GroupMessageEvent):
msg = msg.replace("[at]", str(at(event.user_id)))
try:
if isinstance(event, GroupMessageEvent):
status_message_manager.add(event.group_id)
await bot.send_group_msg(group_id=event.group_id, message=Message(msg))
else:
status_message_manager.add(event.user_id)
await bot.send_private_msg(user_id=event.user_id, message=Message(msg))
except ActionFailed:
pass
class IsSuperuserException(Exception):
pass
@Singleton
class AuthChecker:
"""
权限检查
"""
def __init__(self):
check_notice_info_cd = Config.get_config("hook", "CHECK_NOTICE_INFO_CD")
if check_notice_info_cd is None or check_notice_info_cd < 0:
raise ValueError("模块: [hook], 配置项: [CHECK_NOTICE_INFO_CD] 为空或小于0")
self._flmt = FreqLimiter(check_notice_info_cd)
self._flmt_g = FreqLimiter(check_notice_info_cd)
self._flmt_s = FreqLimiter(check_notice_info_cd)
self._flmt_c = FreqLimiter(check_notice_info_cd)
async def auth(self, matcher: Matcher, bot: Bot, event: Event):
"""
说明:
权限检查
参数:
:param matcher: matcher
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
try:
if plugin_name := matcher.plugin_name:
# self.auth_hidden(matcher, plugin_name)
cost_gold = await self.auth_cost(plugin_name, bot, event)
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
# if user_id and str(user_id) not in bot.config.superusers:
await self.auth_basic(plugin_name, bot, event)
self.auth_group(plugin_name, bot, event)
await self.auth_admin(plugin_name, matcher, bot, event)
await self.auth_plugin(plugin_name, matcher, bot, event)
await self.auth_limit(plugin_name, bot, event)
if cost_gold and user_id and group_id:
await BagUser.spend_gold(user_id, group_id, cost_gold)
logger.debug(f"调用功能花费金币: {cost_gold}", "HOOK", user_id, group_id)
except IsSuperuserException:
logger.debug(f"超级用户或被ban跳过权限检测...", "HOOK", user_id, group_id)
# def auth_hidden(self, matcher: Matcher):
# if plugin_data := plugin_data_manager.get(matcher.plugin_name): # type: ignore
async def auth_limit(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
插件限制
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
if not user_id:
return
group_id = getattr(event, "group_id", None)
if plugins2cd_manager.check_plugin_cd_status(plugin_name):
if (
plugin_cd_data := plugins2cd_manager.get_plugin_cd_data(plugin_name)
) and (plugin_data := plugins2cd_manager.get_plugin_data(plugin_name)):
check_type = plugin_cd_data.check_type
limit_type = plugin_cd_data.limit_type
msg = plugin_cd_data.rst
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugin_data.check_type == "all"
):
cd_type_ = user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(plugin_name, cd_type_):
if msg:
await send_msg(msg, bot, event) # type: ignore
logger.debug(
f"{plugin_name} 正在cd中...", "HOOK", user_id, group_id
)
raise IgnoredException(f"{plugin_name} 正在cd中...")
else:
plugins2cd_manager.start_cd(plugin_name, cd_type_)
# Block
if plugins2block_manager.check_plugin_block_status(plugin_name):
if plugin_block_data := plugins2block_manager.get_plugin_block_data(
plugin_name
):
check_type = plugin_block_data.check_type
limit_type = plugin_block_data.limit_type
msg = plugin_block_data.rst
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all"
):
block_type_ = user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
block_type_ = event.group_id
if plugins2block_manager.check(block_type_, plugin_name):
if msg:
await send_msg(msg, bot, event) # type: ignore
logger.debug(f"正在调用{plugin_name}...", "HOOK", user_id, group_id)
raise IgnoredException(f"{user_id}正在调用{plugin_name}....")
else:
plugins2block_manager.set_true(block_type_, plugin_name)
# Count
if (
plugins2count_manager.check_plugin_count_status(plugin_name)
and user_id not in bot.config.superusers
):
if plugin_count_data := plugins2count_manager.get_plugin_count_data(
plugin_name
):
limit_type = plugin_count_data.limit_type
msg = plugin_count_data.rst
count_type_ = user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
count_type_ = event.group_id
if not plugins2count_manager.check(plugin_name, count_type_):
if msg:
await send_msg(msg, bot, event) # type: ignore
logger.debug(
f"{plugin_name} count次数限制...", "HOOK", user_id, group_id
)
raise IgnoredException(f"{plugin_name} count次数限制...")
else:
plugins2count_manager.increase(plugin_name, count_type_)
async def auth_plugin(
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
):
"""
说明:
插件状态
参数:
:param plugin_name: 模块名
:param matcher: matcher
:param bot: bot
:param event: event
"""
if plugin_name in plugins2settings_manager.keys() and matcher.priority not in [
1,
999,
]:
user_id = getattr(event, "user_id", None)
if not user_id:
return
group_id = getattr(event, "group_id", None)
# 戳一戳单独判断
if (
isinstance(event, GroupMessageEvent)
or isinstance(event, PokeNotifyEvent)
or matcher.plugin_name in other_limit_plugins
) and group_id:
if status_message_manager.get(group_id) is None:
status_message_manager.delete(group_id)
if plugins2settings_manager[
plugin_name
].level > group_manager.get_group_level(group_id):
try:
if (
self._flmt_g.check(user_id)
and plugin_name not in ignore_rst_module
):
self._flmt_g.start_cd(user_id)
await bot.send_group_msg(
group_id=group_id, message="群权限不足..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(group_id)
set_block_limit_false(event, plugin_name)
logger.debug(f"{plugin_name} 群权限不足...", "HOOK", user_id, group_id)
raise IgnoredException("群权限不足")
# 插件状态
if not group_manager.get_plugin_status(plugin_name, group_id):
try:
if plugin_name not in ignore_rst_module and self._flmt_s.check(
group_id
):
self._flmt_s.start_cd(group_id)
await bot.send_group_msg(
group_id=group_id, message="该群未开启此功能.."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(group_id)
set_block_limit_false(event, plugin_name)
logger.debug(f"{plugin_name} 未开启此功能...", "HOOK", user_id, group_id)
raise IgnoredException("未开启此功能...")
# 管理员禁用
if not group_manager.get_plugin_status(
f"{plugin_name}:super", group_id
):
try:
if (
self._flmt_s.check(group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_s.start_cd(group_id)
await bot.send_group_msg(
group_id=group_id, message="管理员禁用了此群该功能..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(group_id)
set_block_limit_false(event, plugin_name)
logger.debug(
f"{plugin_name} 管理员禁用了此群该功能...", "HOOK", user_id, group_id
)
raise IgnoredException("管理员禁用了此群该功能...")
# 群聊禁用
if not plugins_manager.get_plugin_status(
plugin_name, block_type="group"
):
try:
if (
self._flmt_c.check(group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_c.start_cd(group_id)
await bot.send_group_msg(
group_id=group_id, message="该功能在群聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(group_id)
set_block_limit_false(event, plugin_name)
logger.debug(
f"{plugin_name} 该插件在群聊中已被禁用...", "HOOK", user_id, group_id
)
raise IgnoredException("该插件在群聊中已被禁用...")
else:
# 私聊禁用
if not plugins_manager.get_plugin_status(
plugin_name, block_type="private"
):
try:
if self._flmt_c.check(user_id):
self._flmt_c.start_cd(user_id)
await bot.send_private_msg(
user_id=user_id, message="该功能在私聊中已被禁用..."
)
except ActionFailed:
pass
if event.is_tome():
status_message_manager.add(user_id)
set_block_limit_false(event, plugin_name)
logger.debug(
f"{plugin_name} 该插件在私聊中已被禁用...", "HOOK", user_id, group_id
)
raise IgnoredException("该插件在私聊中已被禁用...")
# 维护
if not plugins_manager.get_plugin_status(plugin_name, block_type="all"):
if isinstance(
event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id):
raise IsSuperuserException()
try:
if isinstance(event, GroupMessageEvent):
if (
self._flmt_c.check(event.group_id)
and plugin_name not in ignore_rst_module
):
self._flmt_c.start_cd(event.group_id)
await bot.send_group_msg(
group_id=event.group_id, message="此功能正在维护..."
)
else:
await bot.send_private_msg(
user_id=user_id, message="此功能正在维护..."
)
except ActionFailed:
pass
if event.is_tome():
id_ = group_id or user_id
status_message_manager.add(id_)
set_block_limit_false(event, plugin_name)
logger.debug(f"{plugin_name} 此功能正在维护...", "HOOK", user_id, group_id)
raise IgnoredException("此功能正在维护...")
async def auth_admin(
self, plugin_name: str, matcher: Matcher, bot: Bot, event: Event
):
"""
说明:
管理员命令 个人权限
参数:
:param plugin_name: 模块名
:param matcher: matcher
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
if not user_id:
return
group_id = getattr(event, "group_id", None)
if plugin_name in admin_manager.keys() and matcher.priority not in [1, 999]:
if isinstance(event, GroupMessageEvent):
# 个人权限
if (
not await LevelUser.check_level(
event.user_id,
event.group_id,
admin_manager.get_plugin_level(plugin_name),
)
and admin_manager.get_plugin_level(plugin_name) > 0
):
try:
if self._flmt.check(event.user_id):
self._flmt.start_cd(event.user_id)
await bot.send_group_msg(
group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(plugin_name)}",
)
except ActionFailed:
pass
set_block_limit_false(event, plugin_name)
if event.is_tome():
status_message_manager.add(event.group_id)
logger.debug(f"{plugin_name} 管理员权限不足...", "HOOK", user_id, group_id)
raise IgnoredException("管理员权限不足")
else:
if not await LevelUser.check_level(
user_id, 0, admin_manager.get_plugin_level(plugin_name)
):
try:
await bot.send_private_msg(
user_id=user_id,
message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(plugin_name)}",
)
except ActionFailed:
pass
set_block_limit_false(event, plugin_name)
if event.is_tome():
status_message_manager.add(user_id)
logger.debug(f"{plugin_name} 管理员权限不足...", "HOOK", user_id, group_id)
raise IgnoredException("权限不足")
def auth_group(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
群黑名单检测 群总开关检测
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
if not group_id:
return
if (
group_manager.get_group_level(group_id) < 0
and str(user_id) not in bot.config.superusers
):
logger.debug(f"{plugin_name} 群黑名单, 群权限-1...", "HOOK", user_id, group_id)
raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(group_id):
try:
if str(event.get_message()) != "醒来":
logger.debug(
f"{plugin_name} 功能总开关关闭状态...", "HOOK", user_id, group_id
)
raise IgnoredException("功能总开关关闭状态")
except ValueError:
logger.debug(f"{plugin_name} 功能总开关关闭状态...", "HOOK", user_id, group_id)
raise IgnoredException("功能总开关关闭状态")
async def auth_basic(self, plugin_name: str, bot: Bot, event: Event):
"""
说明:
检测是否满足超级用户权限是否被ban等
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
if not user_id:
return
plugin_setting = plugins2settings_manager.get_plugin_data(plugin_name)
if (
(
not isinstance(event, MessageEvent)
and plugin_name not in other_limit_plugins
)
or await BanUser.is_ban(user_id)
and str(user_id) not in bot.config.superusers
) or (
str(user_id) in bot.config.superusers
and plugin_setting
and not plugin_setting.limit_superuser
):
raise IsSuperuserException()
if plugin_data := plugin_data_manager.get(plugin_name):
if (
plugin_data.plugin_type == PluginType.SUPERUSER
and str(user_id) in bot.config.superusers
):
raise IsSuperuserException()
async def auth_cost(self, plugin_name: str, bot: Bot, event: Event) -> int:
"""
说明:
检测是否满足金币条件
参数:
:param plugin_name: 模块名
:param bot: bot
:param event: event
"""
user_id = getattr(event, "user_id", None)
if not user_id:
return 0
group_id = getattr(event, "group_id", None)
cost_gold = 0
if isinstance(event, GroupMessageEvent) and (
psm := plugins2settings_manager.get_plugin_data(plugin_name)
):
if psm.cost_gold > 0:
if (
await BagUser.get_gold(event.user_id, event.group_id)
< psm.cost_gold
):
await send_msg(f"金币不足..该功能需要{psm.cost_gold}金币..", bot, event)
logger.debug(
f"{plugin_name} 金币限制..该功能需要{psm.cost_gold}金币..",
"HOOK",
user_id,
group_id,
)
raise IgnoredException(f"{plugin_name} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币
if (
str(event.user_id) in bot.config.superusers
and not psm.limit_superuser
):
await BagUser.spend_gold(
event.user_id, event.group_id, psm.cost_gold
)
await UserShopGoldLog.create(
user_id=str(event.user_id),
group_id=str(event.group_id),
type=2,
name=plugin_name,
num=1,
spend_gold=psm.cost_gold,
)
cost_gold = psm.cost_gold
return cost_gold

View File

@ -1,36 +0,0 @@
from typing import Optional
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
Event,
)
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor
from nonebot.typing import T_State
from ._utils import (
set_block_limit_false,
AuthChecker,
)
# # 权限检测
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: Event):
await AuthChecker().auth(matcher, bot, event)
# 解除命令block阻塞
@run_postprocessor
async def _(
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
):
if not isinstance(event, MessageEvent) and matcher.plugin_name != "poke":
return
module = matcher.plugin_name
set_block_limit_false(event, module)

View File

@ -1,83 +0,0 @@
from nonebot.adapters.onebot.v11 import ActionFailed, Bot, Event, GroupMessageEvent
from nonebot.matcher import Matcher
from nonebot.message import IgnoredException, run_preprocessor
from nonebot.typing import T_State
from configs.config import Config
from models.ban_user import BanUser
from services.log import logger
from utils.message_builder import at
from utils.utils import FreqLimiter, is_number, static_flmt
from ._utils import ignore_rst_module, other_limit_plugins
Config.add_plugin_config(
"hook",
"BAN_RESULT",
"才不会给你发消息.",
help_="对被ban用户发送的消息",
)
_flmt = FreqLimiter(300)
# 检查是否被ban
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
if user_id and (
matcher.priority not in [1, 999] or matcher.plugin_name in other_limit_plugins
):
if (
await BanUser.is_super_ban(user_id)
and str(user_id) not in bot.config.superusers
):
logger.debug(f"用户处于超级黑名单中...", "HOOK", user_id, group_id)
raise IgnoredException("用户处于超级黑名单中")
if await BanUser.is_ban(user_id) and str(user_id) not in bot.config.superusers:
time = await BanUser.check_ban_time(user_id)
if isinstance(time, int):
time = abs(int(time))
if time < 60:
time = str(time) + ""
else:
time = str(int(time / 60)) + " 分钟"
else:
time = str(time) + " 分钟"
if isinstance(event, GroupMessageEvent):
if not static_flmt.check(user_id):
logger.debug(f"用户处于黑名单中...", "HOOK", user_id, group_id)
raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(user_id)
if matcher.priority != 999:
try:
ban_result = Config.get_config("hook", "BAN_RESULT")
if (
ban_result
and _flmt.check(user_id)
and matcher.plugin_name not in ignore_rst_module
):
_flmt.start_cd(user_id)
await bot.send_group_msg(
group_id=event.group_id,
message=at(user_id)
+ ban_result
+ f" 在..在 {time} 后才会理你喔",
)
except ActionFailed:
pass
else:
if not static_flmt.check(user_id):
logger.debug(f"用户处于黑名单中...", "HOOK", user_id, group_id)
raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(user_id)
if matcher.priority != 999:
ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result and matcher.plugin_name not in ignore_rst_module:
await bot.send_private_msg(
user_id=user_id,
message=at(user_id) + ban_result + f" 在..在 {time}后才会理你喔",
)
logger.debug(f"用户处于黑名单中...", "HOOK", user_id, group_id)
raise IgnoredException("用户处于黑名单中")

View File

@ -1,72 +0,0 @@
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
GroupMessageEvent,
MessageEvent,
)
from nonebot.matcher import Matcher
from nonebot.message import IgnoredException, run_preprocessor
from nonebot.typing import T_State
from configs.config import Config
from models.ban_user import BanUser
from services.log import logger
from utils.message_builder import at
from utils.utils import BanCheckLimiter
malicious_check_time = Config.get_config("hook", "MALICIOUS_CHECK_TIME")
malicious_ban_count = Config.get_config("hook", "MALICIOUS_BAN_COUNT")
if not malicious_check_time:
raise ValueError("模块: [hook], 配置项: [MALICIOUS_CHECK_TIME] 为空或小于0")
if not malicious_ban_count:
raise ValueError("模块: [hook], 配置项: [MALICIOUS_BAN_COUNT] 为空或小于0")
_blmt = BanCheckLimiter(
malicious_check_time,
malicious_ban_count,
)
# 恶意触发命令检测
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
user_id = getattr(event, "user_id", None)
group_id = getattr(event, "group_id", None)
if not isinstance(event, MessageEvent):
return
malicious_ban_time = Config.get_config("hook", "MALICIOUS_BAN_TIME")
if not malicious_ban_time:
raise ValueError("模块: [hook], 配置项: [MALICIOUS_BAN_TIME] 为空或小于0")
if matcher.type == "message" and matcher.priority not in [1, 999]:
if state["_prefix"]["raw_command"]:
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
await BanUser.ban(
event.user_id,
9,
malicious_ban_time * 60,
)
logger.info(
f"触发了恶意触发检测: {matcher.plugin_name}", "HOOK", user_id, group_id
)
if isinstance(event, GroupMessageEvent):
try:
await bot.send_group_msg(
group_id=event.group_id,
message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟",
)
except ActionFailed:
pass
else:
try:
await bot.send_private_msg(
user_id=event.user_id,
message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟",
)
except ActionFailed:
pass
logger.debug(
f"触发了恶意触发检测: {matcher.plugin_name}", "HOOK", user_id, group_id
)
raise IgnoredException("检测到恶意触发命令")
_blmt.add(f'{event.user_id}{state["_prefix"]["raw_command"]}')

View File

@ -1,43 +0,0 @@
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State
from ._utils import status_message_manager
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
PrivateMessageEvent,
GroupMessageEvent,
)
# 为什么AI会自己和自己聊天
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State):
if not isinstance(event, MessageEvent):
return
if event.user_id == int(bot.self_id):
raise IgnoredException("为什么AI会自己和自己聊天")
# 有命令就别说话了
@run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not isinstance(event, MessageEvent):
return
if matcher.type == "message" and matcher.plugin_name == "ai":
if (
isinstance(event, GroupMessageEvent)
and not status_message_manager.check(event.group_id)
):
status_message_manager.delete(event.group_id)
raise IgnoredException("有命令就别说话了")
elif (
isinstance(event, PrivateMessageEvent)
and not status_message_manager.check(event.user_id)
):
status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了")

View File

@ -1,46 +0,0 @@
import re
from typing import Any, Dict
from nonebot.adapters.onebot.v11 import Bot, Message, unescape
from nonebot.exception import MockApiException
from services.log import logger
from utils.manager import group_manager
@Bot.on_calling_api
async def _(bot: Bot, api: str, data: Dict[str, Any]):
r = None
task = None
group_id = None
try:
if (
api == "send_msg" and data.get("message_type") == "group"
) or api == "send_group_msg":
msg = unescape(
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip()
)
if r := re.search(
"^\[\[_task\|(.*)]]",
msg,
):
if r.group(1) in group_manager.get_task_data().keys():
task = r.group(1)
group_id = data["group_id"]
except Exception as e:
logger.error(f"TaskHook ERROR", "HOOK", e=e)
else:
if task and group_id:
if group_manager.get_group_level(
group_id
) < 0 or not group_manager.check_task_status(task, group_id):
logger.debug(f"被动技能 {task} 处于关闭状态")
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
else:
msg = str(data["message"]).strip()
msg = msg.replace(f"&#91;&#91;_task|{task}&#93;&#93;", "").replace(
f"[[_task|{task}]]", ""
)
data["message"] = Message(msg)

View File

@ -1,32 +0,0 @@
import asyncio
from typing import Optional
from nonebot.adapters.onebot.v11 import Bot, Event
from nonebot.matcher import Matcher
from nonebot.message import run_postprocessor
from nonebot.typing import T_State
from services.log import logger
from utils.manager import withdraw_message_manager
# 消息撤回
@run_postprocessor
async def _(
matcher: Matcher,
exception: Optional[Exception],
bot: Bot,
event: Event,
state: T_State,
):
tasks = []
for id_, time in withdraw_message_manager.data:
tasks.append(asyncio.ensure_future(_withdraw_message(bot, id_, time)))
withdraw_message_manager.remove((id_, time))
await asyncio.gather(*tasks)
async def _withdraw_message(bot: Bot, id_: int, time: int):
await asyncio.sleep(time)
logger.debug(f"撤回消息ID: {id_}", "HOOK")
await bot.delete_msg(message_id=id_)

View File

@ -1,55 +0,0 @@
import nonebot
from nonebot import Driver
from nonebot.adapters.onebot.v11 import Bot
from configs.path_config import DATA_PATH
from services.log import logger
from .check_plugin_status import check_plugin_status
from .init import init
from .init_none_plugin_count_manager import init_none_plugin_count_manager
from .init_plugin_info import init_plugin_info
from .init_plugins_config import init_plugins_config
from .init_plugins_data import init_plugins_data, plugins_manager
from .init_plugins_limit import (
init_plugins_block_limit,
init_plugins_cd_limit,
init_plugins_count_limit,
)
from .init_plugins_resources import init_plugins_resources
from .init_plugins_settings import init_plugins_settings
__zx_plugin_name__ = "初始化插件数据 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def _():
"""
初始化数据
"""
config_file = DATA_PATH / "configs" / "plugins2config.yaml"
_flag = not config_file.exists()
init()
init_plugin_info()
init_plugins_settings()
init_plugins_cd_limit()
init_plugins_block_limit()
init_plugins_count_limit()
init_plugins_data()
init_plugins_config()
init_plugins_resources()
init_none_plugin_count_manager()
if _flag:
raise Exception("首次运行已在configs目录下生成配置文件config.yaml修改后重启即可...")
logger.info("初始化数据完成...")
@driver.on_bot_connect
async def _(bot: Bot):
# await init_group_manager()
await check_plugin_status(bot)

View File

@ -1,18 +0,0 @@
from utils.manager import plugins_manager
from nonebot.adapters.onebot.v11 import Bot
async def check_plugin_status(bot: Bot):
"""
遍历查看插件加载情况
"""
msg = ""
for plugin in plugins_manager.keys():
data = plugins_manager.get(plugin)
if data.error:
msg += f'{plugin}:{data.plugin_name}\n'
if msg and bot.config.superusers:
msg = "以下插件加载失败..\n" + msg
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=msg.strip()
)

View File

@ -1,14 +0,0 @@
from utils.manager import plugins2settings_manager
def init():
if plugins2settings_manager.get("update_pic"):
plugins2settings_manager["update_picture"] = plugins2settings_manager["update_pic"]
plugins2settings_manager.delete("update_pic")
if plugins2settings_manager.get("white2black_img"):
plugins2settings_manager["white2black_image"] = plugins2settings_manager["white2black_img"]
plugins2settings_manager.delete("white2black_img")
if plugins2settings_manager.get("send_img"):
plugins2settings_manager["send_image"] = plugins2settings_manager["send_img"]
plugins2settings_manager.delete("send_img")

View File

@ -1,62 +0,0 @@
from utils.manager import (
none_plugin_count_manager,
plugins2count_manager,
plugins2cd_manager,
plugins2settings_manager,
plugins2block_manager,
plugins_manager,
)
from services.log import logger
from utils.utils import get_matchers
try:
import ujson as json
except ModuleNotFoundError:
import json
def init_none_plugin_count_manager():
"""
清除已删除插件数据
"""
modules = [x.plugin_name for x in get_matchers(True)]
plugins_manager_list = list(plugins_manager.keys())
for module in plugins_manager_list:
try:
if module not in modules or none_plugin_count_manager.check(module):
try:
plugin_name = plugins_manager.get(module).plugin_name
except (AttributeError, KeyError):
plugin_name = ""
if none_plugin_count_manager.check(module):
try:
plugins2settings_manager.delete(module)
plugins2count_manager.delete(module)
plugins2cd_manager.delete(module)
plugins2block_manager.delete(module)
plugins_manager.delete(module)
plugins_manager.save()
# resources_manager.remove_resource(module)
none_plugin_count_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.exception(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}{e}"
)
else:
none_plugin_count_manager.add_count(module)
logger.info(
f"{module}:{plugin_name} 插件疑似已删除,"
f"加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,"
f"当前次数:{none_plugin_count_manager.get(module)}"
)
else:
none_plugin_count_manager.reset(module)
except Exception as e:
logger.error(f"清除插件数据错误 {type(e)}{e}")
plugins2settings_manager.save()
plugins2count_manager.save()
plugins2cd_manager.save()
plugins2block_manager.save()
plugins_manager.save()
none_plugin_count_manager.save()

View File

@ -1,149 +0,0 @@
import random
from types import ModuleType
from typing import Any, Dict
from configs.config import Config
from services import logger
from utils.manager import (
plugin_data_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2count_manager,
plugins2settings_manager,
plugins_manager,
)
from utils.manager.models import (
Plugin,
PluginBlock,
PluginCd,
PluginCount,
PluginData,
PluginSetting,
PluginType,
)
from utils.utils import get_matchers
def get_attr(module: ModuleType, name: str, default: Any = None) -> Any:
"""
说明:
获取属性
参数:
:param module: module
:param name: name
:param default: default
"""
return getattr(module, name, None) or default
def init_plugin_info():
for matcher in [x for x in get_matchers(True)]:
try:
if (plugin := matcher.plugin) and matcher.plugin_name:
metadata = plugin.metadata
extra = metadata.extra if metadata else {}
if hasattr(plugin, "module"):
module = plugin.module
plugin_model = matcher.plugin_name
plugin_name = (
metadata.name
if metadata and metadata.name
else get_attr(module, "__zx_plugin_name__", matcher.plugin_name)
)
if not plugin_name:
logger.warning(f"配置文件 模块:{plugin_model} 获取 plugin_name 失败...")
continue
if "[Admin]" in plugin_name:
plugin_type = PluginType.ADMIN
plugin_name = plugin_name.replace("[Admin]", "").strip()
elif "[Hidden]" in plugin_name:
plugin_type = PluginType.HIDDEN
plugin_name = plugin_name.replace("[Hidden]", "").strip()
elif "[Superuser]" in plugin_name:
plugin_type = PluginType.SUPERUSER
plugin_name = plugin_name.replace("[Superuser]", "").strip()
else:
plugin_type = PluginType.NORMAL
plugin_usage = (
metadata.usage
if metadata and metadata.usage
else get_attr(module, "__plugin_usage__")
)
plugin_des = (
metadata.description
if metadata and metadata.description
else get_attr(module, "__plugin_des__")
)
menu_type = get_attr(module, "__plugin_type__") or ("normal",)
plugin_setting = get_attr(module, "__plugin_settings__")
if plugin_setting:
plugin_setting = PluginSetting(**plugin_setting)
plugin_setting.plugin_type = menu_type
plugin_superuser_usage = get_attr(
module, "__plugin_superuser_usage__"
)
plugin_task = get_attr(module, "__plugin_task__")
plugin_version = extra.get("__plugin_version__") or get_attr(
module, "__plugin_version__"
)
plugin_author = extra.get("__plugin_author__") or get_attr(
module, "__plugin_author__"
)
plugin_cd = get_attr(module, "__plugin_cd_limit__")
if plugin_cd:
plugin_cd = PluginCd(**plugin_cd)
plugin_block = get_attr(module, "__plugin_block_limit__")
if plugin_block:
plugin_block = PluginBlock(**plugin_block)
plugin_count = get_attr(module, "__plugin_count_limit__")
if plugin_count:
plugin_count = PluginCount(**plugin_count)
plugin_resources = get_attr(module, "__plugin_resources__")
plugin_configs = get_attr(module, "__plugin_configs__")
if settings := plugins2settings_manager.get(plugin_model):
plugin_setting = settings
if plugin_cd_limit := plugins2cd_manager.get(plugin_model):
plugin_cd = plugin_cd_limit
if plugin_block_limit := plugins2block_manager.get(plugin_model):
plugin_block = plugin_block_limit
if plugin_count_limit := plugins2count_manager.get(plugin_model):
plugin_count = plugin_count_limit
if plugin_cfg := Config.get(plugin_model):
if plugin_configs:
for config_name in plugin_configs:
config: Dict[str, Any] = plugin_configs[config_name] # type: ignore
Config.add_plugin_config(
plugin_model,
config_name,
config.get("value"),
help_=config.get("help"),
default_value=config.get("default_value"),
type=config.get("type"),
)
plugin_configs = plugin_cfg.configs
plugin_status = plugins_manager.get(plugin_model)
if not plugin_status:
plugin_status = Plugin(plugin_name=plugin_model)
plugin_status.author = plugin_author
plugin_status.version = plugin_version
plugin_data = PluginData(
model=plugin_model,
name=plugin_name.strip(),
plugin_type=plugin_type,
usage=plugin_usage,
superuser_usage=plugin_superuser_usage,
des=plugin_des,
task=plugin_task,
menu_type=menu_type,
plugin_setting=plugin_setting,
plugin_cd=plugin_cd,
plugin_block=plugin_block,
plugin_count=plugin_count,
plugin_resources=plugin_resources,
plugin_configs=plugin_configs, # type: ignore
plugin_status=plugin_status,
)
plugin_data_manager.add_plugin_info(plugin_data)
except Exception as e:
logger.error(f"构造插件数据失败 {matcher.plugin_name}", e=e)

View File

@ -1,173 +0,0 @@
from pathlib import Path
from ruamel import yaml
from ruamel.yaml import YAML, round_trip_dump, round_trip_load
from configs.config import Config
from configs.path_config import DATA_PATH
from services.log import logger
from utils.manager import admin_manager, plugin_data_manager, plugins_manager
from utils.text_utils import prompt2cn
from utils.utils import get_matchers
_yaml = YAML(typ="safe")
def init_plugins_config():
"""
初始化插件数据配置
"""
plugins2config_file = DATA_PATH / "configs" / "plugins2config.yaml"
_data = Config.get_data()
# 优先使用 metadata 数据
for matcher in get_matchers(True):
if matcher.plugin_name:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
# 插件配置版本更新或为Version为None或不在存储配置内当使用metadata时必定更新
version = plugin_data.plugin_status.version
config = _data.get(matcher.plugin_name)
plugin = plugins_manager.get(matcher.plugin_name)
if plugin_data.plugin_configs and (
isinstance(version, str)
or (
version is None
or (
config
and config.configs.keys()
!= plugin_data.plugin_configs.keys()
)
or version > int(plugin.version or 0)
or matcher.plugin_name not in _data.keys()
)
):
plugin_configs = plugin_data.plugin_configs
for key in plugin_configs:
if isinstance(plugin_data.plugin_configs[key], dict):
Config.add_plugin_config(
matcher.plugin_name,
key,
plugin_configs[key].get("value"),
help_=plugin_configs[key].get("help"),
default_value=plugin_configs[key].get("default_value"),
_override=True,
type=plugin_configs[key].get("type"),
)
else:
config = plugin_configs[key]
Config.add_plugin_config(
matcher.plugin_name,
key,
config.value,
name=config.name,
help_=config.help,
default_value=config.default_value,
_override=True,
type=config.type,
)
elif plugin_configs := _data.get(matcher.plugin_name):
for key in plugin_configs.configs:
Config.add_plugin_config(
matcher.plugin_name,
key,
plugin_configs.configs[key].value,
help_=plugin_configs.configs[key].help,
default_value=plugin_configs.configs[key].default_value,
_override=True,
type=plugin_configs.configs[key].type,
)
if not Config.is_empty():
Config.save()
_data = round_trip_load(open(plugins2config_file, encoding="utf8"))
for plugin in _data.keys():
try:
plugin_name = plugins_manager.get(plugin).plugin_name
except (AttributeError, TypeError):
plugin_name = plugin
_data[plugin].yaml_set_start_comment(plugin_name, indent=2)
# 初始化未设置的管理员权限等级
for k, v in Config.get_admin_level_data():
admin_manager.set_admin_level(k, v)
# 存完插件基本设置
with open(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump(
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
)
_replace_config()
def _replace_config():
"""
说明:
定时任务加载的配置读取替换
"""
# 再开始读取用户配置
user_config_file = Path() / "configs" / "config.yaml"
_data = {}
_tmp_data = {}
if user_config_file.exists():
with open(user_config_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
# 数据替换
for plugin in Config.keys():
_tmp_data[plugin] = {}
for k in Config[plugin].configs.keys():
try:
if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k):
try:
admin_manager.set_admin_level(
level2module, _data[plugin][k]
)
except KeyError:
logger.warning(
f"{level2module} 设置权限等级失败:{_data[plugin][k]}"
)
_tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e:
raise AttributeError(
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46)
)
Config.save()
temp_file = Path() / "configs" / "temp_config.yaml"
try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
with open(temp_file, "r", encoding="utf8") as rf:
_data = round_trip_load(rf)
# 添加注释
for plugin in _data.keys():
rst = ""
plugin_name = None
try:
if config_group := Config.get(plugin):
for key in list(config_group.configs.keys()):
try:
if config := config_group.configs[key]:
if config.name:
plugin_name = config.name
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None
if not plugin_name:
try:
plugin_name = plugins_manager.get(plugin).plugin_name
except (AttributeError, TypeError):
plugin_name = plugin
plugin_name = (
plugin_name.replace("[Hidden]", "")
.replace("[Superuser]", "")
.replace("[Admin]", "")
.strip()
)
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f"{k}: {Config[plugin].configs[k].help}" + "\n"
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2)
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf:
round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
except Exception as e:
logger.error(f"生成简易配置注释错误 {type(e)}{e}")
if temp_file.exists():
temp_file.unlink()

View File

@ -1,57 +0,0 @@
from ruamel.yaml import YAML
from utils.manager import plugins_manager, plugin_data_manager
from utils.utils import get_matchers
from services.log import logger
try:
import ujson as json
except ModuleNotFoundError:
import json
_yaml = YAML(typ="safe")
def init_plugins_data():
"""
初始化插件数据信息
"""
for matcher in get_matchers(True):
_plugin = matcher.plugin
if not _plugin:
continue
try:
_module = _plugin.module
except AttributeError:
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.plugin_name, matcher.plugin_name, error=True
)
else:
plugins_manager[matcher.plugin_name].error = True
else:
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
try:
plugin_version = plugin_data.plugin_status.version
plugin_name = plugin_data.name
plugin_author = plugin_data.plugin_status.author
if matcher.plugin_name in plugins_manager.keys():
plugins_manager[matcher.plugin_name].error = False
if matcher.plugin_name not in plugins_manager.keys():
plugins_manager.add_plugin_data(
matcher.plugin_name,
plugin_name=plugin_name,
author=plugin_author,
version=plugin_version,
)
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name].version is None or (
plugin_version is not None
and plugin_version > float(plugins_manager[matcher.plugin_name].version)
):
plugins_manager[matcher.plugin_name].plugin_name = plugin_name
plugins_manager[matcher.plugin_name].author = plugin_author
plugins_manager[matcher.plugin_name].version = plugin_version
except Exception as e:
logger.error(f"插件数据 {matcher.plugin_name} 加载发生错误 {type(e)}{e}")
plugins_manager.save()

View File

@ -1,64 +0,0 @@
from utils.manager import (
plugins2cd_manager,
plugins2block_manager,
plugins2count_manager,
plugin_data_manager,
)
from utils.utils import get_matchers
from configs.path_config import DATA_PATH
def init_plugins_cd_limit():
"""
加载 cd 限制
"""
plugins2cd_file = DATA_PATH / "configs" / "plugins2cd.yaml"
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
for matcher in get_matchers(True):
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name) and (
plugin_data := plugin_data_manager.get(matcher.plugin_name)
):
if plugin_data.plugin_cd:
plugins2cd_manager.add_cd_limit(
matcher.plugin_name, plugin_data.plugin_cd
)
if not plugins2cd_manager.keys():
plugins2cd_manager.add_cd_limit("这是一个示例")
plugins2cd_manager.save()
plugins2cd_manager.reload_cd_limit()
def init_plugins_block_limit():
"""
加载阻塞限制
"""
for matcher in get_matchers(True):
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name) and (
plugin_data := plugin_data_manager.get(matcher.plugin_name)
):
if plugin_data.plugin_block:
plugins2block_manager.add_block_limit(
matcher.plugin_name, plugin_data.plugin_block
)
if not plugins2block_manager.keys():
plugins2block_manager.add_block_limit("这是一个示例")
plugins2block_manager.save()
plugins2block_manager.reload_block_limit()
def init_plugins_count_limit():
"""
加载次数限制
"""
for matcher in get_matchers(True):
if not plugins2count_manager.get_plugin_count_data(matcher.plugin_name) and (
plugin_data := plugin_data_manager.get(matcher.plugin_name)
):
if plugin_data.plugin_count:
plugins2count_manager.add_count_limit(
matcher.plugin_name, plugin_data.plugin_count
)
if not plugins2count_manager.keys():
plugins2count_manager.add_count_limit("这是一个示例")
plugins2count_manager.save()
plugins2count_manager.reload_count_limit()

View File

@ -1,25 +0,0 @@
from utils.manager import resources_manager, plugin_data_manager
from utils.utils import get_matchers
from services.log import logger
from pathlib import Path
def init_plugins_resources():
"""
资源文件路径的移动
"""
for matcher in get_matchers(True):
if plugin_data := plugin_data_manager.get(matcher.plugin_name):
try:
_module = matcher.plugin.module
except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,资源控制未加载...")
else:
if resources := plugin_data.plugin_resources:
path = Path(_module.__getattribute__("__file__")).parent
for resource in resources.keys():
resources_manager.add_resource(
matcher.plugin_name, path / resource, resources[resource]
)
resources_manager.save()
resources_manager.start_move()

View File

@ -1,56 +0,0 @@
import nonebot
from services.log import logger
from utils.manager import admin_manager, plugin_data_manager, plugins2settings_manager
from utils.manager.models import PluginType
from utils.utils import get_matchers
def init_plugins_settings():
"""
初始化插件设置从插件中获取 __zx_plugin_name____plugin_cmd____plugin_settings__
"""
# for x in plugins2settings_manager.keys():
# try:
# _plugin = nonebot.plugin.get_plugin(x)
# _module = _plugin.module
# _module.__getattribute__("__zx_plugin_name__")
# except (KeyError, AttributeError) as e:
# logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
for matcher in get_matchers(True):
try:
if (
matcher.plugin_name
and matcher.plugin_name not in plugins2settings_manager.keys()
):
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()
logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.")

View File

@ -1,170 +0,0 @@
import asyncio
import re
import time
from datetime import datetime
from nonebot import on_message, on_request
from nonebot.adapters.onebot.v11 import (
ActionFailed,
Bot,
FriendRequestEvent,
GroupRequestEvent,
MessageEvent,
)
from configs.config import NICKNAME, Config
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
from utils.manager import requests_manager
from utils.utils import scheduler
from .utils import time_manager
__zx_plugin_name__ = "好友群聊处理请求 [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_ADD_FRIEND": {
"value": False,
"help": "是否自动同意好友添加",
"default_value": False,
"type": bool,
}
}
friend_req = on_request(priority=5, block=True)
group_req = on_request(priority=5, block=True)
x = on_message(priority=999, block=False, rule=lambda: False)
@friend_req.handle()
async def _(bot: Bot, event: FriendRequestEvent):
if time_manager.add_user_request(event.user_id):
logger.debug(f"收录好友请求...", "好友请求", target=event.user_id)
user = await bot.get_stranger_info(user_id=event.user_id)
nickname = user["nickname"]
sex = user["sex"]
age = str(user["age"])
comment = event.comment
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份好友申请*****\n"
f"昵称:{nickname}({event.user_id})\n"
f"自动同意:{'' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
f"日期:{str(datetime.now()).split('.')[0]}\n"
f"备注:{event.comment}",
)
if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
logger.debug(f"已开启好友请求自动同意,成功通过该请求", "好友请求", target=event.user_id)
await bot.set_friend_add_request(flag=event.flag, approve=True)
await FriendUser.create(
user_id=str(user["user_id"]), user_name=user["nickname"]
)
else:
requests_manager.add_request(
str(bot.self_id),
event.user_id,
"private",
event.flag,
nickname=nickname,
sex=sex,
age=age,
comment=comment,
)
else:
logger.debug(f"好友请求五分钟内重复, 已忽略", "好友请求", target=event.user_id)
@group_req.handle()
async def _(bot: Bot, event: GroupRequestEvent):
# 邀请
if event.sub_type == "invite":
if str(event.user_id) in bot.config.superusers:
try:
logger.debug(
f"超级用户自动同意加入群聊", "群聊请求", event.user_id, target=event.group_id
)
await bot.set_group_add_request(
flag=event.flag, sub_type="invite", approve=True
)
group_info = await bot.get_group_info(group_id=event.group_id)
await GroupInfo.update_or_create(
group_id=str(group_info["group_id"]),
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
"group_flag": 1,
},
)
except ActionFailed as e:
logger.error(
"超级用户自动同意加入群聊发生错误",
"群聊请求",
event.user_id,
target=event.group_id,
e=e,
)
else:
if time_manager.add_group_request(event.user_id, event.group_id):
logger.debug(
f"收录 用户[{event.user_id}] 群聊[{event.group_id}] 群聊请求", "群聊请求"
)
user = await bot.get_stranger_info(user_id=event.user_id)
sex = user["sex"]
age = str(user["age"])
nickname = await FriendUser.get_user_name(event.user_id)
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份入群申请*****\n"
f"申请人:{nickname}({event.user_id})\n"
f"群聊:{event.group_id}\n"
f"邀请日期:{datetime.now().replace(microsecond=0)}",
)
await bot.send_private_msg(
user_id=event.user_id,
message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n"
"请确保已经群主或群管理沟通过!\n"
"等待管理员处理吧!",
)
requests_manager.add_request(
str(bot.self_id),
event.user_id,
"group",
event.flag,
invite_group=event.group_id,
nickname=nickname,
sex=sex,
age=age,
)
else:
logger.debug(
f"群聊请求五分钟内重复, 已忽略",
"群聊请求",
target=f"{event.user_id}:{event.group_id}",
)
@x.handle()
async def _(event: MessageEvent):
await asyncio.sleep(0.1)
r = re.search(r'groupcode="(.*?)"', str(event.get_message()))
if r:
group_id = int(r.group(1))
else:
return
r = re.search(r'groupname="(.*?)"', str(event.get_message()))
if r:
group_name = r.group(1)
else:
group_name = "None"
requests_manager.set_group_name(group_name, group_id)
@scheduler.scheduled_job(
"interval",
minutes=5,
)
async def _():
time_manager.clear()

View File

@ -1,87 +0,0 @@
import time
from dataclasses import dataclass
from typing import Dict
@dataclass
class PrivateRequest:
"""
好友请求
"""
user_id: int
time: float = time.time()
@dataclass
class GroupRequest:
"""
群聊请求
"""
user_id: int
group_id: int
time: float = time.time()
class RequestTimeManage:
"""
过滤五分钟以内的重复请求
"""
def __init__(self):
self._group: Dict[str, GroupRequest] = {}
self._user: Dict[int, PrivateRequest] = {}
def add_user_request(self, user_id: int) -> bool:
"""
添加请求时间
Args:
user_id (int): 用户id
Returns:
bool: 是否满足时间
"""
if user := self._user.get(user_id):
if time.time() - user.time < 60 * 5:
return False
self._user[user_id] = PrivateRequest(user_id)
return True
def add_group_request(self, user_id: int, group_id: int) -> bool:
"""
添加请求时间
Args:
user_id (int): 用户id
group_id (int): 邀请群聊
Returns:
bool: 是否满足时间
"""
key = f"{user_id}:{group_id}"
if group := self._group.get(key):
if time.time() - group.time < 60 * 5:
return False
self._group[key] = GroupRequest(user_id=user_id, group_id=group_id)
return True
def clear(self):
"""
清理过期五分钟请求
"""
now = time.time()
for user_id in self._user:
if now - self._user[user_id].time < 60 * 5:
del self._user[user_id]
for key in self._group:
if now - self._group[key].time < 60 * 5:
del self._group[key]
time_manager = RequestTimeManage()

View File

@ -1,207 +0,0 @@
import random
from typing import Any, List, Tuple
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.internal.matcher import Matcher
from nonebot.internal.params import Depends
from nonebot.params import CommandArg, RegexGroup
from nonebot.rule import to_me
from configs.config import NICKNAME
from models.ban_user import BanUser
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.depends import GetConfig
__zx_plugin_name__ = "昵称系统"
__plugin_usage__ = f"""
usage
个人昵称将替换真寻称呼你的名称群聊 私聊 昵称相互独立全局昵称设置将更改您目前所有群聊中及私聊的昵称
指令
以后叫我 [昵称]: 设置当前群聊/私聊的昵称
全局昵称设置 [昵称]: 设置当前所有群聊和私聊的昵称
{NICKNAME}我是谁
""".strip()
__plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁", "全局昵称设置 [昵称]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["昵称"],
}
__plugin_configs__ = {
"BLACK_WORD": {
"value": ["", "", "", ""],
"help": "昵称所屏蔽的关键词,已设置的昵称会被替换为 *,未设置的昵称会在设置时提示",
"default_value": None,
"type": List[str],
}
}
nickname = on_regex(
"(?:以后)?(?:叫我|请叫我|称呼我)(.*)",
rule=to_me(),
priority=5,
block=True,
)
my_nickname = on_command(
"我叫什么", aliases={"我是谁", "我的名字"}, rule=to_me(), priority=5, block=True
)
global_nickname = on_regex("设置全局昵称(.*)", rule=to_me(), priority=5, block=True)
cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True)
def CheckNickname():
"""
说明:
检查名称是否合法
"""
async def dependency(
bot: Bot,
matcher: Matcher,
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
black_word: Any = GetConfig(config="BLACK_WORD"),
):
(msg,) = reg_group
logger.debug(f"昵称检查: {msg}", "昵称设置", event.user_id)
if not msg:
await matcher.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if str(event.user_id) in bot.config.superusers:
logger.debug(f"超级用户设置昵称, 跳过合法检测: {msg}", "昵称设置", event.user_id)
return
if len(msg) > 20:
await nickname.finish("昵称可不能超过20个字", at_sender=True)
if msg in bot.config.nickname:
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
if black_word:
for x in msg:
if x in black_word:
logger.debug("昵称设置禁止字符: [{x}]", "昵称设置", event.user_id)
await nickname.finish(f"字符 [{x}] 为禁止字符!", at_sender=True)
for word in black_word:
if word in msg:
logger.debug("昵称设置禁止字符: [{word}]", "昵称设置", event.user_id)
await nickname.finish(f"字符 [{word}] 为禁止字符!", at_sender=True)
return Depends(dependency)
@global_nickname.handle(parameterless=[CheckNickname()])
async def _(
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
):
(msg,) = reg_group
await FriendUser.set_user_nickname(event.user_id, msg)
await GroupInfoUser.filter(user_id=str(event.user_id)).update(nickname=msg)
logger.info(f"设置全局昵称成功: {msg}", "设置全局昵称", event.user_id)
await global_nickname.send(f"设置全局昵称成功!亲爱的{msg}")
@nickname.handle(parameterless=[CheckNickname()])
async def _(
event: MessageEvent,
reg_group: Tuple[Any, ...] = RegexGroup(),
):
(msg,) = reg_group
if isinstance(event, GroupMessageEvent):
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, msg)
if len(msg) < 5:
if random.random() < 0.3:
msg = "~".join(msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
)
logger.info(f"设置群昵称成功: {msg}", "昵称设置", event.user_id, event.group_id)
else:
await FriendUser.set_user_nickname(event.user_id, msg)
await nickname.send(
random.choice(
[
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.",
]
)
)
logger.info(f"设置私聊昵称成功: {msg}", "昵称设置", event.user_id)
@my_nickname.handle()
async def _(event: MessageEvent):
nickname_ = None
card = None
if isinstance(event, GroupMessageEvent):
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
card = event.sender.card or event.sender.nickname
else:
nickname_ = await FriendUser.get_user_nickname(event.user_id)
card = event.sender.nickname
if nickname_:
await my_nickname.send(
random.choice(
[
f"我肯定记得你啊,你是{nickname_}",
f"我不会忘记你的,你也不要忘记我!{nickname_}",
f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}",
f"嗯?你是失忆了嘛...{nickname_}..",
f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}QAQ",
f"哎?{nickname_}..怎么了吗..突然这样问..",
]
)
)
else:
await my_nickname.send(
random.choice(
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "{}啊,有什么事吗?", "你是{}"]
).format(card)
)
@cancel_nickname.handle()
async def _(event: MessageEvent):
nickname_ = None
if isinstance(event, GroupMessageEvent):
nickname_ = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
else:
nickname_ = await FriendUser.get_user_nickname(event.user_id)
if nickname_:
await cancel_nickname.send(
random.choice(
[
f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}",
f"窝知道了..{nickname_}..",
f"{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}",
f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!",
f"可..可恶!{nickname_}!太可恶了!呜",
]
)
)
if isinstance(event, GroupMessageEvent):
await GroupInfoUser.set_user_nickname(event.user_id, event.group_id, "")
else:
await FriendUser.set_user_nickname(event.user_id, "")
await BanUser.ban(event.user_id, 9, 60)
else:
await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True)

View File

@ -1,75 +0,0 @@
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from services.log import logger
from utils.message_builder import image
from .data_source import (
download_json,
install_plugin,
show_plugin_repo,
uninstall_plugin,
)
__zx_plugin_name__ = "插件商店 [Superuser]"
__plugin_usage__ = """
usage
下载安装插件
指令
查看插件仓库
更新插件仓库
安装插件 [name/id] 重新安装等同于更新
卸载插件 [name/id]
""".strip()
__plugin_des__ = "从真寻适配仓库中下载插件"
__plugin_cmd__ = ["查看插件仓库", "更新插件仓库", "安装插件 [name/id]", "卸载插件 [name/id]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
show_repo = on_regex("^查看插件仓库$", priority=1, block=True, permission=SUPERUSER)
update_repo = on_regex("^更新插件仓库$", priority=1, block=True, permission=SUPERUSER)
install_plugin_matcher = on_command(
"安装插件", priority=1, block=True, permission=SUPERUSER
)
uninstall_plugin_matcher = on_command(
"卸载插件", priority=1, block=True, permission=SUPERUSER
)
@install_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
name = arg.extract_plain_text().strip()
msg = await install_plugin(name)
await install_plugin_matcher.send(msg)
logger.info(f"安装插件: {name}", "安装插件", event.user_id)
@uninstall_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
name = arg.extract_plain_text().strip()
msg = await uninstall_plugin(name)
await install_plugin_matcher.send(msg)
logger.info(f"卸载插件: {name}", "卸载插件", event.user_id)
@update_repo.handle()
async def _(bot: Bot, event: MessageEvent):
code = await download_json()
if code == 200:
await update_repo.finish("更新插件仓库信息成功!")
await update_repo.send("更新插件仓库信息失败!")
logger.info("更新插件仓库信息", "更新插件仓库信息", event.user_id)
@show_repo.handle()
async def _(bot: Bot, event: MessageEvent):
msg = await show_plugin_repo()
if isinstance(msg, int):
await show_repo.finish("文件下载失败或解压失败..")
await show_repo.send(image(msg))
logger.info("查看插件仓库", "查看插件仓库", event.user_id)

View File

@ -1,200 +0,0 @@
import os
import shutil
import zipfile
from pathlib import Path
from typing import Tuple, Union
import ujson as json
from configs.path_config import DATA_PATH, TEMP_PATH
from services import logger
from utils.http_utils import AsyncHttpx
from utils.image_utils import BuildImage, text2image
from utils.manager import plugins_manager
from utils.utils import is_number
path = DATA_PATH / "plugin_shop"
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
repo_json_url = "https://github.com/zhenxun-org/nonebot_plugins_zhenxun_bot/archive/refs/heads/index.zip"
repo_zip_path = TEMP_PATH / "plugin_repo_json.zip"
plugin_json = path / "zhenxun_plugins.json"
extensive_plugin_path = Path() / "extensive_plugin"
path = DATA_PATH / "plugin_shop"
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
if not extensive_plugin_path.exists():
extensive_plugin_path.mkdir(parents=True, exist_ok=True)
data = {}
async def install_plugin(name: str) -> str:
"""
安装插件
:param name: 插件名或下标
"""
try:
if is_number(name):
name, code = await get_plugin_name(int(name))
if code != 200:
return name
plugin_url = data[name]["download_url"]
url = (await AsyncHttpx.get(plugin_url)).headers.get("Location")
zip_file = TEMP_PATH / f"{name}.zip"
if zip_file.exists():
zip_file.unlink()
if await AsyncHttpx.download_file(url, zip_file):
logger.debug("开始解压插件压缩包...", "安装插件", target=name)
# 解压
zf = zipfile.ZipFile(zip_file, "r")
extract_path = TEMP_PATH / f"{name}"
if extract_path.exists():
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
extract_path.mkdir(exist_ok=True, parents=True)
for file in zf.namelist():
zf.extract(file, extract_path)
zf.close()
logger.debug("解压插件压缩包完成...", "安装插件", target=name)
logger.debug("开始移动插件文件夹...", "安装插件", target=name)
if (extensive_plugin_path / f"{name}").exists():
logger.debug(
"extensive_plugin目录下文件夹已存在删除该目录插件文件夹...", "安装插件", target=name
)
shutil.rmtree(
(extensive_plugin_path / f"{name}").absolute(), ignore_errors=True
)
extract_path.rename(extensive_plugin_path / f"{name}")
prompt = ""
if "pyproject.toml" in os.listdir(extensive_plugin_path / f"{name}"):
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
os.system(
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'pyproject.toml').absolute()}"
)
elif "requirements.txt" in os.listdir(extensive_plugin_path / f"{name}"):
prompt = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
os.system(
f"poetry run pip install -r {(extensive_plugin_path / f'{name}' / 'requirements.txt').absolute()}"
)
with open(extensive_plugin_path / f"{name}" / "plugin_info.json", "w") as f:
json.dump(data[name], f, ensure_ascii=False, indent=4)
logger.debug("移动插件文件夹完成...", "安装插件", target=name)
logger.info(f"成功安装插件 {name} 成功!\n{prompt}", "安装插件", target=name)
return f"成功安装插件 {name},请重启真寻!"
except Exception as e:
logger.error(f"安装插失败", "安装插件", target=name, e=e)
return f"安装插件 {name} 失败 {type(e)}{e}"
async def uninstall_plugin(name: str) -> str:
"""
删除插件
:param name: 插件名或下标
"""
try:
if is_number(name):
name, code = await get_plugin_name(int(name))
if code != 200:
return name
if name not in os.listdir(extensive_plugin_path):
return f"未安装 {name} 插件!"
shutil.rmtree((extensive_plugin_path / name).absolute(), ignore_errors=True)
logger.info(f"插件 {name} 删除成功!")
return f"插件 {name} 删除成功!"
except Exception as e:
logger.error(f"删除插件失败", target=name, e=e)
return f"删除插件 {name} 失败 {type(e)}{e}"
async def show_plugin_repo() -> Union[int, str]:
"""
获取插件仓库数据并格式化
"""
if not plugin_json.exists():
code = await download_json()
if code != 200:
return code
plugin_info = json.load(open(plugin_json, "r", encoding="utf8"))
plugins_data = plugins_manager.get_data()
load_plugin_list = plugins_data.keys()
image_list = []
w, h = 0, 0
line_height = 10
for i, key in enumerate(plugin_info.keys()):
data[key] = {
"名称": plugin_info[key]["plugin_name"],
"模块": key,
"作者": plugin_info[key]["author"],
"版本": plugin_info[key]["version"],
"简介": plugin_info[key]["introduction"],
"download_url": plugin_info[key]["download_url"],
"github_url": plugin_info[key]["github_url"],
}
status = ""
version = ""
if key in load_plugin_list:
status = "<f font_color=#1a7e30>[已安装]</f>"
version = f"<f font_color=#1a7e30>[{plugins_data[key].version}]</f>"
s = (
f'id{i+1}\n名称:{plugin_info[key]["plugin_name"]}'
f" \t\t{status}\n"
f"模块:{key}\n"
f'作者:{plugin_info[key]["author"]}\n'
f'版本:{plugin_info[key]["version"]} \t\t{version}\n'
f'简介:{plugin_info[key]["introduction"]}\n'
f"-------------------"
)
img = await text2image(s, font_size=20, color="#f9f6f2")
w = w if w > img.w else img.w
h += img.h + line_height
image_list.append(img)
A = BuildImage(w + 50, h + 50, color="#f9f6f2")
cur_h = 25
for img in image_list:
await A.apaste(img, (25, cur_h))
cur_h += img.h + line_height
return A.pic2bs4()
async def download_json() -> int:
"""
下载插件库json文件
"""
try:
url = (await AsyncHttpx.get(repo_json_url)).headers.get("Location")
if repo_zip_path.exists():
repo_zip_path.unlink()
if await AsyncHttpx.download_file(url, repo_zip_path):
zf = zipfile.ZipFile(repo_zip_path, "r")
extract_path = path / "temp"
for file in zf.namelist():
zf.extract(file, extract_path)
zf.close()
if plugin_json.exists():
plugin_json.unlink()
(
extract_path
/ "nonebot_plugins_zhenxun_bot-index"
/ "zhenxun_plugins.json"
).rename(plugin_json)
shutil.rmtree(extract_path.absolute(), ignore_errors=True)
return 200
except Exception as e:
logger.error(f"下载插件库压缩包失败或解压失败", e=e)
return 999
async def get_plugin_name(index: int) -> Tuple[str, int]:
"""
通过下标获取插件名
:param name: 下标
"""
if not data:
await show_plugin_repo()
if index < 1 or index > len(data.keys()):
return "下标超过上下限!", 999
name = list(data.keys())[index - 1]
return name, 200

View File

@ -1,108 +0,0 @@
import random
from asyncio.exceptions import TimeoutError
import nonebot
from nonebot.adapters.onebot.v11 import Bot
from nonebot.drivers import Driver
from configs.path_config import TEXT_PATH
from models.bag_user import BagUser
from models.group_info import GroupInfo
from services.log import logger
from utils.http_utils import AsyncHttpx
from utils.utils import GDict, scheduler
try:
import ujson as json
except ModuleNotFoundError:
import json
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def update_city():
"""
部分插件需要中国省份城市
这里直接更新避免插件内代码重复
"""
china_city = TEXT_PATH / "china_city.json"
data = {}
if not china_city.exists():
try:
logger.debug("开始更新城市列表...")
res = await AsyncHttpx.get(
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
)
res.encoding = "utf8"
provinces_data = json.loads(res.text)
for province in provinces_data.keys():
data[provinces_data[province]] = []
res = await AsyncHttpx.get(
f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html",
timeout=5,
)
res.encoding = "utf8"
city_data = json.loads(res.text)
for city in city_data.keys():
data[provinces_data[province]].append(city_data[city])
with open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....")
except TimeoutError as e:
logger.warning("自动更新城市列表超时...", e=e)
except ValueError as e:
logger.warning("自动城市列表失败.....", e=e)
except Exception as e:
logger.error(f"自动城市列表未知错误", e=e)
@driver.on_bot_connect
async def _(bot: Bot):
"""
版本某些需要的变换
"""
# 清空不存在的群聊信息并将已所有已存在的群聊group_flag设置为1认证所有已存在的群
if not await GroupInfo.get_or_none(group_id=114514):
# 标识符,该功能只需执行一次
await GroupInfo.create(
group_id=114514,
group_name="114514",
max_member_count=114514,
member_count=114514,
group_flag=1,
)
group_list = await bot.get_group_list()
group_list = [g["group_id"] for g in group_list]
_gl = [x.group_id for x in await GroupInfo.all()]
if 114514 in _gl:
_gl.remove(114514)
for group_id in _gl:
if group_id in group_list:
if group := await GroupInfo.get_or_none(group_id=group_id):
group.group_flag = 1
await group.save(update_fields=["group_flag"])
else:
group_info = await bot.get_group_info(group_id=group_id)
await GroupInfo.create(
group_id=group_info["group_id"],
group_name=group_info["group_name"],
max_member_count=group_info["max_member_count"],
member_count=group_info["member_count"],
group_flag=1,
)
logger.info(f"已添加群认证...", group_id=group_id)
else:
await GroupInfo.filter(group_id=group_id).delete()
logger.info(f"移除不存在的群聊信息", group_id=group_id)
# 自动更新城市列表
@scheduler.scheduled_job(
"cron",
hour=6,
minute=1,
)
async def _():
await update_city()

View File

@ -1,27 +0,0 @@
from pathlib import Path
import nonebot
from nonebot.drivers import Driver
from configs.config import Config
from utils.decorator.shop import shop_register
driver: Driver = nonebot.get_driver()
Config.add_plugin_config(
"shop",
"IMPORT_DEFAULT_SHOP_GOODS",
True,
help_="导入商店自带的三个商品",
default_value=True,
type=bool,
)
nonebot.load_plugins(str(Path(__file__).parent.resolve()))
@driver.on_bot_connect
async def _():
await shop_register.load_register()

View File

@ -1,109 +0,0 @@
import time
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg
from models.bag_user import BagUser
from models.goods_info import GoodsInfo
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.utils import is_number
__zx_plugin_name__ = "商店 - 购买道具"
__plugin_usage__ = """
usage
购买道具
指令
购买 [序号或名称] ?[数量=1]
示例购买 好感双倍加持卡Ⅰ
示例购买 1 4
""".strip()
__plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "购买道具"],
}
__plugin_cd_limit__ = {"cd": 3}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@buy.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
goods = None
if arg.extract_plain_text().strip() in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_list = [
x
for x in await GoodsInfo.get_all_goods()
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
]
goods_name_list = [x.goods_name for x in goods_list]
msg = arg.extract_plain_text().strip().split()
num = 1
if len(msg) > 1:
if is_number(msg[1]) and int(msg[1]) > 0:
num = int(msg[1])
else:
await buy.finish("购买的数量要是数字且大于0", at_sender=True)
if is_number(msg[0]):
msg = int(msg[0])
if msg > len(goods_name_list) or msg < 1:
await buy.finish("请输入正确的商品id", at_sender=True)
goods = goods_list[msg - 1]
else:
if msg[0] in goods_name_list:
for i in range(len(goods_name_list)):
if msg[0] == goods_name_list[i]:
goods = goods_list[i]
break
else:
await buy.finish("请输入正确的商品名称!")
else:
await buy.finish("请输入正确的商品名称!", at_sender=True)
if (
await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num * goods.goods_discount:
await buy.finish("您的金币好像不太够哦", at_sender=True)
flag, n = await GoodsInfo.check_user_daily_purchase(
goods, event.user_id, event.group_id, num
)
if flag:
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
spend_gold = int(goods.goods_discount * goods.goods_price * num)
await BagUser.spend_gold(event.user_id, event.group_id, spend_gold)
await BagUser.add_property(event.user_id, event.group_id, goods.goods_name, num)
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
await buy.send(
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True,
)
logger.info(
f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!",
"购买道具",
event.user_id,
event.group_id,
)
await UserShopGoldLog.create(
user_id=str(event.user_id),
group_id=str(event.group_id),
type=0,
name=goods.goods_name,
num=num,
spend_gold=goods.goods_price * num * goods.goods_discount,
)
# else:
# await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True)
# logger.info(
# f"USER {event.user_id} GROUP {event.group_id} "
# f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 失败!"
# )

View File

@ -1,62 +0,0 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import ActionFailed, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg
from models.bag_user import BagUser
from utils.data_utils import init_rank
from utils.image_utils import text2image
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "商店 - 我的金币"
__plugin_usage__ = """
usage
我的金币
指令
我的金币
""".strip()
__plugin_des__ = "商店 - 我的金币"
__plugin_cmd__ = ["我的金币"]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "我的金币"],
}
my_gold = on_command("我的金币", priority=5, block=True, permission=GROUP)
gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle()
async def _(event: GroupMessageEvent):
msg = await BagUser.get_user_total_gold(event.user_id, event.group_id)
try:
await my_gold.send(msg)
except ActionFailed:
await my_gold.send(
image(b64=(await text2image(msg, color="#f9f6f2", padding=10)).pic2bs4())
)
@gold_rank.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
num = arg.extract_plain_text().strip()
if is_number(num) and 51 > int(num) > 10:
num = int(num)
else:
num = 10
all_users = await BagUser.filter(group_id=event.group_id)
all_user_id = [user.user_id for user in all_users]
all_user_data = [user.gold for user in all_users]
rank_image = await init_rank(
"金币排行", all_user_id, all_user_data, event.group_id, num
)
if rank_image:
await gold_rank.finish(image(b64=rank_image.pic2bs4()))

View File

@ -1,41 +0,0 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.adapters.onebot.v11.permission import GROUP
from models.bag_user import BagUser
from services.log import logger
from utils.message_builder import image
from ._data_source import create_bag_image
__zx_plugin_name__ = "商店 - 我的道具"
__plugin_usage__ = """
usage
我的道具
指令
我的道具
""".strip()
__plugin_des__ = "商店 - 我的道具"
__plugin_cmd__ = ["我的道具"]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "我的道具"],
}
my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle()
async def _(event: GroupMessageEvent):
props = await BagUser.get_property(str(event.user_id), str(event.group_id))
if props:
await my_props.send(image(b64=await create_bag_image(props)))
logger.info(f"查看我的道具", "我的道具", event.user_id, event.group_id)
else:
await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True)

View File

@ -1,121 +0,0 @@
from typing import Dict, List, Optional
from models.bag_user import BagUser
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH
icon_path = IMAGE_PATH / "shop_icon"
async def create_bag_image(props: Dict[str, int]):
"""
说明:
创建背包道具图片
参数:
:param props: 道具仓库字典
"""
goods_list = await GoodsInfo.get_all_goods()
active_props = await _init_prop(props, [x for x in goods_list if not x.is_passive])
passive_props = await _init_prop(props, [x for x in goods_list if x.is_passive])
active_w = 0
active_h = 0
passive_w = 0
passive_h = 0
if active_props:
img = BuildImage(
active_props.w,
active_props.h + 70,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
await img.apaste(active_props, (0, 70))
await img.atext((0, 30), "主动道具")
active_props = img
active_w = img.w
active_h = img.h
if passive_props:
img = BuildImage(
passive_props.w,
passive_props.h + 70,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
await img.apaste(passive_props, (0, 70))
await img.atext((0, 30), "被动道具")
passive_props = img
passive_w = img.w
passive_h = img.h
A = BuildImage(
active_w + passive_w + 100,
max(active_h, passive_h) + 60,
font="CJGaoDeGuo.otf",
font_size=30,
color="#f9f6f2",
)
curr_w = 50
if active_props:
await A.apaste(active_props, (curr_w, 0))
curr_w += active_props.w + 10
if passive_props:
await A.apaste(passive_props, (curr_w, 0))
if active_props and passive_props:
await A.aline(
(active_props.w + 45, 70, active_props.w + 45, A.h - 20), fill=(0, 0, 0)
)
return A.pic2bs4()
async def _init_prop(
props: Dict[str, int], _props: List[GoodsInfo]
) -> Optional[BuildImage]:
"""
说明:
构造道具列表图片
参数:
:param props: 道具仓库字典
:param _props: 道具列表
"""
active_name = [x.goods_name for x in _props]
name_list = [x for x in props.keys() if x in active_name]
if not name_list:
return None
temp_img = BuildImage(0, 0, font_size=20)
image_list = []
num_list = []
for i, name in enumerate(name_list):
img = BuildImage(
temp_img.getsize(name)[0] + 50,
30,
font="msyh.ttf",
font_size=20,
color="#f9f6f2",
)
await img.atext((30, 5), f"{i + 1}.{name}")
goods = [x for x in _props if x.goods_name == name][0]
if goods.icon and (icon_path / goods.icon).exists():
icon = BuildImage(30, 30, background=icon_path / goods.icon)
await img.apaste(icon, alpha=True)
image_list.append(img)
num_list.append(
BuildImage(
30, 30, font_size=20, font="msyh.ttf", plain_text=f"×{props[name]}"
)
)
max_w = 0
num_max_w = 0
h = 0
for img, num in zip(image_list, num_list):
h += img.h
max_w = max_w if max_w > img.w else img.w
num_max_w = num_max_w if num_max_w > num.w else num.w
A = BuildImage(max_w + num_max_w + 30, h, color="#f9f6f2")
curr_h = 0
for img, num in zip(image_list, num_list):
await A.apaste(img, (0, curr_h))
await A.apaste(num, (max_w + 20, curr_h + 5), True)
curr_h += img.h
return A

View File

@ -1,162 +0,0 @@
import os
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from configs.path_config import IMAGE_PATH
from models.bag_user import BagUser
from services.log import logger
from utils.message_builder import image
from utils.utils import is_number, scheduler
from .data_source import (
GoodsInfo,
create_shop_help,
delete_goods,
parse_goods_info,
register_goods,
update_goods,
)
__zx_plugin_name__ = "商店"
__plugin_usage__ = """
usage
商店项目这可不是奸商
指令
商店
""".strip()
__plugin_superuser_usage__ = """
usage
商品操作
指令
添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时)
删除商品 [名称或序号]
修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时]
示例添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯但是里面.. discount:0.4 limit_time:90
示例添加商品 name:可疑的药 price:5 des:效果未知
示例删除商品 2
示例修改商品 name:1 price:900 修改序号为1的商品的价格为900
* 修改商品只需添加需要值即可 *
""".strip()
__plugin_des__ = "商店系统[金币回收计划]"
__plugin_cmd__ = [
"商店",
"添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时)) [_superuser]",
"删除商品 [名称或序号] [_superuser]",
"修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] [_superuser]",
]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店"],
}
__plugin_block_limit__ = {"limit_type": "group"}
shop_help = on_command("商店", priority=5, block=True)
shop_add_goods = on_command("添加商品", priority=5, permission=SUPERUSER, block=True)
shop_del_goods = on_command("删除商品", priority=5, permission=SUPERUSER, block=True)
shop_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER, block=True)
@shop_help.handle()
async def _():
await shop_help.send(image(b64=await create_shop_help()))
@shop_add_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
await shop_add_goods.finish(data)
if not data.get("name") or not data.get("price") or not data.get("des"):
await shop_add_goods.finish("name:price:des 参数不可缺少!")
if await register_goods(**data):
await shop_add_goods.send(
f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n"
f"简介:{data['des']}\n"
f"折扣:{data.get('discount')}\n"
f"限时:{data.get('limit_time')}",
at_sender=True,
)
logger.info(f"添加商品 {msg} 成功", "添加商品", event.user_id)
else:
await shop_add_goods.send(f"添加商品 {msg} 失败了...", at_sender=True)
logger.warning(f"添加商品 {msg} 失败", "添加商品", event.user_id)
@shop_del_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
name = ""
id_ = 0
if is_number(msg):
id_ = int(msg)
else:
name = msg
rst, goods_name, code = await delete_goods(name, id_)
if code == 200:
await shop_del_goods.send(f"删除商品 {goods_name} 成功了...", at_sender=True)
if os.path.exists(f"{IMAGE_PATH}/shop_help.png"):
os.remove(f"{IMAGE_PATH}/shop_help.png")
logger.info(f"删除商品成功", "删除商品", event.user_id, target=goods_name)
else:
await shop_del_goods.send(f"删除商品 {goods_name} 失败了...", at_sender=True)
logger.info(f"删除商品失败", "删除商品", event.user_id, target=goods_name)
@shop_update_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
if msg:
data = parse_goods_info(msg)
if isinstance(data, str):
await shop_add_goods.finish(data)
if not data.get("name"):
await shop_add_goods.finish("name 参数不可缺少!")
flag, name, text = await update_goods(**data)
if flag:
await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True)
logger.info(f"修改商品数据 {text} 成功", "修改商品", event.user_id, target=name)
else:
await shop_update_goods.send(name, at_sender=True)
logger.info(f"修改商品数据 {text} 失败", "修改商品", event.user_id, target=name)
@scheduler.scheduled_job(
"cron",
hour=0,
minute=0,
)
async def _():
try:
await GoodsInfo.all().update(daily_purchase_limit={})
logger.info("商品每日限购次数重置成功...")
except Exception as e:
logger.error(f"商品每日限购次数重置出错", e=e)
@scheduler.scheduled_job(
"cron",
hour=0,
minute=1,
)
async def _():
try:
await BagUser.all().update(get_today_gold=0, spend_today_gold=0)
except Exception as e:
logger.error(f"重置每日金币", "定时任务", e=e)

View File

@ -1,416 +0,0 @@
import time
from typing import List, Optional, Tuple, Union
from PIL import Image
from configs.path_config import IMAGE_PATH
from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage, text2image
from utils.utils import GDict, is_number
icon_path = IMAGE_PATH / "shop_icon"
# 创建商店界面
async def create_shop_help() -> str:
"""
制作商店图片
:return: 图片base64
"""
goods_lst = await GoodsInfo.get_all_goods()
_dc = {}
font_h = BuildImage(0, 0).getsize("")[1]
h = 10
_list: List[GoodsInfo] = []
for goods in goods_lst:
if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time:
_list.append(goods)
# A = BuildImage(1100, h, color="#f9f6f2")
total_n = 0
image_list = []
for idx, goods in enumerate(_list):
name_image = BuildImage(
580, 40, font_size=25, color="#e67b6b", font="CJGaoDeGuo.otf"
)
await name_image.atext(
(15, 0), f"{idx + 1}.{goods.goods_name}", center_type="by_height"
)
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
await name_image.atext((390, 0), "售价:", center_type="by_height")
if goods.goods_discount != 1:
discount_price = int(goods.goods_discount * goods.goods_price)
old_price_image = BuildImage(
0,
0,
plain_text=str(goods.goods_price),
font_color=(194, 194, 194),
font="CJGaoDeGuo.otf",
font_size=15,
)
await old_price_image.aline(
(
0,
int(old_price_image.h / 2),
old_price_image.w + 1,
int(old_price_image.h / 2),
),
(0, 0, 0),
)
await name_image.apaste(old_price_image, (440, 0), True)
await name_image.atext((440, 15), str(discount_price), (255, 255, 255))
else:
await name_image.atext(
(440, 0),
str(goods.goods_price),
(255, 255, 255),
center_type="by_height",
)
await name_image.atext(
(
440
+ BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w,
0,
),
f" 金币",
center_type="by_height",
)
des_image = None
font_img = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
)
p = font_img.getsize("简介:")[0] + 20
if goods.goods_description:
des_list = goods.goods_description.split("\n")
desc = ""
for des in des_list:
if font_img.getsize(des)[0] > font_img.w - p - 20:
msg = ""
tmp = ""
for i in range(len(des)):
if font_img.getsize(tmp)[0] < font_img.w - p - 20:
tmp += des[i]
else:
msg += tmp + "\n"
tmp = des[i]
desc += msg
if tmp:
desc += tmp
else:
desc += des + "\n"
if desc[-1] == "\n":
desc = desc[:-1]
des_image = await text2image(desc, color="#a29ad6")
goods_image = BuildImage(
600,
(50 + des_image.h) if des_image else 50,
font_size=20,
color="#a29ad6",
font="CJGaoDeGuo.otf",
)
if des_image:
await goods_image.atext((15, 50), "简介:")
await goods_image.apaste(des_image, (p, 50))
await name_image.acircle_corner(5)
await goods_image.apaste(name_image, (0, 5), True, center_type="by_width")
await goods_image.acircle_corner(20)
bk = BuildImage(
1180,
(50 + des_image.h) if des_image else 50,
font_size=15,
color="#f9f6f2",
font="CJGaoDeGuo.otf",
)
if goods.icon and (icon_path / goods.icon).exists():
icon = BuildImage(70, 70, background=icon_path / goods.icon)
await bk.apaste(icon)
await bk.apaste(goods_image, (70, 0), alpha=True)
n = 0
_w = 650
# 添加限时图标和时间
if goods.goods_limit_time > 0:
n += 140
_limit_time_logo = BuildImage(
40, 40, background=f"{IMAGE_PATH}/other/time.png"
)
await bk.apaste(_limit_time_logo, (_w + 50, 0), True)
await bk.apaste(
BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"),
(_w + 90, 10),
True,
)
limit_time = time.strftime(
"%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)
).split()
y_m_d = limit_time[0]
_h_m = limit_time[1].split(":")
h_m = _h_m[0] + "" + _h_m[1] + ""
await bk.atext((_w + 55, 38), str(y_m_d))
await bk.atext((_w + 65, 57), str(h_m))
_w += 140
if goods.goods_discount != 1:
n += 140
_discount_logo = BuildImage(
30, 30, background=f"{IMAGE_PATH}/other/discount.png"
)
await bk.apaste(_discount_logo, (_w + 50, 10), True)
await bk.apaste(
BuildImage(0, 0, plain_text="折扣!", font_size=23, font="CJGaoDeGuo.otf"),
(_w + 90, 15),
True,
)
await bk.apaste(
BuildImage(
0,
0,
plain_text=f"{10 * goods.goods_discount:.1f}",
font_size=30,
font="CJGaoDeGuo.otf",
font_color=(85, 156, 75),
),
(_w + 50, 44),
True,
)
_w += 140
if goods.daily_limit != 0:
n += 140
_daily_limit_logo = BuildImage(
35, 35, background=f"{IMAGE_PATH}/other/daily_limit.png"
)
await bk.apaste(_daily_limit_logo, (_w + 50, 10), True)
await bk.apaste(
BuildImage(0, 0, plain_text="限购!", font_size=23, font="CJGaoDeGuo.otf"),
(_w + 90, 20),
True,
)
await bk.apaste(
BuildImage(
0,
0,
plain_text=f"{goods.daily_limit}",
font_size=30,
font="CJGaoDeGuo.otf",
),
(_w + 72, 45),
True,
)
if total_n < n:
total_n = n
if n:
await bk.aline((650, -1, 650 + n, -1), "#a29ad6", 5)
# await bk.aline((650, 80, 650 + n, 80), "#a29ad6", 5)
# 添加限时图标和时间
image_list.append(bk)
# await A.apaste(bk, (0, current_h), True)
# current_h += 90
h = 0
current_h = 0
for img in image_list:
h += img.h + 10
A = BuildImage(1100, h, color="#f9f6f2")
for img in image_list:
await A.apaste(img, (0, current_h), True)
current_h += img.h + 10
w = 950
if total_n:
w += total_n
h = A.h + 230 + 100
h = 1000 if h < 1000 else h
shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan_3.png")
zx_img.transpose(Image.FLIP_LEFT_RIGHT)
zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242))
await shop.apaste(zx_img, (0, 100))
shop.paste(A, (20 + zx_img.w, 230))
await shop.apaste(shop_logo, (450, 30), True)
shop.text(
(int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170),
"注【通过 序号 或者 商品名称 购买】",
)
shop.text((20 + zx_img.w, h - 100), "神秘药水\t\t售价9999999金币\n\t\t鬼知道会有什么效果~")
return shop.pic2bs4()
async def register_goods(
name: str,
price: int,
des: str,
discount: Optional[float] = 1,
limit_time: Optional[int] = 0,
daily_limit: Optional[int] = 0,
is_passive: Optional[bool] = False,
icon: Optional[str] = None,
) -> bool:
"""
添加商品
例如 折扣可选参数 限时时间:可选单位为小时
添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯但是里面.. discount:0.4 limit_time:90
添加商品 name:可疑的药 price:5 des:效果未知
:param name: 商品名称
:param price: 商品价格
:param des: 商品简介
:param discount: 商品折扣
:param limit_time: 商品限时销售时间单位为小时
:param daily_limit: 每日购买次数限制
:param is_passive: 是否为被动
:param icon: 图标
:return: 是否添加成功
"""
if not await GoodsInfo.get_or_none(goods_name=name):
limit_time_ = float(limit_time) if limit_time else limit_time
discount = discount if discount is not None else 1
limit_time_ = (
int(time.time() + limit_time_ * 60 * 60)
if limit_time_ is not None and limit_time_ != 0
else 0
)
await GoodsInfo.create(
goods_name=name,
goods_price=int(price),
goods_description=des,
goods_discount=float(discount),
goods_limit_time=limit_time_,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon,
)
return True
return False
# 删除商品
async def delete_goods(name: str, id_: int) -> Tuple[str, str, int]:
"""
删除商品
:param name: 商品名称
:param id_: 商品id
:return: 删除状况
"""
goods_lst = await GoodsInfo.get_all_goods()
if id_:
if id_ < 1 or id_ > len(goods_lst):
return "序号错误,没有该序号商品...", "", 999
goods_name = goods_lst[id_ - 1].goods_name
if await GoodsInfo.delete_goods(goods_name):
return f"删除商品 {goods_name} 成功!", goods_name, 200
else:
return f"删除商品 {goods_name} 失败!", goods_name, 999
if name:
if await GoodsInfo.delete_goods(name):
return f"删除商品 {name} 成功!", name, 200
else:
return f"删除商品 {name} 失败!", name, 999
return "获取商品失败", "", 999
# 更新商品信息
async def update_goods(**kwargs) -> Tuple[bool, str, str]:
"""
更新商品信息
:param kwargs: kwargs
:return: 更新状况
"""
if kwargs:
goods_lst = await GoodsInfo.get_all_goods()
if is_number(kwargs["name"]):
if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst):
return False, "序号错误,没有该序号的商品...", ""
goods = goods_lst[int(kwargs["name"]) - 1]
else:
goods = await GoodsInfo.filter(goods_name=kwargs["name"]).first()
if not goods:
return False, "名称错误,没有该名称的商品...", ""
name: str = goods.goods_name
price = goods.goods_price
des = goods.goods_description
discount = goods.goods_discount
limit_time = goods.goods_limit_time
daily_limit = goods.daily_limit
is_passive = goods.is_passive
new_time = 0
tmp = ""
if kwargs.get("price"):
tmp += f'价格:{price} --> {kwargs["price"]}\n'
price = kwargs["price"]
if kwargs.get("des"):
tmp += f'描述:{des} --> {kwargs["des"]}\n'
des = kwargs["des"]
if kwargs.get("discount"):
tmp += f'折扣:{discount} --> {kwargs["discount"]}\n'
discount = kwargs["discount"]
if kwargs.get("limit_time"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = (
time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
)
if kwargs["limit_time"] != 0
else 0
)
tmp += f"限时至: {new_time}\n" if new_time else "取消了限时\n"
limit_time = kwargs["limit_time"]
if kwargs.get("daily_limit"):
tmp += (
f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n'
if daily_limit
else "取消了购买限制\n"
)
daily_limit = int(kwargs["daily_limit"])
if kwargs.get("is_passive"):
tmp += f'被动道具:{is_passive} --> {kwargs["is_passive"]}\n'
des = kwargs["is_passive"]
await GoodsInfo.update_goods(
name,
int(price),
des,
float(discount),
int(
time.time() + limit_time * 60 * 60
if limit_time != 0 and new_time
else 0
),
daily_limit,
is_passive,
)
return (
True,
name,
tmp[:-1],
)
def parse_goods_info(msg: str) -> Union[dict, str]:
"""
解析格式数据
:param msg: 消息
:return: 解析完毕的数据data
"""
if "name:" not in msg:
return "必须指定修改的商品名称或序号!"
data = {}
for x in msg.split():
sp = x.split(":", maxsplit=1)
if str(sp[1]).strip():
sp[1] = sp[1].strip()
if sp[0] == "name":
data["name"] = sp[1]
elif sp[0] == "price":
if not is_number(sp[1]) or int(sp[1]) < 0:
return "price参数不合法必须大于等于0"
data["price"] = sp[1]
elif sp[0] == "des":
data["des"] = sp[1]
elif sp[0] == "discount":
if not is_number(sp[1]) or float(sp[1]) < 0:
return "discount参数不合法必须大于0"
data["discount"] = sp[1]
elif sp[0] == "limit_time":
if not is_number(sp[1]) or float(sp[1]) < 0:
return "limit_time参数不合法必须为数字且大于0"
data["limit_time"] = sp[1]
elif sp[0] == "daily_limit":
if not is_number(sp[1]) or float(sp[1]) < 0:
return "daily_limit参数不合法必须为数字且大于0"
data["daily_limit"] = sp[1]
return data

View File

@ -1,115 +0,0 @@
from typing import Any, Tuple
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.params import CommandArg, RegexGroup
from models.bag_user import BagUser
from models.user_shop_gold_log import UserShopGoldLog
from services.log import logger
from utils.decorator.shop import NotMeetUseConditionsException
from utils.utils import is_number
from .data_source import build_params, effect, func_manager, register_use
__zx_plugin_name__ = "商店 - 使用道具"
__plugin_usage__ = """
usage
普通的使用道具
指令
使用道具 [序号或道具名称] ?[数量]=1 ?[其他信息]
示例使用道具好感度双倍加持卡 使用道具好感度双倍加持卡
示例使用道具1 使用第一个道具
示例使用道具1 10 使用10个第一个道具
示例使用道具1 1 来点色图 使用第一个道具并附带信息
* 序号以 我的道具 为准 *
""".strip()
__plugin_des__ = "商店 - 使用道具"
__plugin_cmd__ = ["使用道具 [序号或道具名称]"]
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["商店", "使用道具"],
}
use_props = on_command(r"使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text()
num = 1
text = ""
prop_n = None
index = None
split = msg.split()
if size := len(split):
if size == 1:
prop_n = split[0].strip()
index = 1
if size > 1 and is_number(split[1].strip()):
prop_n = split[0].strip()
num = int(split[1].strip())
index = 2
else:
await use_props.finish("缺少参数,请查看帮助", at_sender=True)
if index:
text = " ".join(split[index:])
property_ = await BagUser.get_property(event.user_id, event.group_id, True)
if property_:
name = None
if prop_n and is_number(prop_n):
if 0 < int(prop_n) <= len(property_):
name = list(property_.keys())[int(prop_n) - 1]
else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else:
if prop_n not in property_.keys():
await use_props.finish("道具名称错误!", at_sender=True)
name = prop_n
if not name:
await use_props.finish("未获取到道具名称", at_sender=True)
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
try:
model, kwargs = build_params(bot, event, name, num, text)
except KeyError:
logger.warning(f"{name} 未注册使用函数")
await use_props.finish(f"{name} 未注册使用方法")
else:
try:
await func_manager.run_handle(
type_="before_handle", param=model, **kwargs
)
except NotMeetUseConditionsException as e:
await use_props.finish(e.get_info(), at_sender=True)
if await BagUser.delete_property(event.user_id, event.group_id, name, num):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
if msg := await effect(bot, event, name, num, text, event.message):
await use_props.send(msg, at_sender=True)
logger.info(f"使用道具 {name} {num} 次成功", event.user_id, event.group_id)
await UserShopGoldLog.create(
user_id=event.user_id,
group_id=event.group_id,
type=1,
name=name,
num=num,
)
else:
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info(
f"使用道具 {name} {num} 次失败", "使用道具", event.user_id, event.group_id
)
await func_manager.run_handle(type_="after_handle", param=model, **kwargs)
else:
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)

View File

@ -1,235 +0,0 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment, Message
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot
from pydantic import create_model
from utils.models import ShopParam
from typing import Optional, Union, Callable, List, Tuple, Dict, Any
from types import MappingProxyType
import inspect
import asyncio
class GoodsUseFuncManager:
def __init__(self):
self._data = {}
def register_use_before_handle(self, goods_name: str, fun_list: List[Callable]):
"""
说明:
注册商品使用前函数
参数:
:param goods_name: 商品名称
:param fun_list: 函数列表
"""
if fun_list:
self._data[goods_name]["before_handle"] = fun_list
logger.info(f"register_use_before_handle 成功注册商品:{goods_name}{len(fun_list)}个使用前函数")
def register_use_after_handle(self, goods_name: str, fun_list: List[Callable]):
"""
说明:
注册商品使用后函数
参数:
:param goods_name: 商品名称
:param fun_list: 函数列表
"""
if fun_list:
self._data[goods_name]["after_handle"] = fun_list
logger.info(f"register_use_after_handle 成功注册商品:{goods_name}{len(fun_list)}个使用后函数")
def register_use(self, goods_name: str, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param kwargs: kwargs
"""
self._data[goods_name] = kwargs
def exists(self, goods_name: str) -> bool:
"""
判断商品使用方法是否被注册
:param goods_name: 商品名称
"""
return bool(self._data.get(goods_name))
def get_max_num_limit(self, goods_name: str) -> int:
"""
获取单次商品使用数量
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]["max_num_limit"]
return 1
def _parse_args(self, args_: MappingProxyType, param: ShopParam, **kwargs):
param_list_ = []
_bot = param.bot
param.bot = None
param_json = param.dict()
param_json["bot"] = _bot
for par in args_.keys():
if par in ["shop_param"]:
param_list_.append(param)
elif par not in ["args", "kwargs"]:
param_list_.append(param_json.get(par))
if kwargs.get(par) is not None:
del kwargs[par]
return param_list_
async def use(
self, param: ShopParam, **kwargs
) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param param: BaseModel
:param kwargs: kwargs
"""
goods_name = param.goods_name
if self.exists(goods_name):
# 使用方法
args = inspect.signature(self._data[goods_name]["func"]).parameters
if args and list(args.keys())[0] != "kwargs":
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
*self._parse_args(args, param, **kwargs)
)
else:
return self._data[goods_name]["func"](
*self._parse_args(args, param, **kwargs)
)
else:
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
**kwargs,
)
else:
return self._data[goods_name]["func"](
**kwargs,
)
async def run_handle(self, goods_name: str, type_: str, param: ShopParam, **kwargs):
if self._data.get(goods_name) and self._data[goods_name].get(type_):
for func in self._data[goods_name].get(type_):
args = inspect.signature(func).parameters
if args and list(args.keys())[0] != "kwargs":
if asyncio.iscoroutinefunction(func):
await func(*self._parse_args(args, param, **kwargs))
else:
func(*self._parse_args(args, param, **kwargs))
else:
if asyncio.iscoroutinefunction(func):
await func(**kwargs)
else:
func(**kwargs)
def check_send_success_message(self, goods_name: str) -> bool:
"""
检查是否发送使用成功信息
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return bool(self._data[goods_name]["kwargs"]["send_success_msg"])
return False
def get_kwargs(self, goods_name: str) -> dict:
"""
获取商品使用方法的kwargs
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]
return {}
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int, text: str):
return self._data[goods_name]["model"](
**{
"goods_name": goods_name,
"bot": bot,
"event": event,
"user_id": event.user_id,
"group_id": event.group_id,
"num": num,
"message": event.message,
"text": text
}
)
func_manager = GoodsUseFuncManager()
def build_params(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str
) -> Tuple[ShopParam, Dict[str, Any]]:
"""
说明:
构造参数
参数:
:param bot: bot
:param event: event
:param goods_name: 商品名称
:param num: 数量
:param text: 其他信息
"""
_kwargs = func_manager.get_kwargs(goods_name)
return (
func_manager.init_model(goods_name, bot, event, num, text),
{
**_kwargs,
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"text": text,
"message": event.message,
"goods_name": goods_name,
},
)
async def effect(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int, text: str, message: Message
) -> Optional[Union[str, MessageSegment]]:
"""
商品生效
:param bot: Bot
:param event: GroupMessageEvent
:param goods_name: 商品名称
:param num: 使用数量
:param text: 其他信息
:param message: Message
:return: 使用是否成功
"""
# 优先使用注册的商品插件
# try:
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
model, kwargs = build_params(bot, event, goods_name, num, text)
return await func_manager.use(model, **kwargs)
# except Exception as e:
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}{e}")
return None
def register_use(goods_name: str, func: Callable, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param func: 使用函数
:param kwargs: kwargs
"""
if func_manager.exists(goods_name):
raise ValueError("该商品使用函数已被注册!")
# 发送使用成功信息
kwargs["send_success_msg"] = kwargs.get("send_success_msg", True)
kwargs["max_num_limit"] = kwargs.get("max_num_limit", 1)
func_manager.register_use(
goods_name,
**{
"func": func,
"model": create_model(f"{goods_name}_model", __base__=ShopParam, **kwargs),
"kwargs": kwargs,
},
)
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")

View File

@ -1,158 +0,0 @@
from typing import Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from models.group_info import GroupInfo
from services.log import logger
from utils.depends import OneCommand
from utils.manager import requests_manager
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "显示所有好友群组 [Superuser]"
__plugin_usage__ = """
usage
显示所有好友群组
指令
查看所有好友/查看所有群组
同意好友请求 [id]
拒绝好友请求 [id]
同意群聊请求 [id]
拒绝群聊请求 [id]
查看所有请求
清空所有请求
""".strip()
__plugin_des__ = "显示所有好友群组"
__plugin_cmd__ = [
"查看所有好友/查看所有群组",
"同意好友请求 [id]",
"拒绝好友请求 [id]",
"同意群聊请求 [id]",
"拒绝群聊请求 [id]",
"查看所有请求",
"清空所有请求",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
cls_group = on_command(
"查看所有群组", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
cls_friend = on_command(
"查看所有好友", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
friend_handle = on_command(
"同意好友请求", aliases={"拒绝好友请求"}, permission=SUPERUSER, priority=1, block=True
)
group_handle = on_command(
"同意群聊请求", aliases={"拒绝群聊请求"}, permission=SUPERUSER, priority=1, block=True
)
clear_request = on_command("清空所有请求", permission=SUPERUSER, priority=1, block=True)
cls_request = on_command("查看所有请求", permission=SUPERUSER, priority=1, block=True)
@cls_group.handle()
async def _(bot: Bot):
gl = await bot.get_group_list()
msg = ["{group_id} {group_name}".format_map(g) for g in gl]
msg = "\n".join(msg)
msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg
await cls_group.send(msg)
@cls_friend.handle()
async def _(bot: Bot):
gl = await bot.get_friend_list()
msg = ["{user_id} {nickname}".format_map(g) for g in gl]
msg = "\n".join(msg)
msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg
await cls_friend.send(msg)
@friend_handle.handle()
async def _(bot: Bot, cmd: str = OneCommand(), arg: Message = CommandArg()):
id_ = arg.extract_plain_text().strip()
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
flag = await requests_manager.approve(bot, id_, "private")
else:
flag = await requests_manager.refused(bot, id_, "private")
if flag == 1:
await friend_handle.send(f"{cmd[:2]}好友请求失败,该请求已失效..")
requests_manager.delete_request(id_, "private")
elif flag == 2:
await friend_handle.send(f"{cmd[:2]}好友请求失败未找到此id的请求..")
else:
await friend_handle.send(f"{cmd[:2]}好友请求成功!")
else:
await friend_handle.send("id必须为纯数字")
@group_handle.handle()
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_):
id_ = int(id_)
if cmd[:2] == "同意":
rid = requests_manager.get_group_id(id_)
if rid:
if group := await GroupInfo.get_or_none(group_id=str(rid)):
group.group_flag = 1
await group.save(update_fields=["group_flag"])
else:
group_info = await bot.get_group_info(group_id=rid)
await GroupInfo.create(
group_id=str(rid),
group_name=group_info["group_name"],
max_member_count=group_info["max_member_count"],
member_count=group_info["member_count"],
group_flag=1,
)
flag = await requests_manager.approve(bot, id_, "group")
else:
await group_handle.send("同意群聊请求失败未找到此id的请求..")
logger.info("同意群聊请求失败未找到此id的请求..", cmd, event.user_id)
else:
flag = await requests_manager.refused(bot, id_, "group")
if flag == 1:
await group_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..")
requests_manager.delete_request(id_, "group")
elif flag == 2:
await group_handle.send(f"{cmd[:2]}群聊请求失败未找到此id的请求..")
else:
await group_handle.send(f"{cmd[:2]}群聊请求成功!")
else:
await group_handle.send("id必须为纯数字")
@cls_request.handle()
async def _():
_str = ""
for type_ in ["private", "group"]:
msg = await requests_manager.show(type_)
if msg:
_str += image(msg)
else:
_str += "没有任何好友请求.." if type_ == "private" else "没有任何群聊请求.."
if type_ == "private":
_str += "\n--------------------\n"
await cls_request.send(Message(_str))
@clear_request.handle()
async def _():
requests_manager.clear()
await clear_request.send("已清空所有好友/群聊请求..")

View File

@ -1,75 +0,0 @@
import asyncio
import os
import time
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.path_config import TEMP_PATH
from services.log import logger
from utils.manager import resources_manager
from utils.utils import scheduler
__zx_plugin_name__ = "清理临时数据 [Superuser]"
__plugin_usage__ = """
usage
清理临时数据
指令
清理临时数据
""".strip()
__plugin_des__ = "清理临时数据"
__plugin_cmd__ = [
"清理临时数据",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
clear_data = on_command(
"清理临时数据", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
resources_manager.add_temp_dir(TEMP_PATH)
@clear_data.handle()
async def _():
await clear_data.send("开始清理临时数据....")
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
logger.info("清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
def _clear_data() -> float:
logger.debug("开始清理临时文件...")
size = 0
dir_list = [dir_ for dir_ in resources_manager.get_temp_data_dir() if dir_.exists()]
for dir_ in dir_list:
logger.debug(f"尝试清理文件夹: {dir_.absolute()}", "清理临时数据")
dir_size = 0
for file in os.listdir(dir_):
file = dir_ / file
if file.is_file():
try:
if time.time() - os.path.getatime(file) > 10:
file_size = os.path.getsize(file)
file.unlink()
size += file_size
dir_size += file_size
logger.debug(f"移除临时文件: {file.absolute()}", "清理临时数据")
except Exception as e:
logger.error(f"清理临时数据错误,临时文件夹: {dir_.absolute()}...", "清理临时数据", e=e)
logger.debug("清理临时文件夹大小: {:.2f}MB".format(size / 1024 / 1024), "清理临时数据")
return float(size)
@scheduler.scheduled_job(
"cron",
hour=1,
minute=1,
)
async def _():
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
logger.info("自动清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))

View File

@ -1,92 +0,0 @@
import asyncio
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from tortoise import Tortoise
from services.db_context import TestSQL
from services.log import logger
from utils.message_builder import custom_forward_msg
__zx_plugin_name__ = "执行sql [Superuser]"
__plugin_usage__ = """
usage
执行一段sql语句
指令
exec [sql语句] ([查询页数19/])
查看所有表
""".strip()
__plugin_des__ = "执行一段sql语句"
__plugin_cmd__ = ["exec [sql语句]", "查看所有表"]
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
tables = on_command("查看所有表", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
@exec_.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
sql = arg.extract_plain_text().strip()
if not sql:
await exec_.finish("未接受到sql语句")
db = Tortoise.get_connection("default")
# try:
# 判断是否为SELECT语句
if sql.lower().startswith("select"):
pass
# # 分割语句
try:
page = int(sql.split(" ")[-1]) - 1
sql_list = sql.split(" ")[:-1]
except ValueError:
page = 0
sql_list = sql.split(" ")
# 拼接语句
sql = " ".join(sql_list)
res = await db.execute_query_dict(sql)
msg_list = [f"{page+1}页查询结果:"]
# logger.info(res)
# 获取所有字段
keys = res[0].keys()
# 每页10条
for i in res[page * 10 : (page + 1) * 10]:
msg = ""
for key in keys:
msg += f"{key}: {i[key]}\n"
msg += f"{page+1}页第{res.index(i)+1}"
msg_list.append(msg)
# 检查是私聊还是群聊
if isinstance(event, GroupMessageEvent):
forward_msg_list = custom_forward_msg(msg_list, bot.self_id)
await bot.send_group_forward_msg(
group_id=event.group_id, messages=forward_msg_list
)
else:
for msg in msg_list:
await exec_.send(msg)
await asyncio.sleep(0.2)
return
else:
await TestSQL.raw(sql)
await exec_.send("执行 sql 语句成功.")
# except Exception as e:
# await exec_.send(f"执行 sql 语句失败 {type(e)}{e}")
# logger.error(f"执行 sql 语句失败 {type(e)}{e}")
@tables.handle()
async def _(bot: Bot, event: MessageEvent):
# 获取所有表
db = Tortoise.get_connection("default")
query = await db.execute_query_dict(
"select tablename from pg_tables where schemaname = 'public'"
)
msg = "数据库中的所有表名:\n"
for tablename in query:
msg += str(tablename["tablename"]) + "\n"
await tables.finish(msg)

View File

@ -1,235 +0,0 @@
from typing import Tuple
from nonebot import on_command, on_regex
from nonebot.adapters.onebot.v11 import (
GROUP,
Bot,
GroupMessageEvent,
Message,
MessageEvent,
)
from nonebot.params import Command, CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.config import NICKNAME
from models.group_info import GroupInfo
from services.log import logger
from utils.depends import OneCommand
from utils.image_utils import text2image
from utils.manager import group_manager, plugins2settings_manager
from utils.message_builder import image
from utils.utils import is_number
__zx_plugin_name__ = "管理群操作 [Superuser]"
__plugin_usage__ = """
usage
群权限 | 群白名单 | 退出群 操作
退群添加/删除群白名单添加/删除群认证当在群聊中这五个命令且没有指定群号时默认指定当前群聊
指令:
退群 ?[group_id]
修改群权限 [group_id] [等级]
修改群权限 [等级]: 该命令仅在群聊时生效默认修改当前群聊
添加群白名单 ?*[group_id]
删除群白名单 ?*[group_id]
添加群认证 ?*[group_id]
删除群认证 ?*[group_id]
查看群白名单
""".strip()
__plugin_des__ = "管理群操作"
__plugin_cmd__ = [
"退群 [group_id]",
"修改群权限 [group_id] [等级]",
"添加群白名单 *[group_id]",
"删除群白名单 *[group_id]",
"添加群认证 *[group_id]",
"删除群认证 *[group_id]",
"查看群白名单",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
del_group = on_command("退群", rule=to_me(), permission=SUPERUSER, priority=1, block=True)
add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True)
my_group_level = on_command(
"查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True
)
what_up_group_level = on_regex(
"(:?提高|提升|升高|增加|加上).*?群权限",
rule=to_me(),
priority=5,
permission=GROUP,
block=True,
)
manager_group_whitelist = on_command(
"添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True
)
show_group_whitelist = on_command(
"查看群白名单", priority=1, permission=SUPERUSER, block=True
)
group_auth = on_command(
"添加群认证", aliases={"删除群认证"}, priority=1, permission=SUPERUSER, block=True
)
@del_group.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
group_id = arg.extract_plain_text().strip()
if not group_id and isinstance(event, GroupMessageEvent):
group_id = event.group_id
if group_id:
if is_number(group_id):
group_list = [x["group_id"] for x in await bot.get_group_list()]
group_id = int(group_id)
if group_id not in group_list:
logger.debug("群聊不存在", "退群", event.user_id, target=group_id)
await del_group.finish(f"{NICKNAME}未在该群聊中...")
try:
await bot.set_group_leave(group_id=group_id)
logger.info(f"{NICKNAME}退出群聊成功", "退群", event.user_id, target=group_id)
await del_group.send(f"退出群聊 {group_id} 成功", at_sender=True)
group_manager.delete_group(group_id)
await GroupInfo.filter(group_id=group_id).delete()
except Exception as e:
logger.error(f"退出群聊失败", "退群", event.user_id, target=group_id, e=e)
await del_group.send(f"退出群聊 {group_id} 失败", at_sender=True)
else:
await del_group.send(f"请输入正确的群号", at_sender=True)
else:
await del_group.send(f"请输入群号", at_sender=True)
@add_group_level.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
msg = msg.split()
group_id = 0
level = 0
if isinstance(event, GroupMessageEvent) and len(msg) == 1:
msg = [event.group_id, msg[0]]
if not msg:
await add_group_level.finish("缺失参数...")
if len(msg) < 2:
await add_group_level.finish("缺失参数...")
if is_number(msg[0]) and is_number(msg[1]):
group_id = str(msg[0])
level = int(msg[1])
else:
await add_group_level.finish("参数错误...群号和等级必须是数字..")
old_level = group_manager.get_group_level(group_id)
group_manager.set_group_level(group_id, level)
await add_group_level.send("修改成功...", at_sender=True)
if level > -1:
await bot.send_group_msg(
group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}"
)
logger.info(f"修改群权限:{level}", "修改群权限", event.user_id, target=group_id)
@my_group_level.handle()
async def _(event: GroupMessageEvent):
level = group_manager.get_group_level(event.group_id)
tmp = ""
data = plugins2settings_manager.get_data()
for module in data:
if data[module].level > level:
plugin_name = data[module].cmd[0]
if plugin_name == "pixiv":
plugin_name = "搜图 p站排行"
tmp += f"{plugin_name}\n"
if not tmp:
await my_group_level.finish(f"当前群权限:{level}")
await my_group_level.finish(
f"当前群权限:{level}\n目前无法使用的功能:\n"
+ image(await text2image(tmp, padding=10, color="#f9f6f2"))
)
@what_up_group_level.handle()
async def _():
await what_up_group_level.finish(
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.超级管理员修改权限"
)
@manager_group_whitelist.handle()
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
msg = arg.extract_plain_text().strip().split()
if not msg and isinstance(event, GroupMessageEvent):
msg = [event.group_id]
if not msg:
await manager_group_whitelist.finish("请输入群号")
all_group = [g["group_id"] for g in await bot.get_group_list()]
error_group = []
group_list = []
for group_id in msg:
if is_number(group_id) and int(group_id) in all_group:
group_list.append(int(group_id))
else:
logger.debug(f"群号不合法或不存在", cmd, target=group_id)
error_group.append(group_id)
if group_list:
for group_id in group_list:
if cmd in ["添加群白名单"]:
group_manager.add_group_white_list(group_id)
else:
group_manager.delete_group_white_list(group_id)
group_list = [str(x) for x in group_list]
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd)
group_manager.save()
if error_group:
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))
@show_group_whitelist.handle()
async def _():
group = [str(g) for g in group_manager.get_group_white_list()]
if not group:
await show_group_whitelist.finish("没有任何群在群白名单...")
await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(group))
@group_auth.handle()
async def _(
bot: Bot, event: MessageEvent, cmd: str = OneCommand(), arg: Message = CommandArg()
):
msg = arg.extract_plain_text().strip().split()
if isinstance(event, GroupMessageEvent) and not msg:
msg = [event.group_id]
if not msg:
await manager_group_whitelist.finish("请输入群号")
error_group = []
for group_id in msg:
group_id = int(group_id)
if cmd[:2] == "添加":
try:
await GroupInfo.update_or_create(
group_id=group_id,
defaults={
"group_flag": 1,
},
)
except Exception as e:
await group_auth.send(f"添加群认证 {group_id} 发生错误!")
logger.error(f"添加群认证发生错误", cmd, target=group_id, e=e)
else:
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..")
logger.info(f"添加群认证成功", cmd, target=group_id)
else:
if group := await GroupInfo.filter(group_id=group_id).first():
await group.update_or_create(
group_id=group_id, defaults={"group_flag": 0}
)
await group_auth.send(f"已删除 {group_id} 群认证..")
logger.info(f"删除群认证成功", cmd, target=group_id)
else:
await group_auth.send(f"未查找到群聊: {group_id}")
logger.info(f"未找到群聊", cmd, target=group_id)
if error_group:
await manager_group_whitelist.send("以下群聊不合法或不存在:\n" + "\n".join(error_group))

View File

@ -1,64 +0,0 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from configs.config import Config
from services.log import logger
from utils.manager import (
group_manager,
plugins2block_manager,
plugins2cd_manager,
plugins2settings_manager,
)
from utils.utils import scheduler
__zx_plugin_name__ = "重载配置 [Superuser]"
__plugin_usage__ = """
usage
重载配置
plugins2settings,
plugins2cd
plugins2block
group_manager
指令
重载配置
""".strip()
__plugin_des__ = "重载配置"
__plugin_cmd__ = [
"重载配置",
]
__plugin_version__ = 0.2
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {"value": False, "help": "自动重载配置文件", "default_value": False, "type": bool},
"AUTO_RELOAD_TIME": {"value": 180, "help": "控制自动重载配置文件时长", "default_value": 180, "type": int},
}
reload_plugins_manager = on_command(
"重载配置", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
@reload_plugins_manager.handle()
async def _():
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
await reload_plugins_manager.send("重载完成...")
@scheduler.scheduled_job(
"interval",
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
)
async def _():
if Config.get_config("reload_setting", "AUTO_RELOAD"):
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
logger.debug("已自动重载所有配置文件...")

View File

@ -1,114 +0,0 @@
from typing import List, Tuple
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.exception import ActionFailed
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from models.level_user import LevelUser
from services.log import logger
from utils.depends import AtList, OneCommand
from utils.message_builder import at
from utils.utils import is_number
__zx_plugin_name__ = "用户权限管理 [Superuser]"
__plugin_usage__ = """
usage
增删改用户的权限
指令
添加权限 [at] [权限]
添加权限 [qq] [group_id] [权限]
删除权限 [at]
删除权限 [qq] [group_id]
""".strip()
__plugin_des__ = "增删改用户的权限"
__plugin_cmd__ = [
"添加权限 [at] [权限]",
"添加权限 [qq] [group_id] [权限]",
"删除权限 [at]",
"删除权限 [qq] [group_id]",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
super_cmd = on_command(
"添加管理",
aliases={"删除管理", "添加权限", "删除权限"},
priority=1,
permission=SUPERUSER,
block=True,
)
@super_cmd.handle()
async def _(
bot: Bot,
event: MessageEvent,
cmd: str = OneCommand(),
arg: Message = CommandArg(),
at_list: List[int] = AtList(),
):
group_id = event.group_id if isinstance(event, GroupMessageEvent) else -1
level = None
args = arg.extract_plain_text().strip().split()
flag = 2
qq = None
try:
if at_list:
qq = at_list[0]
if cmd[:2] == "添加" and args and is_number(args[0]):
level = int(args[0])
else:
if cmd[:2] == "添加":
if (
len(args) > 2
and is_number(args[0])
and is_number(args[1])
and is_number(args[2])
):
qq = int(args[0])
group_id = int(args[1])
level = int(args[2])
else:
if len(args) > 1 and is_number(args[0]) and is_number(args[1]):
qq = int(args[0])
group_id = int(args[1])
flag = 1
level = -1 if cmd[:2] == "删除" else level
if group_id == -1 or not level or not qq:
raise IndexError()
except IndexError:
await super_cmd.finish(__plugin_usage__)
if not qq:
await super_cmd.finish("未指定对象...")
try:
if cmd[:2] == "添加":
await LevelUser.set_level(qq, group_id, level, 1)
result = f"设置权限成功, 权限: {level}"
else:
if await LevelUser.delete_level(qq, group_id):
result = "删除管理成功!"
else:
result = "该账号无管理权限!"
if flag == 2:
await super_cmd.send(result)
elif flag == 1:
try:
await bot.send_group_msg(
group_id=group_id,
message=Message(
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
)
except ActionFailed:
pass
await super_cmd.send("修改成功")
logger.info(
f"修改权限: {level if level != -1 else 0}", cmd, event.user_id, group_id, qq
)
except Exception as e:
await super_cmd.send("执行指令失败!")
logger.error(f"执行指令失败", cmd, event.user_id, group_id, qq, e=e)

View File

@ -1,77 +0,0 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from models.friend_user import FriendUser
from models.group_info import GroupInfo
from services.log import logger
__zx_plugin_name__ = "更新群/好友信息 [Superuser]"
__plugin_usage__ = """
usage
更新群/好友信息
指令
更新群信息
更新好友信息
""".strip()
__plugin_des__ = "更新群/好友信息"
__plugin_cmd__ = [
"更新群信息",
"更新好友信息",
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
update_group_info = on_command(
"更新群信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
update_friend_info = on_command(
"更新好友信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True
)
@update_group_info.handle()
async def _(bot: Bot, event: MessageEvent):
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
num = 0
for g in gl:
try:
group_info = await bot.get_group_info(group_id=g)
await GroupInfo.update_or_create(
group_id=str(group_info["group_id"]),
defaults={
"group_name": group_info["group_name"],
"max_member_count": group_info["max_member_count"],
"member_count": group_info["member_count"],
},
)
num += 1
logger.debug(
"群聊信息更新成功", "更新群信息", event.user_id, target=group_info["group_id"]
)
except Exception as e:
logger.error(f"更新群聊信息失败", "更新群信息", event.user_id, target=g, e=e)
await update_group_info.send(f"成功更新了 {len(gl)} 个群的信息")
logger.info(f"更新群聊信息完成,共更新了 {len(gl)} 个群的信息", "更新群信息", event.user_id)
@update_friend_info.handle()
async def _(bot: Bot, event: MessageEvent):
num = 0
error_list = []
fl = await bot.get_friend_list()
for f in fl:
try:
await FriendUser.update_or_create(
user_id=str(f["user_id"]), defaults={"nickname": f["nickname"]}
)
logger.debug(f"更新好友信息成功", "更新好友信息", event.user_id, target=f["user_id"])
num += 1
except Exception as e:
logger.error(f"更新好友信息失败", "更新好友信息", event.user_id, target=f["user_id"], e=e)
await update_friend_info.send(f"成功更新了 {num} 个好友的信息")
if error_list:
await update_friend_info.send(f"以下好友更新失败:\n" + "\n".join(error_list))
logger.info(f"更新好友信息完成,共更新了 {num} 个群的信息", "更新好友信息", event.user_id)

View File

@ -1,24 +0,0 @@
from nonebot import on_command
from nonebot.permission import SUPERUSER
from nonebot.rule import to_me
from utils.message_builder import image
from .data_source import SUPERUSER_HELP_IMAGE, create_help_image
__zx_plugin_name__ = "超级用户帮助 [Superuser]"
if SUPERUSER_HELP_IMAGE.exists():
SUPERUSER_HELP_IMAGE.unlink()
super_help = on_command(
"超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True
)
@super_help.handle()
async def _():
if not SUPERUSER_HELP_IMAGE.exists():
await create_help_image()
await super_help.finish(image(SUPERUSER_HELP_IMAGE))

View File

@ -1,81 +0,0 @@
import nonebot
from nonebot import Driver
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.image_template import help_template
from utils.image_utils import BuildImage, build_sort_image, group_image, text2image
from utils.manager import plugin_data_manager
from utils.manager.models import PluginType
driver: Driver = nonebot.get_driver()
SUPERUSER_HELP_IMAGE = IMAGE_PATH / "superuser_help.png"
@driver.on_bot_connect
async def create_help_image():
"""
创建超级用户帮助图片
"""
if SUPERUSER_HELP_IMAGE.exists():
return
plugin_data_ = plugin_data_manager.get_data()
image_list = []
task_list = []
for plugin_data in [
plugin_data_[x]
for x in plugin_data_
if plugin_data_[x].name != "超级用户帮助 [Superuser]"
]:
try:
if plugin_data.plugin_type in [PluginType.SUPERUSER, PluginType.ADMIN]:
usage = None
if (
plugin_data.plugin_type == PluginType.SUPERUSER
and plugin_data.usage
):
usage = await text2image(
plugin_data.usage, padding=5, color=(204, 196, 151)
)
if plugin_data.superuser_usage:
usage = await text2image(
plugin_data.superuser_usage, padding=5, color=(204, 196, 151)
)
if usage:
await usage.acircle_corner()
image = await help_template(plugin_data.name, usage)
image_list.append(image)
if plugin_data.task:
for x in plugin_data.task.keys():
task_list.append(plugin_data.task[x])
except Exception as e:
logger.warning(
f"获取超级用户插件 {plugin_data.model}: {plugin_data.name} 设置失败...", e=e
)
task_str = "\n".join(task_list)
task_str = "通过私聊 开启被动/关闭被动 + [被动名称] 来控制全局被动\n----------\n" + task_str
task_image = await text2image(task_str, padding=5, color=(204, 196, 151))
task_image = await help_template("被动任务", task_image)
image_list.append(task_image)
image_group, _ = group_image(image_list)
A = await build_sort_image(image_group, color="#f9f6f2", padding_top=180)
await A.apaste(
BuildImage(0, 0, font="CJGaoDeGuo.otf", plain_text="超级用户帮助", font_size=50),
(50, 30),
True,
)
await A.apaste(
BuildImage(
0,
0,
font="CJGaoDeGuo.otf",
plain_text="注: * 代表可有多个相同参数 ? 代表可省略该参数",
font_size=30,
font_color="red",
),
(50, 90),
True,
)
await A.asave(SUPERUSER_HELP_IMAGE)
logger.info(f"已成功加载 {len(image_list)} 条超级用户命令")

View File

@ -1,32 +0,0 @@
from nonebot import on_command
from utils.message_builder import image
__zx_plugin_name__ = "更新信息"
__plugin_usage__ = """
usage
更新信息
指令
更新信息
""".strip()
__plugin_des__ = "当前版本的更新信息"
__plugin_cmd__ = ["更新信息"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["更新信息"],
}
update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, block=True)
@update_info.handle()
async def _():
if img := image("update_info.png"):
await update_info.finish(img)
else:
await update_info.finish("目前没有更新信息哦")

28
bot.py
View File

@ -1,20 +1,26 @@
import nonebot
from nonebot.adapters.onebot.v11 import Adapter
from services.db_context import init, disconnect
# from nonebot.adapters.discord import Adapter as DiscordAdapter
from nonebot.adapters.dodo import Adapter as DoDoAdapter
from nonebot.adapters.kaiheila import Adapter as KaiheilaAdapter
from nonebot.adapters.onebot.v11 import Adapter as OneBotV11Adapter
from zhenxun.services.db_context import disconnect, init
nonebot.init()
driver = nonebot.get_driver()
driver.register_adapter(Adapter)
config = driver.config
driver.register_adapter(OneBotV11Adapter)
driver.register_adapter(KaiheilaAdapter)
driver.register_adapter(DoDoAdapter)
# driver.register_adapter(DiscordAdapter)
driver.on_startup(init)
driver.on_shutdown(disconnect)
# 优先加载定时任务
nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins")
nonebot.load_plugins("extensive_plugin")
# 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks")
nonebot.load_builtin_plugins("echo") # 内置插件
nonebot.load_plugins("zhenxun/builtin_plugins")
nonebot.load_plugins("zhenxun/plugins")
if __name__ == "__main__":

View File

@ -1,47 +0,0 @@
from pathlib import Path
import os
# 图片路径
IMAGE_PATH = Path() / "resources" / "image"
# 语音路径
RECORD_PATH = Path() / "resources" / "record"
# 文本路径
TEXT_PATH = Path() / "resources" / "text"
# 日志路径
LOG_PATH = Path() / "log"
# 字体路径
FONT_PATH = Path() / "resources" / "font"
# 数据路径
DATA_PATH = Path() / "data"
# 临时数据路径
TEMP_PATH = Path() / "resources" / "temp"
# 网页模板路径
TEMPLATE_PATH = Path() / "resources" / "template"
def load_path():
old_img_dir = Path() / "resources" / "img"
if not IMAGE_PATH.exists() and old_img_dir.exists():
os.rename(old_img_dir, IMAGE_PATH)
old_voice_dir = Path() / "resources" / "voice"
if not RECORD_PATH.exists() and old_voice_dir.exists():
os.rename(old_voice_dir, RECORD_PATH)
old_ttf_dir = Path() / "resources" / "ttf"
if not FONT_PATH.exists() and old_ttf_dir.exists():
os.rename(old_ttf_dir, FONT_PATH)
old_txt_dir = Path() / "resources" / "txt"
if not TEXT_PATH.exists() and old_txt_dir.exists():
os.rename(old_txt_dir, TEXT_PATH)
IMAGE_PATH.mkdir(parents=True, exist_ok=True)
RECORD_PATH.mkdir(parents=True, exist_ok=True)
TEXT_PATH.mkdir(parents=True, exist_ok=True)
LOG_PATH.mkdir(parents=True, exist_ok=True)
FONT_PATH.mkdir(parents=True, exist_ok=True)
DATA_PATH.mkdir(parents=True, exist_ok=True)
TEMP_PATH.mkdir(parents=True, exist_ok=True)
load_path()

BIN
docs_image/tt.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
docs_image/webui1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

BIN
docs_image/webui2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

BIN
docs_image/webui3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
docs_image/webui4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
docs_image/webui5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
docs_image/webui6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
docs_image/webui7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,173 +0,0 @@
from typing import Dict, Union
from tortoise import fields
from services.db_context import Model
from .goods_info import GoodsInfo
class BagUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.CharField(255)
"""群聊id"""
gold = fields.IntField(default=100)
"""金币数量"""
spend_total_gold = fields.IntField(default=0)
"""花费金币总数"""
get_total_gold = fields.IntField(default=0)
"""获取金币总数"""
get_today_gold = fields.IntField(default=0)
"""今日获取金币"""
spend_today_gold = fields.IntField(default=0)
"""今日获取金币"""
property: Dict[str, int] = fields.JSONField(default={})
"""道具"""
class Meta:
table = "bag_users"
table_description = "用户道具数据表"
unique_together = ("user_id", "group_id")
@classmethod
async def get_user_total_gold(cls, user_id: Union[int, str], group_id: Union[int, str]) -> str:
"""
说明:
获取金币概况
参数:
:param user_id: 用户id
:param group_id: 所在群组id
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
f"\n今日收益:{user.get_today_gold - user.spend_today_gold}"
f"\n总赚取金币:{user.get_total_gold}\n总花费金币:{user.spend_total_gold}"
)
@classmethod
async def get_gold(cls, user_id: Union[int, str], group_id: Union[int, str]) -> int:
"""
说明:
获取当前金币
参数:
:param user_id: 用户id
:param group_id: 所在群组id
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
return user.gold
@classmethod
async def get_property(
cls, user_id: Union[int, str], group_id: Union[int, str], only_active: bool = False
) -> Dict[str, int]:
"""
说明:
获取当前道具
参数:
:param user_id: 用户id
:param group_id: 所在群组id
:param only_active: 仅仅获取主动使用的道具
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
if only_active and user.property:
data = {}
name_list = [
x.goods_name
for x in await GoodsInfo.get_all_goods()
if not x.is_passive
]
for key in [x for x in user.property if x in name_list]:
data[key] = user.property[key]
return data
return user.property
@classmethod
async def add_gold(cls, user_id: Union[int, str], group_id: Union[int, str], num: int):
"""
说明:
增加金币
参数:
:param user_id: 用户id
:param group_id: 所在群组id
:param num: 金币数量
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
user.gold = user.gold + num
user.get_total_gold = user.get_total_gold + num
user.get_today_gold = user.get_today_gold + num
await user.save(update_fields=["gold", "get_today_gold", "get_total_gold"])
@classmethod
async def spend_gold(cls, user_id: Union[int, str], group_id: Union[int, str], num: int):
"""
说明:
花费金币
参数:
:param user_id: 用户id
:param group_id: 所在群组id
:param num: 金币数量
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
user.gold = user.gold - num
user.spend_total_gold = user.spend_total_gold + num
user.spend_today_gold = user.spend_today_gold + num
await user.save(update_fields=["gold", "spend_total_gold", "spend_today_gold"])
@classmethod
async def add_property(cls, user_id: Union[int, str], group_id: Union[int, str], name: str, num: int = 1):
"""
说明:
增加道具
参数:
:param user_id: 用户id
:param group_id: 所在群组id
:param name: 道具名称
:param num: 道具数量
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
property_ = user.property
if property_.get(name) is None:
property_[name] = 0
property_[name] += num
user.property = property_
await user.save(update_fields=["property"])
@classmethod
async def delete_property(
cls, user_id: Union[int, str], group_id: Union[int, str], name: str, num: int = 1
) -> bool:
"""
说明:
使用/删除 道具
参数:
:param user_id: 用户id
:param group_id: 所在群组id
:param name: 道具名称
:param num: 使用个数
"""
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
property_ = user.property
if name in property_:
if (n := property_.get(name, 0)) < num:
return False
if n == num:
del property_[name]
else:
property_[name] -= num
await user.save(update_fields=["property"])
return True
return False
@classmethod
async def _run_script(cls):
return ["ALTER TABLE bag_users DROP props;", # 删除 props 字段
"ALTER TABLE bag_users RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE bag_users ALTER COLUMN user_id TYPE character varying(255);",
# 将user_id字段类型改为character varying(255)
"ALTER TABLE bag_users ALTER COLUMN group_id TYPE character varying(255);"
]

View File

@ -1,131 +0,0 @@
import time
from typing import Union
from tortoise import fields
from services.db_context import Model
from services.log import logger
class BanUser(Model):
user_id = fields.CharField(255, pk=True)
"""用户id"""
ban_level = fields.IntField()
"""使用ban命令的用户等级"""
ban_time = fields.BigIntField()
"""ban开始的时间"""
duration = fields.BigIntField()
"""ban时长"""
class Meta:
table = "ban_users"
table_description = ".ban/b了 封禁人员数据表"
@classmethod
async def check_ban_level(cls, user_id: Union[int, str], level: int) -> bool:
"""
说明:
检测ban掉目标的用户与unban用户的权限等级大小
参数:
:param user_id: unban用户的用户id
:param level: ban掉目标用户的权限等级
"""
user = await cls.filter(user_id=str(user_id)).first()
if user:
logger.debug(
f"检测用户被ban等级user_level: {user.ban_level}level: {level}",
target=str(user_id),
)
return bool(user and user.ban_level > level)
return False
@classmethod
async def check_ban_time(cls, user_id: Union[int, str]) -> Union[str, int]:
"""
说明:
检测用户被ban时长
参数:
:param user_id: 用户id
"""
logger.debug(f"获取用户ban时长", target=str(user_id))
if user := await cls.filter(user_id=str(user_id)).first():
if (
time.time() - (user.ban_time + user.duration) > 0
and user.duration != -1
):
return ""
if user.duration == -1:
return ""
return int(time.time() - user.ban_time - user.duration)
return ""
@classmethod
async def is_ban(cls, user_id: Union[int, str]) -> bool:
"""
说明:
判断用户是否被ban
参数:
:param user_id: 用户id
"""
logger.debug(f"检测是否被ban", target=str(user_id))
if await cls.check_ban_time(str(user_id)):
return True
else:
await cls.unban(user_id)
return False
@classmethod
async def is_super_ban(cls, user_id: Union[int, str]) -> bool:
"""
说明:
判断用户是否被超级用户ban / b了
参数:
:param user_id: 用户id
"""
logger.debug(f"检测是否被超级用户权限封禁", target=str(user_id))
if user := await cls.filter(user_id=str(user_id)).first():
if user.ban_level == 10:
return True
return False
@classmethod
async def ban(cls, user_id: Union[int, str], ban_level: int, duration: int):
"""
说明:
ban掉目标用户
参数:
:param user_id: 目标用户id
:param ban_level: 使用ban命令用户的权限
:param duration: ban时长
"""
logger.debug(f"封禁用户,等级:{ban_level},时长: {duration}", target=str(user_id))
if await cls.filter(user_id=str(user_id)).first():
await cls.unban(user_id)
await cls.create(
user_id=str(user_id),
ban_level=ban_level,
ban_time=time.time(),
duration=duration,
)
@classmethod
async def unban(cls, user_id: Union[int, str]) -> bool:
"""
说明:
unban用户
参数:
:param user_id: 用户id
"""
if user := await cls.filter(user_id=str(user_id)).first():
logger.debug("解除封禁", target=str(user_id))
await user.delete()
return True
return False
@classmethod
async def _run_script(cls):
return ["ALTER TABLE ban_users RENAME COLUMN user_qq TO user_id;", # 将user_id改为user_id
"ALTER TABLE ban_users ALTER COLUMN user_id TYPE character varying(255);",
# 将user_id字段类型改为character varying(255)
]

View File

@ -1,65 +0,0 @@
from tortoise import fields
from typing import Union
from configs.config import Config
from services.db_context import Model
class FriendUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255, unique=True)
"""用户id"""
user_name = fields.CharField(max_length=255, default="")
"""用户名称"""
nickname = fields.CharField(max_length=255, null=True)
"""私聊下自定义昵称"""
class Meta:
table = "friend_users"
table_description = "好友信息数据表"
@classmethod
async def get_user_name(cls, user_id: Union[int, str]) -> str:
"""
说明:
获取好友用户名称
参数:
:param user_id: 用户id
"""
if user := await cls.get_or_none(user_id=str(user_id)):
return user.user_name
return ""
@classmethod
async def get_user_nickname(cls, user_id: Union[int, str]) -> str:
"""
说明:
获取用户昵称
参数:
:param user_id: 用户id
"""
if user := await cls.get_or_none(user_id=str(user_id)):
if user.nickname:
_tmp = ""
if black_word := Config.get_config("nickname", "BLACK_WORD"):
for x in user.nickname:
_tmp += "*" if x in black_word else x
return _tmp
return ""
@classmethod
async def set_user_nickname(cls, user_id: Union[int, str], nickname: str):
"""
说明:
设置用户昵称
参数:
:param user_id: 用户id
:param nickname: 昵称
"""
await cls.update_or_create(user_id=str(user_id), defaults={"nickname": nickname})
@classmethod
async def _run_script(cls):
await cls.raw("ALTER TABLE friend_users ALTER COLUMN user_id TYPE character varying(255);")
# 将user_id字段类型改为character varying(255))

View File

@ -1,205 +0,0 @@
from typing import Dict, List, Optional, Tuple, Union
from tortoise import fields
from services.db_context import Model
class GoodsInfo(Model):
__tablename__ = "goods_info"
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
goods_name = fields.CharField(255, unique=True)
"""商品名称"""
goods_price = fields.IntField()
"""价格"""
goods_description = fields.TextField()
"""描述"""
goods_discount = fields.FloatField(default=1)
"""折扣"""
goods_limit_time = fields.BigIntField(default=0)
"""限时"""
daily_limit = fields.IntField(default=0)
"""每日限购"""
daily_purchase_limit: Dict[str, Dict[str, int]] = fields.JSONField(default={})
"""用户限购记录"""
is_passive = fields.BooleanField(default=False)
"""是否为被动道具"""
icon = fields.TextField(null=True)
"""图标路径"""
class Meta:
table = "goods_info"
table_description = "商品数据表"
@classmethod
async def add_goods(
cls,
goods_name: str,
goods_price: int,
goods_description: str,
goods_discount: float = 1,
goods_limit_time: int = 0,
daily_limit: int = 0,
is_passive: bool = False,
icon: Optional[str] = None,
):
"""
说明:
添加商品
参数:
:param goods_name: 商品名称
:param goods_price: 商品价格
:param goods_description: 商品简介
:param goods_discount: 商品折扣
:param goods_limit_time: 商品限时
:param daily_limit: 每日购买限制
:param is_passive: 是否为被动道具
:param icon: 图标
"""
if not await cls.filter(goods_name=goods_name).first():
await cls.create(
goods_name=goods_name,
goods_price=goods_price,
goods_description=goods_description,
goods_discount=goods_discount,
goods_limit_time=goods_limit_time,
daily_limit=daily_limit,
is_passive=is_passive,
icon=icon,
)
@classmethod
async def delete_goods(cls, goods_name: str) -> bool:
"""
说明:
删除商品
参数:
:param goods_name: 商品名称
"""
if goods := await cls.get_or_none(goods_name=goods_name):
await goods.delete()
return True
return False
@classmethod
async def update_goods(
cls,
goods_name: str,
goods_price: Optional[int] = None,
goods_description: Optional[str] = None,
goods_discount: Optional[float] = None,
goods_limit_time: Optional[int] = None,
daily_limit: Optional[int] = None,
is_passive: Optional[bool] = None,
icon: Optional[str] = None,
):
"""
说明:
更新商品信息
参数:
:param goods_name: 商品名称
:param goods_price: 商品价格
:param goods_description: 商品简介
:param goods_discount: 商品折扣
:param goods_limit_time: 商品限时时间
:param daily_limit: 每日次数限制
:param is_passive: 是否为被动
:param icon: 图标
"""
if goods := await cls.get_or_none(goods_name=goods_name):
await cls.update_or_create(
goods_name=goods_name,
defaults={
"goods_price": goods_price or goods.goods_price,
"goods_description": goods_description or goods.goods_description,
"goods_discount": goods_discount or goods.goods_discount,
"goods_limit_time": goods_limit_time
if goods_limit_time is not None
else goods.goods_limit_time,
"daily_limit": daily_limit
if daily_limit is not None
else goods.daily_limit,
"is_passive": is_passive
if is_passive is not None
else goods.is_passive,
"icon": icon or goods.icon,
},
)
@classmethod
async def get_all_goods(cls) -> List["GoodsInfo"]:
"""
说明:
获得全部有序商品对象
"""
query = await cls.all()
id_lst = [x.id for x in query]
goods_lst = []
for _ in range(len(query)):
min_id = min(id_lst)
goods_lst.append([x for x in query if x.id == min_id][0])
id_lst.remove(min_id)
return goods_lst
@classmethod
async def add_user_daily_purchase(
cls, goods: "GoodsInfo", user_id_: Union[int, str], group_id_: Union[int, str], num: int = 1
):
"""
说明:
添加用户明日购买限制
参数:
:param goods: 商品
:param user_id: 用户id
:param group_id: 群号
:param num: 数量
"""
user_id = str(user_id_)
group_id = str(group_id_)
if goods and goods.daily_limit and goods.daily_limit > 0:
if not goods.daily_purchase_limit.get(group_id):
goods.daily_purchase_limit[group_id] = {}
if not goods.daily_purchase_limit[group_id].get(user_id):
goods.daily_purchase_limit[group_id][user_id] = 0
goods.daily_purchase_limit[group_id][user_id] += num
await goods.save(update_fields=["daily_purchase_limit"])
@classmethod
async def check_user_daily_purchase(
cls, goods: "GoodsInfo", user_id_: Union[int, str], group_id_: Union[int, str], num: int = 1
) -> Tuple[bool, int]:
"""
说明:
检测用户每日购买上限
参数:
:param goods: 商品
:param user_id: 用户id
:param group_id: 群号
:param num: 数量
"""
user_id = str(user_id_)
group_id = str(group_id_)
if goods and goods.daily_limit > 0:
if (
not goods.daily_limit
or not goods.daily_purchase_limit.get(group_id)
or not goods.daily_purchase_limit[group_id].get(user_id)
):
return goods.daily_limit - num < 0, goods.daily_limit
if goods.daily_purchase_limit[group_id][user_id] + num > goods.daily_limit:
return (
True,
goods.daily_limit - goods.daily_purchase_limit[group_id][user_id],
)
return False, 0
@classmethod
def _run_script(cls):
return [
"ALTER TABLE goods_info ADD daily_limit Integer DEFAULT 0;",
"ALTER TABLE goods_info ADD daily_purchase_limit Json DEFAULT '{}';",
"ALTER TABLE goods_info ADD is_passive boolean DEFAULT False;",
"ALTER TABLE goods_info ADD icon VARCHAR(255);",
]

View File

@ -1,31 +0,0 @@
from typing import List, Optional
from tortoise import fields
from services.db_context import Model
from services.log import logger
class GroupInfo(Model):
group_id = fields.CharField(255, pk=True)
"""群聊id"""
group_name = fields.TextField(default="")
"""群聊名称"""
max_member_count = fields.IntField(default=0)
"""最大人数"""
member_count = fields.IntField(default=0)
"""当前人数"""
group_flag: int = fields.IntField(default=0)
"""群认证标记"""
class Meta:
table = "group_info"
table_description = "群聊信息表"
@classmethod
def _run_script(cls):
return ["ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;", # group_info表添加一个group_flag
"ALTER TABLE group_info ALTER COLUMN group_id TYPE character varying(255);"
# 将group_id字段类型改为character varying(255)
]

View File

@ -1,122 +0,0 @@
from datetime import datetime
from typing import List, Optional, Set, Union
from tortoise import fields
from configs.config import Config
from services.db_context import Model
from services.log import logger
class GroupInfoUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
"""用户id"""
user_name = fields.CharField(255, default="")
"""用户昵称"""
group_id = fields.CharField(255)
"""群聊id"""
user_join_time: datetime = fields.DatetimeField(null=True)
"""用户入群时间"""
nickname = fields.CharField(255, null=True)
"""群聊昵称"""
uid = fields.BigIntField(null=True)
"""用户uid"""
class Meta:
table = "group_info_users"
table_description = "群员信息数据表"
unique_together = ("user_id", "group_id")
@classmethod
async def get_group_member_id_list(cls, group_id: Union[int, str]) -> Set[int]:
"""
说明:
获取该群所有用户id
参数:
:param group_id: 群号
"""
return set(
await cls.filter(group_id=str(group_id)).values_list("user_id", flat=True)
) # type: ignore
@classmethod
async def set_user_nickname(cls, user_id: Union[int, str], group_id: Union[int, str], nickname: str):
"""
说明:
设置群员在该群内的昵称
参数:
:param user_id: 用户id
:param group_id: 群号
:param nickname: 昵称
"""
await cls.update_or_create(
user_id=str(user_id),
group_id=str(group_id),
defaults={"nickname": nickname},
)
@classmethod
async def get_user_all_group(cls, user_id: Union[int, str]) -> List[int]:
"""
说明:
获取该用户所在的所有群聊
参数:
:param user_id: 用户id
"""
return list(
await cls.filter(user_id=str(user_id)).values_list("group_id", flat=True)
) # type: ignore
@classmethod
async def get_user_nickname(cls, user_id: Union[int, str], group_id: Union[int, str]) -> str:
"""
说明:
获取用户在该群的昵称
参数:
:param user_id: 用户id
:param group_id: 群号
"""
if user := await cls.get_or_none(user_id=str(user_id), group_id=str(group_id)):
if user.nickname:
nickname = ""
if black_word := Config.get_config("nickname", "BLACK_WORD"):
for x in user.nickname:
nickname += "*" if x in black_word else x
return nickname
return user.nickname
return ""
@classmethod
async def get_group_member_uid(cls, user_id: Union[int, str], group_id: Union[int, str]) -> Optional[int]:
logger.debug(
f"GroupInfoUser 尝试获取 用户[<u><e>{user_id}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID"
)
user, _ = await cls.get_or_create(user_id=str(user_id), group_id=str(group_id))
_max_uid_user, _ = await cls.get_or_create(user_id="114514", group_id="114514")
_max_uid = _max_uid_user.uid
if not user.uid:
all_user = await cls.filter(user_id=str(user_id)).all()
for x in all_user:
if x.uid:
return x.uid
user.uid = _max_uid + 1
_max_uid_user.uid = _max_uid + 1
await cls.bulk_update([user, _max_uid_user], ["uid"])
logger.debug(
f"GroupInfoUser 获取 用户[<u><e>{user_id}</e></u>] 群聊[<u><e>{group_id}</e></u>] UID: {user.uid}"
)
return user.uid
@classmethod
async def _run_script(cls):
return [
"alter table group_info_users alter user_join_time drop not null;", # 允许 user_join_time 为空
"ALTER TABLE group_info_users ALTER COLUMN user_join_time TYPE timestamp with time zone USING user_join_time::timestamp with time zone;",
"ALTER TABLE group_info_users RENAME COLUMN user_qq TO user_id;", # 将user_id改为user_id
"ALTER TABLE group_info_users ALTER COLUMN user_id TYPE character varying(255);",
# 将user_id字段类型改为character varying(255)
"ALTER TABLE group_info_users ALTER COLUMN group_id TYPE character varying(255);"
]

View File

@ -1,108 +0,0 @@
from tortoise import fields
from services.db_context import Model
from typing import Union
class LevelUser(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.CharField(255)
"""群聊id"""
user_level = fields.BigIntField()
"""用户权限等级"""
group_flag = fields.IntField(default=0)
"""特殊标记,是否随群管理员变更而设置权限"""
class Meta:
table = "level_users"
table_description = "用户权限数据库"
unique_together = ("user_id", "group_id")
@classmethod
async def get_user_level(cls, user_id: Union[int, str], group_id: Union[int, str]) -> int:
"""
说明:
获取用户在群内的等级
参数:
:param user_id: 用户id
:param group_id: 群组id
"""
if user := await cls.get_or_none(user_id=str(user_id), group_id=str(group_id)):
return user.user_level
return -1
@classmethod
async def set_level(
cls, user_id: Union[int, str], group_id: Union[int, str], level: int, group_flag: int = 0
):
"""
说明:
设置用户在群内的权限
参数:
:param user_id: 用户id
:param group_id: 群组id
:param level: 权限等级
:param group_flag: 是否被自动更新刷新权限 01
"""
await cls.update_or_create(
user_id=str(user_id),
group_id=str(group_id),
defaults={"user_level": level, "group_flag": group_flag},
)
@classmethod
async def delete_level(cls, user_id: Union[int, str], group_id: Union[int, str]) -> bool:
"""
说明:
删除用户权限
参数:
:param user_id: 用户id
:param group_id: 群组id
"""
if user := await cls.get_or_none(user_id=str(user_id), group_id=str(group_id)):
await user.delete()
return True
return False
@classmethod
async def check_level(cls, user_id: Union[int, str], group_id: Union[int, str], level: int) -> bool:
"""
说明:
检查用户权限等级是否大于 level
参数:
:param user_id: 用户id
:param group_id: 群组id
:param level: 权限等级
"""
if group_id:
if user := await cls.get_or_none(user_id=str(user_id), group_id=str(group_id)):
return user.user_level >= level
else:
user_list = await cls.filter(user_id=str(user_id)).all()
user = max(user_list, key=lambda x: x.user_level)
return user.user_level >= level
return False
@classmethod
async def is_group_flag(cls, user_id: Union[int, str], group_id: Union[int, str]) -> bool:
"""
说明:
检测是否会被自动更新刷新权限
参数:
:param user_id: 用户id
:param group_id: 群组id
"""
if user := await cls.get_or_none(user_id=str(user_id), group_id=str(group_id)):
return user.group_flag == 1
return False
@classmethod
async def _run_script(cls):
return ["ALTER TABLE level_users RENAME COLUMN user_qq TO user_id;", # 将user_id改为user_id
"ALTER TABLE level_users ALTER COLUMN user_id TYPE character varying(255);",
# 将user_id字段类型改为character varying(255)
"ALTER TABLE level_users ALTER COLUMN group_id TYPE character varying(255);"
]

View File

@ -1,38 +0,0 @@
from datetime import datetime
from tortoise import fields
from services.db_context import Model
class UserShopGoldLog(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
user_id = fields.CharField(255)
"""用户id"""
group_id = fields.CharField(255)
"""群聊id"""
type = fields.IntField()
"""金币使用类型 0: 购买, 1: 使用, 2: 插件"""
name = fields.CharField(255)
"""商品/插件 名称"""
spend_gold = fields.IntField(default=0)
"""花费金币"""
num = fields.IntField()
"""数量"""
create_time = fields.DatetimeField(auto_now_add=True)
"""创建时间"""
class Meta:
table = "user_shop_gold_log"
table_description = "金币使用日志表"
@classmethod
def _run_script(cls):
return [
"ALTER TABLE user_shop_gold_log RENAME COLUMN user_qq TO user_id;", # 将user_qq改为user_id
"ALTER TABLE user_shop_gold_log ALTER COLUMN user_id TYPE character varying(255);",
# 将user_id字段类型改为character varying(255)
"ALTER TABLE user_shop_gold_log ALTER COLUMN group_id TYPE character varying(255);",
]

View File

@ -1,43 +0,0 @@
from nonebot import on_regex
from nonebot.rule import to_me
from pathlib import Path
__zx_plugin_name__ = "关于"
__plugin_usage__ = """
usage
想要更加了解真寻吗
指令
关于
""".strip()
__plugin_des__ = "想要更加了解真寻吗"
__plugin_cmd__ = ["关于"]
__plugin_version__ = 0.1
__plugin_type__ = ("其他",)
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 1,
"default_status": True,
"limit_superuser": False,
"cmd": ["关于"],
}
about = on_regex("^关于$", priority=5, block=True, rule=to_me())
@about.handle()
async def _():
ver_file = Path() / '__version__'
version = None
if ver_file.exists():
with open(ver_file, 'r', encoding='utf8') as f:
version = f.read().split(':')[-1].strip()
msg = f"""
绪山真寻Bot
版本{version}
简介基于Nonebot2与go-cqhttp开发是一个非常可爱的Bot呀希望与大家要好好相处
项目地址https://github.com/HibiKier/zhenxun_bot
文档地址https://hibikier.github.io/zhenxun_bot/
""".strip()
await about.send(msg)

View File

@ -1,60 +0,0 @@
import os
import random
from nonebot import on_command, on_keyword
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.adapters.onebot.v11.permission import GROUP
from nonebot.rule import to_me
from configs.config import NICKNAME
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import FreqLimiter
__zx_plugin_name__ = "基本设置 [Hidden]"
__plugin_usage__ = "用法: 无"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
_flmt = FreqLimiter(300)
config_play_game = on_keyword({"打游戏"}, permission=GROUP, priority=1, block=True)
@config_play_game.handle()
async def _(event: GroupMessageEvent):
if not _flmt.check(event.group_id):
return
_flmt.start_cd(event.group_id)
await config_play_game.finish(
image(IMAGE_PATH / random.choice(os.listdir(IMAGE_PATH / "dayouxi")))
)
self_introduction = on_command(
"自我介绍", aliases={"介绍", "你是谁", "你叫什么"}, rule=to_me(), priority=5, block=True
)
@self_introduction.handle()
async def _():
if NICKNAME.find("真寻") != -1:
result = (
"我叫绪山真寻\n"
"你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n"
"年龄的话我还是个**岁初中生(至少现在是)\n"
"身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n"
"我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n"
"最好的朋友是椛!\n" + image("zhenxun.jpg")
)
await self_introduction.finish(result)
my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True)
@my_wife.handle()
async def _():
await my_wife.finish(image(IMAGE_PATH / "other" / "laopo.jpg"))

View File

@ -1,87 +0,0 @@
from typing import List
from nonebot import on_message
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.rule import to_me
from configs.config import NICKNAME, Config
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.utils import get_message_img, get_message_text
from .data_source import get_chat_result, hello, no_result
__zx_plugin_name__ = "AI"
__plugin_usage__ = f"""
usage
{NICKNAME}普普通通的对话吧
"""
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["Ai", "ai", "AI", "aI"],
}
__plugin_configs__ = {
"TL_KEY": {"value": [], "help": "图灵Key", "type": List[str]},
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False,
"type": bool,
},
"TEXT_FILTER": {
"value": ["", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
"type": List[str],
},
}
Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
)
ai = on_message(rule=to_me(), priority=998)
@ai.handle()
async def _(bot: Bot, event: MessageEvent):
msg = get_message_text(event.json())
img = get_message_img(event.json())
if "CQ:xml" in str(event.get_message()):
return
# 打招呼
if (not msg and not img) or msg in [
"你好啊",
"你好",
"在吗",
"在不在",
"您好",
"您好啊",
"你好",
"",
]:
await ai.finish(hello())
img = img[0] if img else ""
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_user_nickname(event.user_id, event.group_id)
else:
nickname = await FriendUser.get_user_nickname(event.user_id)
if not nickname:
if isinstance(event, GroupMessageEvent):
nickname = event.sender.card or event.sender.nickname
else:
nickname = event.sender.nickname
result = await get_chat_result(msg, img, event.user_id, nickname)
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else ''} "
f"问题:{msg} ---- 回答:{result}"
)
if result:
result = str(result)
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result))
else:
await ai.finish(no_result())

View File

@ -1,11 +0,0 @@
from pathlib import Path
import nonebot
from configs.config import Config
Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
)
nonebot.load_plugins(str(Path(__file__).parent.resolve()))

View File

@ -1,45 +0,0 @@
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from ._data_source import get_data
from services.log import logger
__zx_plugin_name__ = "网易云热评"
__plugin_usage__ = """
usage
到点了还是防不了下塔
指令
网易云热评/到点了/12点了
""".strip()
__plugin_des__ = "生了个人,我很抱歉"
__plugin_cmd__ = ["网易云热评", "到点了", "12点了"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["网易云热评", "网易云评论", "到点了", "12点了"],
}
comments_163 = on_regex(
"^(网易云热评|网易云评论|到点了|12点了)$", priority=5, block=True
)
comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle()
async def _(event: MessageEvent):
data, code = await get_data(comments_163_url)
if code != 200:
await comments_163.finish(data, at_sender=True)
data = data["data"]
comment = data["comment_content"]
song_name = data["title"]
await comments_163.send(f"{comment}\n\t——《{song_name}")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送网易云热评: {comment} \n\t\t————{song_name}"
)

View File

@ -1,47 +0,0 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent
from utils.message_builder import image
from nonebot.params import CommandArg
from ._data_source import get_data
from services.log import logger
__zx_plugin_name__ = "b封面"
__plugin_usage__ = """
usage
b封面 [链接/av/bv/cv/直播id]
示例b封面 av86863038
""".strip()
__plugin_des__ = "快捷的b站视频封面获取方式"
__plugin_cmd__ = ["b封面/B封面"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["b封面", "B封面"],
}
cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True)
cover_url = "https://v2.alapi.cn/api/bilibili/cover"
@cover.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip()
params = {"c": msg}
data, code = await get_data(cover_url, params)
if code != 200:
await cover.finish(data, at_sender=True)
data = data["data"]
title = data["title"]
img = data["cover"]
await cover.send(Message(f"title{title}\n{image(img)}"))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 获取b站封面: {title} url{img}"
)

View File

@ -1,45 +0,0 @@
from nonebot import on_regex
from services.log import logger
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from ._data_source import get_data
__zx_plugin_name__ = "鸡汤"
__plugin_usage__ = """
usage
不喝点什么感觉有点不舒服
指令
鸡汤
""".strip()
__plugin_des__ = "喏,亲手为你煮的鸡汤"
__plugin_cmd__ = ["鸡汤"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["鸡汤", "毒鸡汤"],
}
url = "https://v2.alapi.cn/api/soul"
jitang = on_regex("^毒?鸡汤$", priority=5, block=True)
@jitang.handle()
async def _(event: MessageEvent):
try:
data, code = await get_data(url)
if code != 200:
await jitang.finish(data, at_sender=True)
await jitang.send(data["data"]["content"])
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送鸡汤:" + data["data"]["content"]
)
except Exception as e:
await jitang.send("鸡汤煮坏掉了...")
logger.error(f"鸡汤煮坏掉了 {type(e)}{e}")

View File

@ -1,41 +0,0 @@
from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from services.log import logger
from ._data_source import get_data
__zx_plugin_name__ = "古诗"
__plugin_usage__ = """usage
平白无故念首诗
示例念诗/来首诗/念首诗
"""
__plugin_des__ = "为什么突然文艺起来了!"
__plugin_cmd__ = ["念诗/来首诗/念首诗"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["念诗", "来首诗", "念首诗"],
}
poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True)
poetry_url = "https://v2.alapi.cn/api/shici"
@poetry.handle()
async def _(event: MessageEvent):
data, code = await get_data(poetry_url)
if code != 200:
await poetry.finish(data, at_sender=True)
data = data["data"]
content = data["content"]
title = data["origin"]
author = data["author"]
await poetry.send(f"{content}\n\t——{author}{title}")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送古诗: f'{content}\n\t--{author}{title}'"
)

View File

@ -1,307 +0,0 @@
from typing import Any, Optional, Tuple
import nonebot
from nonebot import Driver, on_command, on_regex
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import ArgStr, CommandArg, RegexGroup
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
from utils.utils import get_bot, is_number, scheduler
from .data_source import (
BilibiliSub,
SubManager,
add_live_sub,
add_season_sub,
add_up_sub,
delete_sub,
get_media_id,
get_sub_status,
)
__zx_plugin_name__ = "B站订阅"
__plugin_usage__ = """
usage
B站直播番剧UP动态开播等提醒
主播订阅相当于 直播间订阅 + UP订阅
指令[示例Id乱打的仅做示例]
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
删除订阅 ['主播'/'UP'/'id'] [id]
查看订阅
示例添加订阅主播 2345344 <-(直播房间id)
示例添加订阅UP 2355543 <-(个人主页id)
示例添加订阅番剧 史莱姆 <-(支持模糊搜索)
示例添加订阅番剧 125344 <-(番剧id)
示例删除订阅id 2324344 <-(任意id通过查看订阅获取)
""".strip()
__plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 ['主播'/'UP'/'id'] [id]", "查看订阅"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier & NumberSir"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["B站订阅", "b站订阅", "添加订阅", "删除订阅", "查看订阅"],
}
__plugin_configs__ = {
"GROUP_BILIBILI_SUB_LEVEL": {
"value": 5,
"help": "群内bilibili订阅需要管理的权限",
"default_value": 5,
"type": int,
},
"LIVE_MSG_AT_ALL": {
"value": False,
"help": "直播提醒是否AT全体仅在真寻是管理员时生效",
"default_value": False,
"type": bool,
},
"UP_MSG_AT_ALL": {
"value": False,
"help": "UP动态投稿提醒是否AT全体仅在真寻是管理员时生效",
"default_value": False,
"type": bool,
},
"DOWNLOAD_DYNAMIC_IMAGE": {
"value": True,
"help": "下载动态中的图片并发送在提醒消息中",
"default_value": True,
"type": bool,
},
}
add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_regex("^查看订阅$", priority=5, block=True)
driver: Driver = nonebot.get_driver()
sub_manager: SubManager
@driver.on_startup
async def _():
global sub_manager
sub_manager = SubManager()
@add_sub.handle()
@del_sub.handle()
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("参数不完全,请查看订阅帮助...")
sub_type = msg[0]
id_ = ""
if isinstance(event, GroupMessageEvent):
if not await LevelUser.check_level(
event.user_id,
event.group_id,
sub_level, # type: ignore
):
await add_sub.finish(
f"您的权限不足,群内订阅的需要 {sub_level} 级权限..",
at_sender=True,
)
sub_user = f"{event.user_id}:{event.group_id}"
else:
sub_user = f"{event.user_id}"
state["sub_type"] = sub_type
state["sub_user"] = sub_user
if len(msg) > 1:
if "http" in msg[1]:
msg[1] = msg[1].split("?")[0]
msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1]
msg[1] = msg[1].split("/")[-1]
id_ = msg[1][2:] if msg[1].startswith("md") else msg[1]
if not is_number(id_):
if sub_type in ["season", "动漫", "番剧"]:
rst = "*以为您找到以下番剧请输入Id选择*\n"
state["season_data"] = await get_media_id(id_)
if len(state["season_data"]) == 0: # type: ignore
await add_sub.finish(f"未找到番剧:{msg}")
for i, x in enumerate(state["season_data"]): # type: ignore
rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n' # type: ignore
await add_sub.send("\n".join(rst.split("\n")[:-1]))
else:
await add_sub.finish("Id 必须为全数字!")
else:
state["id"] = int(id_)
@add_sub.got("sub_type")
@add_sub.got("sub_user")
@add_sub.got("id")
async def _(
event: MessageEvent,
state: T_State,
id_: str = ArgStr("id"),
sub_type: str = ArgStr("sub_type"),
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["season", "动漫", "番剧"] and state.get("season_data"):
season_data = state["season_data"]
if not is_number(id_) or int(id_) < 1 or int(id_) > len(season_data):
await add_sub.reject_arg("id", "Id必须为数字且在范围内请重新输入...")
id_ = season_data[int(id_) - 1]["media_id"]
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
await add_sub.send(await add_up_sub(id_, sub_user))
elif sub_type in ["season", "动漫", "番剧"]:
await add_sub.send(await add_season_sub(id_, sub_user))
else:
await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!")
logger.info(
f"添加订阅:{sub_type} -> {sub_user} -> {id_}",
"添加订阅",
event.user_id,
getattr(event, "group_id", None),
)
@del_sub.got("sub_type")
@del_sub.got("sub_user")
@del_sub.got("id")
async def _(
event: MessageEvent,
id_: str = ArgStr("id"),
sub_type: str = ArgStr("sub_type"),
sub_user: str = ArgStr("sub_user"),
):
if sub_type in ["主播", "直播"]:
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user, "live")
elif sub_type.lower() in ["up", "用户"]:
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user, "up")
else:
result = await BilibiliSub.delete_bilibili_sub(id_, sub_user)
if result:
await del_sub.send(f"删除订阅id{id_} 成功...")
logger.info(
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()
async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent):
id_ = f"{event.group_id}"
else:
id_ = f"{event.user_id}"
data = await BilibiliSub.filter(sub_users__contains=id_).all()
live_rst = ""
up_rst = ""
season_rst = ""
for x in data:
if x.sub_type == "live":
live_rst += (
f"\t直播间id{x.sub_id}\n" f"\t名称:{x.uname}\n" f"------------------\n"
)
if x.sub_type == "up":
up_rst += f"\tUP{x.uname}\n" f"\tuid{x.uid}\n" f"------------------\n"
if x.sub_type == "season":
season_rst += (
f"\t番剧id{x.sub_id}\n"
f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n"
)
live_rst = "当前订阅的直播:\n" + live_rst if live_rst else live_rst
up_rst = "当前订阅的UP\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(
image(
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
)
)
# 推送
@scheduler.scheduled_job(
"interval",
seconds=30,
)
async def _():
bot = get_bot()
sub = None
if bot:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
try:
logger.debug(f"Bilibili订阅开始检测{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot) # type: ignore
if sub.sub_type == "live":
rst += "\n" + await get_sub_status(sub.uid, sub.sub_id, "up")
await send_sub_msg(rst, 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):
"""
推送信息
:param rst: 回复
:param sub: BilibiliSub
:param bot: Bot
"""
temp_group = []
if rst and rst.strip():
for x in sub.sub_users.split(",")[:-1]:
try:
if ":" in x and x.split(":")[1] not in temp_group:
group_id = int(x.split(":")[1])
temp_group.append(group_id)
if (
await bot.get_group_member_info(
group_id=group_id, user_id=int(bot.self_id), no_cache=True
)
)["role"] in ["owner", "admin"]:
if (
sub.sub_type == "live"
and Config.get_config("bilibili_sub", "LIVE_MSG_AT_ALL")
) or (
sub.sub_type == "up"
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
):
rst = "[CQ:at,qq=all]\n" + rst
if group_manager.get_plugin_status("bilibili_sub", group_id):
await bot.send_group_msg(
group_id=group_id, message=Message(rst)
)
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}", e=e)

View File

@ -1,451 +0,0 @@
import random
from asyncio.exceptions import TimeoutError
from datetime import datetime
from typing import Optional, Tuple, Union
# from .utils import get_videos
from bilireq import dynamic
from bilireq.exceptions import ResponseCodeError
from bilireq.grpc.dynamic import grpc_get_user_dynamics
from bilireq.grpc.protos.bilibili.app.dynamic.v2.dynamic_pb2 import DynamicType
from bilireq.live import get_room_info_by_id
from bilireq.user import get_videos
from nonebot.adapters.onebot.v11 import Message, MessageSegment
from configs.config import Config
from configs.path_config import IMAGE_PATH, TEMP_PATH
from services.log import logger
from utils.browser import get_browser
from utils.http_utils import AsyncHttpx, AsyncPlaywright
from utils.manager import resources_manager
from utils.message_builder import image
from utils.utils import get_bot, get_local_proxy
from .model import BilibiliSub
from .utils import get_meta, get_user_card
SEARCH_URL = "https://api.bilibili.com/x/web-interface/search/all/v2"
DYNAMIC_PATH = IMAGE_PATH / "bilibili_sub" / "dynamic"
DYNAMIC_PATH.mkdir(exist_ok=True, parents=True)
TYPE2MSG = {
0: "发布了新动态",
DynamicType.forward: "转发了一条动态",
DynamicType.word: "发布了新文字动态",
DynamicType.draw: "发布了新图文动态",
DynamicType.av: "发布了新投稿",
DynamicType.article: "发布了新专栏",
DynamicType.music: "发布了新音频",
}
resources_manager.add_temp_dir(DYNAMIC_PATH)
async def add_live_sub(live_id: str, sub_user: str) -> str:
"""
添加直播订阅
:param live_id: 直播房间号
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
: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 = str(live_info["uid"])
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
try:
user_info = await get_user_card(uid)
except ResponseCodeError:
return f"未找到UpId{uid} 的信息请检查Id是否正确"
uname = user_info["name"]
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"]["dynamic_id"]
if await BilibiliSub.sub_handle(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
):
if data := await BilibiliSub.get_or_none(sub_id=room_id):
uname = data.uname
return (
"已成功订阅主播:\n"
f"\ttitle{title}\n"
f"\tname {uname}\n"
f"\tlive_id{room_id}\n"
f"\tuid{uid}"
)
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id: {live_id} 错误", e=e)
return "添加订阅失败..."
async def add_up_sub(uid: str, sub_user: str) -> str:
"""
添加订阅 UP
: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)
except ResponseCodeError:
return f"未找到UpId{uid} 的信息请检查Id是否正确"
uname = user_info["name"]
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
dynamic_info = await dynamic.get_user_dynamics(int(uid))
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["dynamic_id"]
except Exception as e:
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:
"""
添加订阅 UP
:param media_id: 番剧 media_id
: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)
except ResponseCodeError:
return f"未找到media_id{media_id} 的信息请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.sub_handle(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id: {media_id} 错误", e=e)
return "添加订阅失败..."
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(sub_id, sub_user):
return f"已成功取消订阅:{sub_id}"
else:
return f"取消订阅:{sub_id} 失败请检查是否订阅过该Id...."
async def get_media_id(keyword: str) -> Optional[dict]:
"""
获取番剧的 media_id
:param keyword: 番剧名称
"""
params = {"keyword": keyword}
for _ in range(3):
try:
_season_data = {}
response = await AsyncHttpx.get(SEARCH_URL, params=params, timeout=5)
if response.status_code == 200:
data = response.json()
if data.get("data"):
for item in data["data"]["result"]:
if item["result_type"] == "media_bangumi":
idx = 0
for x in item["data"]:
_season_data[idx] = {
"media_id": x["media_id"],
"title": x["title"]
.replace('<em class="keyword">', "")
.replace("</em>", ""),
}
idx += 1
return _season_data
except TimeoutError:
pass
return {}
async def get_sub_status(id_: str, sub_id: str, sub_type: str) -> Union[Message, str]:
"""
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
"""
try:
if sub_type == "live":
return await _get_live_status(id_)
elif sub_type == "up":
return await _get_up_status(id_, sub_id)
elif sub_type == "season":
return await _get_season_status(id_)
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_}", e=e)
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
return ""
async def _get_live_status(id_: str) -> str:
"""
获取直播订阅状态
:param id_: 直播间 id
"""
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = await get_room_info_by_id(id_)
title = live_info["title"]
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["user_cover"]
if sub := await BilibiliSub.get_or_none(sub_id=id_):
if sub.live_status != live_status:
await BilibiliSub.sub_handle(id_, live_status=live_status)
if sub.live_status in [0, 2] and live_status == 1:
return (
f""
f"{image(cover)}\n"
f"{sub.uname} 开播啦!\n"
f"标题:{title}\n"
f"直链https://live.bilibili.com/{room_id}"
)
return ""
async def _get_up_status(
id_: str, live_id: Optional[str] = None
) -> Union[Message, str]:
"""获取up动态
参数:
id_: up的id
live_id: 直播间id当订阅直播间时才有.
返回:
Union[Message, str]: 消息
"""
rst = ""
if _user := await BilibiliSub.get_or_none(sub_id=live_id or id_):
dynamics = None
dynamic = None
uname = ""
try:
dynamics = (
await grpc_get_user_dynamics(int(id_), proxy=get_local_proxy())
).list
except Exception as e:
logger.error("获取动态失败...", target=id_, e=e)
if dynamics:
uname = dynamics[0].modules[0].module_author.author.name
for dyn in dynamics:
if int(dyn.extend.dyn_id_str) > _user.dynamic_upload_time:
dynamic = dyn
break
if not dynamic:
logger.debug(f"{_user.sub_type}:{id_} 未有任何动态, 已跳过....")
return ""
if _user.uname != uname:
await BilibiliSub.sub_handle(live_id or id_, uname=uname)
dynamic_img, link = await get_user_dynamic(dynamic.extend.dyn_id_str, _user)
if not dynamic_img:
logger.debug(f"{id_} 未发布新动态或截图失败, 已跳过....")
return ""
await BilibiliSub.sub_handle(
live_id or id_, dynamic_upload_time=int(dynamic.extend.dyn_id_str)
)
rst += (
f"{uname} {TYPE2MSG.get(dynamic.card_type, TYPE2MSG[0])}\n"
+ dynamic_img
+ f"\n{link}\n"
)
video_info = ""
if video_list := [
module
for module in dynamic.modules
if str(module.module_dynamic.dyn_archive)
]:
video = video_list[0].module_dynamic.dyn_archive
video_info = (
image(video.cover)
+ f"标题: {video.title}\nBvid: {video.bvid}\n直链: https://www.bilibili.com/video/{video.bvid}"
)
rst += video_info + "\n"
download_dynamic_image = Config.get_config(
"bilibili_sub", "DOWNLOAD_DYNAMIC_IMAGE"
)
draw_info = ""
if download_dynamic_image and (
draw_list := [
module.module_dynamic.dyn_draw
for module in dynamic.modules
if str(module.module_dynamic.dyn_draw)
]
):
idx = 0
for draws in draw_list:
for draw in list(draws.items):
path = (
TEMP_PATH
/ f"{_user.uid}_{dynamic.extend.dyn_id_str}_draw_{idx}.jpg"
)
if await AsyncHttpx.download_file(draw.src, path):
draw_info += image(path)
idx += 1
if draw_info:
rst += "动态图片\n" + draw_info + "\n"
return rst
async def _get_season_status(id_: str) -> str:
"""
获取 番剧 更新状态
:param id_: 番剧 id
"""
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
season_info = await get_meta(id_)
title = season_info["media"]["title"]
if data := await BilibiliSub.get_or_none(sub_id=id_):
_idx = data.season_current_episode
new_ep = season_info["media"]["new_ep"]["index"]
if new_ep != _idx:
await BilibiliSub.sub_handle(
id_, season_current_episode=new_ep, season_update_time=datetime.now()
)
return (
f'{image(season_info["media"]["cover"])}\n'
f"[{title}]更新啦\n"
f"最新集数:{new_ep}"
)
return ""
async def get_user_dynamic(
dynamic_id: str, local_user: BilibiliSub
) -> Tuple[Optional[MessageSegment], str]:
"""
获取用户动态
:param dynamic_id: 动态id
:param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间
"""
if local_user.dynamic_upload_time < int(dynamic_id):
image = await AsyncPlaywright.screenshot(
f"https://t.bilibili.com/{dynamic_id}",
DYNAMIC_PATH / f"sub_{local_user.sub_id}.png",
".bili-dyn-item__main",
wait_until="networkidle",
)
return (
image,
f"https://t.bilibili.com/{dynamic_id}",
)
return None, ""
class SubManager:
def __init__(self):
self.live_data = []
self.up_data = []
self.season_data = []
self.current_index = -1
async def reload_sub_data(self):
"""
重载数据
"""
if not self.live_data or not self.up_data or not self.season_data:
(
_live_data,
_up_data,
_season_data,
) = await BilibiliSub.get_all_sub_data()
if not self.live_data:
self.live_data = _live_data
if not self.up_data:
self.up_data = _up_data
if not self.season_data:
self.season_data = _season_data
async def random_sub_data(self) -> Optional[BilibiliSub]:
"""
随机获取一条数据
:return:
"""
sub = None
if not self.live_data and not self.up_data and not self.season_data:
return sub
self.current_index += 1
if self.current_index == 0:
if self.live_data:
sub = random.choice(self.live_data)
self.live_data.remove(sub)
elif self.current_index == 1:
if self.up_data:
sub = random.choice(self.up_data)
self.up_data.remove(sub)
elif self.current_index == 2:
if self.season_data:
sub = random.choice(self.season_data)
self.season_data.remove(sub)
else:
self.current_index = -1
if sub:
return sub
await self.reload_sub_data()
return await self.random_sub_data()

View File

@ -1,207 +0,0 @@
from datetime import datetime
from typing import List, Optional, Tuple
from tortoise import fields
from services.db_context import Model
from services.log import logger
class BilibiliSub(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
sub_id = fields.CharField(255)
"""订阅id"""
sub_type = fields.CharField(255)
"""订阅类型"""
sub_users = fields.TextField()
"""订阅用户"""
live_short_id = fields.CharField(255, null=True)
"""直播短id"""
live_status = fields.IntField(null=True)
"""直播状态 0: 停播 1: 直播"""
uid = fields.CharField(255, null=True)
"""主播/UP UID"""
uname = fields.CharField(255, null=True)
"""主播/UP 名称"""
latest_video_created = fields.BigIntField(null=True)
"""最后视频上传时间"""
dynamic_upload_time = fields.BigIntField(null=True, default=0)
"""动态发布时间"""
season_name = fields.CharField(255, null=True)
"""番剧名称"""
season_id = fields.IntField(null=True)
"""番剧id"""
season_current_episode = fields.CharField(255, null=True)
"""番剧最新集数"""
season_update_time = fields.DateField(null=True)
"""番剧更新日期"""
class Meta:
table = "bilibili_sub"
table_description = "B站订阅数据表"
unique_together = ("sub_id", "sub_type")
@classmethod
async def sub_handle(
cls,
sub_id: str,
sub_type: Optional[str] = None,
sub_user: str = "",
*,
live_short_id: Optional[str] = None,
live_status: Optional[int] = None,
dynamic_upload_time: int = 0,
uid: Optional[str] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
添加订阅
参数:
:param sub_id: 订阅名称房间号番剧号等
:param sub_type: 订阅类型
:param sub_user: 订阅此条目的用户
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
: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.error(f"添加订阅 Id: {sub_id} 错误", e=e)
return False
@classmethod
async def delete_bilibili_sub(
cls, sub_id: str, sub_user: str, sub_type: Optional[str] = None
) -> bool:
"""
说明:
删除订阅
参数:
:param sub_id: 订阅名称
: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.get_or_none(
sub_id=sub_id, sub_type=sub_type, sub_users__contains=contains_str
)
else:
sub = await cls.get_or_none(
sub_id=sub_id, sub_users__contains=contains_str
)
if not sub:
return False
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.error(f"bilibili_sub 删除订阅错误", target=sub_id, e=e)
return False
@classmethod
async def get_all_sub_data(
cls,
) -> Tuple[List["BilibiliSub"], List["BilibiliSub"], List["BilibiliSub"]]:
"""
说明:
分类获取所有数据
"""
live_data = []
up_data = []
season_data = []
query = await cls.all()
for x in query:
if x.sub_type == "live":
live_data.append(x)
if x.sub_type == "up":
up_data.append(x)
if x.sub_type == "season":
season_data.append(x)
return live_data, up_data, season_data
@classmethod
def _run_script(cls):
return [
"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

@ -1,150 +0,0 @@
from io import BytesIO
# from bilibili_api import user
from bilireq.user import get_user_info
from httpx import AsyncClient
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx, get_user_agent
from utils.image_utils import BuildImage
BORDER_PATH = IMAGE_PATH / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True)
BASE_URL = "https://api.bilibili.com"
async def get_pic(url: str) -> bytes:
"""
获取图像
:param url: 图像链接
:return: 图像二进制
"""
return (await AsyncHttpx.get(url, timeout=10)).content
async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str):
"""
生成主播简介图片
:param uid: 主播 uid
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:return:
"""
user_info = await get_user_info(uid)
name = user_info["name"]
sex = user_info["sex"]
face = user_info["face"]
sign = user_info["sign"]
ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle()
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
def _create_live_des_image(
title: str,
cover: BuildImage,
tags: str,
des: str,
user_name: str,
sex: str,
sign: str,
ava: BuildImage,
):
"""
生成主播简介图片
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:param user_name: 主播名称
:param sex: 主播性别
:param sign: 主播签名
:param ava: 主播头像
:return:
"""
border = BORDER_PATH / "0.png"
border_img = None
if border.exists():
border_img = BuildImage(1772, 2657, background=border)
bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width")
async def get_meta(media_id: str, auth=None, reqtype="both", **kwargs):
"""
根据番剧 ID 获取番剧元数据信息
作为bilibili_api和bilireq的替代品
如果bilireq.bangumi更新了可以转为调用bilireq.bangumi的get_meta方法两者完全一致
"""
from bilireq.utils import get
url = f"{BASE_URL}/pgc/review/user"
params = {"media_id": media_id}
raw_json = await get(
url, raw=True, params=params, auth=auth, reqtype=reqtype, **kwargs
)
return raw_json["result"]
async def get_videos(
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate"
):
"""
获取用户投该视频信息
作为bilibili_api和bilireq的替代品
如果bilireq.user更新了可以转为调用bilireq.user的get_videos方法两者完全一致
:param uid: 用户 UID
:param tid: 分区 ID
:param pn: 页码
:param keyword: 搜索关键词
:param order: 排序方式可以为 pubdate(上传日期从新到旧), stow(收藏从多到少), click(播放量从多到少)
"""
from bilireq.utils import ResponseCodeError
url = f"{BASE_URL}/x/space/arc/search"
headers = get_user_agent()
headers["Referer"] = f"https://space.bilibili.com/{uid}/video"
async with AsyncClient() as client:
r = await client.head(
"https://space.bilibili.com",
headers=headers,
)
params = {
"mid": uid,
"ps": 30,
"tid": tid,
"pn": pn,
"keyword": keyword,
"order": order,
}
raw_json = (
await client.get(url, params=params, headers=headers, cookies=r.cookies)
).json()
if raw_json["code"] != 0:
raise ResponseCodeError(
code=raw_json["code"],
msg=raw_json["message"],
data=raw_json.get("data", None),
)
return raw_json["data"]
async def get_user_card(
mid: str, photo: bool = False, auth=None, reqtype="both", **kwargs
):
from bilireq.utils import get
url = f"{BASE_URL}/x/web-interface/card"
return (
await get(
url,
params={"mid": mid, "photo": photo},
auth=auth,
reqtype=reqtype,
**kwargs,
)
)["card"]

View File

@ -1,278 +0,0 @@
from datetime import datetime
from typing import Any, List, Tuple
from nonebot import on_command, on_message, on_regex
from nonebot.adapters.onebot.v11 import (
Bot,
Event,
GroupMessageEvent,
Message,
MessageEvent,
)
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.params import CommandArg, RegexGroup
from nonebot.permission import SUPERUSER
from configs.config import NICKNAME, Config
from models.ban_user import BanUser
from services.log import logger
from utils.image_utils import BuildImage
from utils.manager import group_manager
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from .data_source import set_user_punish, show_black_text_image
from .model import BlackWord
from .utils import black_word_manager
__zx_plugin_name__ = "敏感词检测"
__plugin_usage__ = """
usage
注意你的发言
指令
惩罚机制
""".strip()
__plugin_superuser_usage__ = """
usage
查看和设置惩罚
Regex^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$
设置惩罚id需要通过 '记录名单u:xxxxxxxx' 获取
指令
记录名单
设置惩罚 [user_id] [下标] [惩罚等级]
示例记录名单
示例记录名单u:12345678
示例设置惩罚 12345678 1 4
""".strip()
__plugin_des__ = "请注意你的发言!!"
__plugin_type__ = ("其他",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_cmd__ = ["惩罚机制", "记录名单 [_superuser]", "设置惩罚 [_superuser]"]
__plugin_settings__ = {
"cmd": ["敏感词检测"],
}
Config.add_plugin_config(
"black_word",
"CYCLE_DAYS",
30,
name="敏感词检测与惩罚",
help_="黑名单词汇记录周期",
default_value=30,
type=int,
)
Config.add_plugin_config(
"black_word",
"TOLERATE_COUNT",
[5, 1, 1, 1, 1],
help_="各个级别惩罚的容忍次数依次为1, 2, 3, 4, 5",
default_value=[5, 1, 1, 1, 1],
type=List[int],
)
Config.add_plugin_config(
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True, type=bool
)
# Config.add_plugin_config(
# "black_word", "IGNORE_GROUP", [], help_="退出群聊惩罚中忽略的群聊,即不会退出的群聊", default_value=[]
# )
Config.add_plugin_config(
"black_word",
"BAN_4_DURATION",
360,
help_="Union[int, List[int, int]]Ban时长分钟四级惩罚可以为指定数字或指定列表区间(随机),例如 [30, 360]",
default_value=360,
type=int,
)
Config.add_plugin_config(
"black_word",
"BAN_3_DURATION",
7,
help_="Union[int, List[int, int]]Ban时长三级惩罚可以为指定数字或指定列表区间(随机),例如 [7, 30]",
default_value=7,
type=int,
)
Config.add_plugin_config(
"black_word",
"WARNING_RESULT",
f"请注意对{NICKNAME}的发言内容",
help_="口头警告内容",
default_value=f"请注意对{NICKNAME}的发言内容",
)
Config.add_plugin_config(
"black_word",
"AUTO_ADD_PUNISH_LEVEL",
True,
help_="自动提级机制,当周期内处罚次数大于某一特定值就提升惩罚等级",
default_value=True,
type=bool,
)
Config.add_plugin_config(
"black_word",
"ADD_PUNISH_LEVEL_TO_COUNT",
3,
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
default_value=3,
type=int,
)
Config.add_plugin_config(
"black_word",
"ALAPI_CHECK_FLAG",
False,
help_="当未检测到已收录的敏感词时开启ALAPI文本检测并将疑似文本发送给超级用户",
default_value=False,
type=bool,
)
Config.add_plugin_config(
"black_word",
"CONTAIN_BLACK_STOP_PROPAGATION",
True,
help_="当文本包含任意敏感词时停止向下级插件传递即不触发ai",
default_value=True,
type=bool,
)
message_matcher = on_message(priority=1, block=False)
set_punish = on_command("设置惩罚", priority=1, permission=SUPERUSER, block=True)
show_black = on_regex(
r"^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$",
priority=1,
permission=SUPERUSER,
block=True,
)
show_punish = on_command("惩罚机制", aliases={"敏感词检测"}, priority=1, block=True)
# 黑名单词汇检测
@run_preprocessor
async def _(
bot: Bot,
matcher: Matcher,
event: Event,
):
msg = get_message_text(event.json())
if (
isinstance(event, MessageEvent)
and event.is_tome()
and not msg.startswith("原神绑定")
):
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"
and not await BanUser.is_ban(event.user_id)
):
# 屏蔽群权限-1的群
if (
isinstance(event, GroupMessageEvent)
and group_manager.get_group_level(event.group_id) < 0
):
return
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
) and Config.get_config("black_word", "CONTAIN_BLACK_STOP_PROPAGATION"):
matcher.stop_propagation()
@show_black.handle()
async def _(bot: Bot, reg_group: Tuple[Any, ...] = RegexGroup()):
user_id, group_id, date = reg_group
date_type = "="
if date:
date_type = date[1]
date = date[2:]
try:
date = datetime.strptime(date, "%Y-%m-%d")
except ValueError:
await show_black.finish("日期格式错误,需要:年-月-日")
pic = await show_black_text_image(
bot,
user_id.split(":")[1] if user_id else None,
group_id.split(":")[1] if group_id else None,
date,
date_type,
)
await show_black.send(image(b64=pic.pic2bs4()))
@show_punish.handle()
async def _():
text = f"""
** 惩罚机制 **
惩罚前包含容忍机制在指定周期内会容忍偶尔少次数的敏感词只会进行警告提醒
多次触发同级惩罚会使惩罚等级提高即惩罚自动提级机制
目前公开的惩罚等级
1永久ban
2删除好友
3ban指定/随机天数
4ban指定/随机时长
5警告
备注
该功能为测试阶段如果你有被误封情况请联系管理员会从数据库中提取出你的数据进行审核后判断
目前该功能暂不完善部分情况会由管理员鉴定请注意对真寻的发言
关于敏感词
记住不要骂{NICKNAME}就对了
""".strip()
max_width = 0
for m in text.split("\n"):
max_width = len(m) * 20 if len(m) * 20 > max_width else max_width
max_height = len(text.split("\n")) * 24
A = BuildImage(
max_width, max_height, font="CJGaoDeGuo.otf", font_size=24, color="#E3DBD1"
)
A.text((10, 10), text)
await show_punish.send(image(b64=A.pic2bs4()))
@set_punish.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if (
len(msg) < 3
or not is_number(msg[0])
or not is_number(msg[1])
or not is_number(msg[2])
):
await set_punish.finish("参数错误,请查看帮助...", at_sender=True)
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"设置惩罚 uid{uid} id_{id_} punish_level:{punish_level} --> {rst}",
"设置惩罚",
event.user_id,
)

View File

@ -1,121 +0,0 @@
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 Config, _get_punish
async def show_black_text_image(
bot: Bot,
user_id: Optional[str],
group_id: Optional[str],
date: Optional[datetime],
data_type: str = "=",
) -> BuildImage:
"""
展示记录名单
:param bot: bot
:param user: 用户qq
:param group_id: 群聊
:param date: 日期
:param data_type: 日期搜索类型
:return:
"""
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()
id_str = ""
uname_str = ""
uid_str = ""
gid_str = ""
plant_text_str = ""
black_word_str = ""
punish_str = ""
punish_level_str = ""
create_time_str = ""
for i, x in enumerate(data):
try:
if x.group_id:
user_name = (
await bot.get_group_member_info(
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"] == int(x.user_id)
][0]
except Exception as e:
logger.warning(
f"show_black_text_image 获取 USER {x.user_id} user_name 失败", e=e
)
user_name = x.user_id
id_str += f"{i}\n"
uname_str += f"{user_name}\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:
plant_text = plant_text[:20] + "..."
plant_text_str += f"{plant_text}\n"
black_word_str += f"{x.black_word}\n"
punish_str += f"{x.punish}\n"
punish_level_str += f"{x.punish_level}\n"
create_time_str += f"{x.create_time.replace(microsecond=0)}\n"
_tmp_img = BuildImage(0, 0, font_size=35, font="CJGaoDeGuo.otf")
for s, type_ in [
(id_str, "Id"),
(uname_str, "昵称"),
(uid_str, "UID"),
(gid_str, "GID"),
(plant_text_str, "文本"),
(black_word_str, "检测"),
(punish_str, "惩罚"),
(punish_level_str, "等级"),
(create_time_str, "记录日期"),
]:
img = await text2image(s, color="#f9f6f2", _add_height=2.1)
w = _tmp_img.getsize(type_)[0] if _tmp_img.getsize(type_)[0] > img.w else img.w
A = BuildImage(w + 11, img.h + 50, color="#f9f6f2", font_size=35, font="CJGaoDeGuo.otf")
await A.atext((10, 10), type_)
await A.apaste(img, (0, 50))
image_list.append(A)
horizontal_line = []
w, h = 0, 0
for img in image_list:
w += img.w + 20
h = img.h if img.h > h else h
horizontal_line.append(img.w)
A = BuildImage(w, h, color="#f9f6f2")
current_w = 0
for img in image_list:
await A.apaste(img, (current_w, 0))
current_w += img.w + 20
return A
async def set_user_punish(user_id: str, id_: int, punish_level: int) -> str:
"""
设置惩罚
:param user_id: 用户id
:param id_: 记录下标
:param punish_level: 惩罚等级
"""
result = await _get_punish(punish_level, user_id)
punish = {
1: "永久ban",
2: "删除好友",
3: f"ban {result}",
4: f"ban {result} 分钟",
5: "口头警告"
}
if await BlackWord.set_user_punish(user_id, punish[punish_level], id_=id_):
return f"已对 USER {user_id} 进行 {punish[punish_level]} 处罚。"
else:
return "操作失败可能未找到用户id或敏感词"

View File

@ -1,91 +0,0 @@
from asyncio.exceptions import TimeoutError
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Message, PrivateMessageEvent
from nonebot.adapters.onebot.v11.permission import PRIVATE
from nonebot.params import ArgStr, CommandArg
from nonebot.typing import T_State
from services.log import logger
from utils.utils import is_number
from .data_source import get_bt_info
__zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """
usage
* 请各位使用后不要转发 *
* 拒绝反冲斗士 *
指令
bt [关键词] ?[页数]
示例bt 钢铁侠
示例bt 钢铁侠 3
""".strip()
__plugin_des__ = "bt(磁力搜索)[仅支持私聊,懂的都懂]"
__plugin_cmd__ = ["bt [关键词] ?[页数]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["bt", "磁力搜索", "Bt", "BT"],
}
__plugin_block_limit__ = {"rst": "您有bt任务正在进行请等待结束."}
__plugin_configs__ = {
"BT_MAX_NUM": {
"value": 10,
"help": "单次BT搜索返回最大消息数量",
"default_value": 10,
"type": int,
}
}
bt = on_command("bt", permission=PRIVATE, priority=5, block=True)
@bt.handle()
async def _(state: T_State, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip().split()
if msg:
keyword = None
page = 1
if n := len(msg):
keyword = msg[0]
if n > 1 and is_number(msg[1]) and int(msg[1]) > 0:
page = int(msg[1])
state["keyword"] = keyword
state["page"] = page
else:
state["page"] = 1
@bt.got("keyword", prompt="请输入要查询的内容!")
async def _(
event: PrivateMessageEvent,
state: T_State,
keyword: str = ArgStr("keyword"),
page: str = ArgStr("page"),
):
send_flag = False
try:
async for title, type_, create_time, file_size, link in get_bt_info(
keyword, page
):
await bt.send(
f"标题:{title}\n"
f"类型:{type_}\n"
f"创建时间:{create_time}\n"
f"文件大小:{file_size}\n"
f"种子:{link}"
)
send_flag = True
except TimeoutError:
await bt.finish(f"搜索 {keyword} 超时...")
except Exception as e:
logger.error(f"bt 错误 {type(e)}{e}")
await bt.finish(f"bt 其他未知错误..")
if not send_flag:
await bt.send(f"{keyword} 未搜索到...")
logger.info(f"USER {event.user_id} BT搜索 {keyword}{page}")

Some files were not shown because too many files have changed in this diff Show More