47
.env.dev
@ -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
@ -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
@ -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
@ -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>
|
||||
|
||||

|
||||

|
||||

|
||||
<div align=center>
|
||||
|
||||

|
||||

|
||||
|
||||
</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 @@
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
</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>
|
||||
|
||||
|
||||
@ -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()))
|
||||
@ -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 "那我先睡觉了..."
|
||||
@ -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)
|
||||
@ -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("发生了一些未知错误...")
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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))
|
||||
@ -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)} 条管理员命令")
|
||||
@ -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', # 任务储存库,在下一小节中说明
|
||||
# )
|
||||
@ -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...")
|
||||
@ -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
|
||||
@ -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}")
|
||||
@ -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)
|
||||
)
|
||||
@ -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))
|
||||
@ -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))
|
||||
@ -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
|
||||
@ -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"))
|
||||
@ -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
|
||||
@ -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
|
||||
)
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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("用户处于黑名单中")
|
||||
@ -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"]}')
|
||||
@ -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("有命令就别说话了")
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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"[[_task|{task}]]", "").replace(
|
||||
f"[[_task|{task}]]", ""
|
||||
)
|
||||
data["message"] = Message(msg)
|
||||
@ -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_)
|
||||
@ -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)
|
||||
@ -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()
|
||||
)
|
||||
@ -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")
|
||||
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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())} 个非限制插件.")
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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} 失败!"
|
||||
# )
|
||||
@ -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()))
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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} 的使用函数")
|
||||
@ -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("已清空所有好友/群聊请求..")
|
||||
@ -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))
|
||||
@ -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)
|
||||
@ -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))
|
||||
@ -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("已自动重载所有配置文件...")
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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))
|
||||
@ -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)} 条超级用户命令")
|
||||
@ -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
@ -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__":
|
||||
|
||||
@ -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
|
After Width: | Height: | Size: 188 KiB |
BIN
docs_image/webui1.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
docs_image/webui2.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
docs_image/webui3.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
docs_image/webui4.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
docs_image/webui5.png
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
docs_image/webui6.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
docs_image/webui7.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
@ -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);"
|
||||
]
|
||||
@ -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)
|
||||
]
|
||||
@ -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))
|
||||
@ -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);",
|
||||
]
|
||||
@ -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)
|
||||
]
|
||||
@ -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);"
|
||||
]
|
||||
@ -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: 是否被自动更新刷新权限 0:是,1:否
|
||||
"""
|
||||
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);"
|
||||
]
|
||||
@ -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);",
|
||||
]
|
||||
@ -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)
|
||||
@ -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"))
|
||||
@ -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())
|
||||
@ -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()))
|
||||
@ -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}"
|
||||
)
|
||||
@ -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}"
|
||||
)
|
||||
@ -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}")
|
||||
@ -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}》'"
|
||||
)
|
||||
@ -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)
|
||||
@ -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()
|
||||
@ -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字段改为字符串
|
||||
]
|
||||
@ -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"]
|
||||
@ -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级:删除好友
|
||||
|
||||
3级:ban指定/随机天数
|
||||
|
||||
4级:ban指定/随机时长
|
||||
|
||||
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,
|
||||
)
|
||||
@ -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或敏感词"
|
||||
@ -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} 页")
|
||||