Compare commits

...
22 Commits
Author SHA1 Message Date
HibiKier ae5df38c21 🐛 修复图片模板方法 2024-11-16 17:59:15 +08:00
HibiKier 545f6fd7f9 🎨 入群检测代码优化 2024-11-14 18:56:53 +08:00
HibiKier 4c7c223b2c 🐛 修复我的信息at用户时头像不正确 2024-11-14 15:27:17 +08:00
HibiKier 14b983cfa0 💬 修改md 2024-11-09 13:53:38 +08:00
HibiKier 229908207b 💬 更新md 2024-11-09 13:53:16 +08:00
HibiKier cfe7dfd6b5 💬 修改md 2024-11-08 21:00:48 +08:00
HibiKier b2da0a902d 🎨 playwright添加cookie参数 2024-11-07 13:38:20 +08:00
HibiKier 3f06131c34 💬 修改md 2024-11-07 00:29:31 +08:00
HibiKier 24f1a1ca9a 💬 修改测试url 2024-11-07 00:28:41 +08:00
HibiKier 54a74cdd91 🎨 更新md 2024-11-05 20:45:11 +08:00
HibiKier 7ca4c18e1c 🎨 修改md 2024-11-05 15:54:53 +08:00
HibiKier a2b7980ae2 💬 修改版本号 2024-11-05 15:51:19 +08:00
HibiKier e75a56fe08 🐛 修复自定义pixiv反向代理无法对涩图生效 2024-11-05 15:47:59 +08:00
HibiKier abd8b9b705 ✨ 添加用户消息撤回命令 (#1719) 2024-11-05 15:34:45 +08:00
HibiKier 54d2ba1df4 🐛 修复邀请群组强制退出 2024-11-05 14:47:07 +08:00
HibiKier dbfeeac313 Merge branch 'dev' of https://github.com/HibiKier/zhenxun_bot into dev 2024-10-29 08:27:02 +08:00
HibiKier 93316be5fe 🎨 工具代码优化 2024-10-29 08:26:41 +08:00
AkashiCoin 60b78a9f1c 🎉 chore(version): Update version to v0.2.4-6359ba6 (#1692) 2024-10-28 09:41:35 +08:00
HibiKier 6359ba63f9 💬 更新权限检查日志提示 2024-10-24 16:27:37 +08:00
HibiKier 2853a2feaa ✨ ban限制依赖类型插件 2024-10-24 11:09:01 +08:00
HibiKier c9b864ca54 🐛 修复合并转发在bytes传输下图片丢失 2024-10-23 15:21:53 +08:00
梦璃雨落 6519b1cad4 🐞 fix: 更新货物未找到异常的信息处理方式 (#1713) 2024-10-23 13:19:47 +08:00
35 changed files with 282 additions and 188 deletions
+2 -1
View File
@@ -25,7 +25,8 @@
"ujson", "ujson",
"unban", "unban",
"userinfo", "userinfo",
"zhenxun" "zhenxun",
"jsdelivr"
], ],
"python.analysis.autoImportCompletions": true, "python.analysis.autoImportCompletions": true,
"python.testing.pytestArgs": ["tests"], "python.testing.pytestArgs": ["tests"],
+53 -114
View File
@@ -60,7 +60,7 @@
<img width="350" height="350" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png"/> <img width="350" height="350" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/help.png"/>
<img width="250" height="500" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png"/> <img width="250" height="500" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/html_help.png"/>
<img width="180" height="450" src="https://github.com/HibiKier/zhenxun_bot/blob/dev/docs_image/zhenxun_help.png"/> <img width="180" height="450" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/zhenxun_help.png"/>
</div> </div>
@@ -68,11 +68,11 @@
### 1. 体验一下? ### 1. 体验一下?
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 napcat 或拉格朗日等直接连接用于体验与测试 这是一个免费的,版本为 dev 的 zhenxun,你可以通过 [napcat](https://github.com/NapNeko/NapCatQQ) 或 [拉格朗日](https://github.com/LagrangeDev/Lagrange.Core) 以及 [matcha](https://github.com/A-kirami/matcha) 等直接连接用于体验与测试
(球球了测试君!) (球球了测试君!)
``` ```
Url: 43.143.112.57:11451/onebot/v11/ws Url: ws://test.zhenxun.org:8080/onebot/v11/ws
AccessToken: PUBLIC_ZHENXUN_TEST AccessToken: PUBLIC_ZHENXUN_TEST
注:你无法获得超级用户权限 注:你无法获得超级用户权限
@@ -95,13 +95,14 @@ AccessToken: PUBLIC_ZHENXUN_TEST
<details> <details>
<summary> <strong> WebUI </strong>后台示例图 </summary> <summary> <strong> WebUI </strong>后台示例图 </summary>
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui1.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui00.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui2.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui01.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui3.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui02.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui4.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui03.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui5.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui04.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui6.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui05.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui7.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui06.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui07.png)
</details> </details>
@@ -111,7 +112,8 @@ AccessToken: PUBLIC_ZHENXUN_TEST
## ~~来点优点?~~ 可爱难道还不够吗 ## ~~来点优点?~~ 可爱难道还不够吗
- 实现了许多功能,且提供了大量功能管理命令 - 实现了许多功能,且提供了大量功能管理命令,进行了多平台适配,兼容 nb2 商店插件
- 拥有完善可用的 webui
- 通过 Config 配置项将所有插件配置统计保存至 config.yaml,利于统一用户修改 - 通过 Config 配置项将所有插件配置统计保存至 config.yaml,利于统一用户修改
- 方便增删插件,原生 nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息` - 方便增删插件,原生 nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
- 提供了 cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`PluginCdBlock` 等 - 提供了 cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`PluginCdBlock` 等
@@ -165,161 +167,97 @@ python bot.py
``` ```
<!-- ## 功能列表 (旧版列表) ## 功能列表
<details> <details>
<summary>已实现的功能</summary> <summary>内置功能</summary>
### 已实现的常用功能 **真寻原 `plugins` 插件文件夹已迁移至其他仓库,当前内置仅保留必要的功能**
- [x] 昵称系统(群与群与私聊分开.) ### 基础功能
- [x] 图灵 AI(会把'你'等关键字替换为你的昵称),且带有 [AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus),够味 - [x] 昵称系统(群与群与私聊分开)
- [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置) - [x] 签到/我的签到/好感度排行/好感度总排行(影响色图概率和开箱次数,支持配置)
- [x] 发送某文件夹下的随机图片(支持自定义,默认:美图,萝莉,壁纸) - [x] 商店/我的金币/购买道具/使用道具/金币排行(完整的商店添加/购买/使用流程)
- [x] 色图(这不是基础功能嘛喂)
- [x] coser
- [x] 黑白草图生成器
- [x] 鸡汤/语录
- [x] 骂我(钉宫语音)
- [x] 戳一戳(概率发送美图,钉宫语音或者戳回去)
- [x] 模拟开箱/我的开箱/群开箱统计/我的金色/设置 cookie(csgo,内置爬虫脚本,需要提前抓取数据和图片,需要 session,可能需要代理,阿里云服务器等 ip 也许已经被 ban 了(我无代理访问失败),如果访问太多账号 API 调用可能被禁止访问 api!)
- [x] 鲁迅说过
- [x] 构造假消息(自定义的分享链接)
- [x] 商店/我的金币/购买道具/使用道具
- [x] 8 种手游抽卡 (查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw))
- [x] 我有一个朋友想问问..(借鉴 pcrbot 插件)
- [x] 原神黄历
- [x] 原神今日素材
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包
- [x] 微博热搜
- [x] B 站主播/UP/番剧订阅
- [x] pil 对图片的一些操作
- [x] BUFF 饰品底价查询(需要 session)
- [x] 天气查询
- [x] 疫情查询
- [x] bt 磁力搜索(咳咳,这功能我想 dddd)
- [x] reimu 搜索(上车) (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
- [x] 靠图识番 (使用[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot)的插件)
- [x] 以图搜图 (使用[nonebot_plugin_picsearcher](https://github.com/synodriver/nonebot_plugin_picsearcher)插件)
- [x] 搜番
- [x] 点歌 [nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2)插件(删除了选歌和评论)
- [x] epic 免费游戏
- [x] p 站排行榜
- [x] p 站搜图
- [x] 翻译(日英韩)
- [x] pix 图库(一个自己的图库,含有增删查改,黑名单等命令)
- [x] 查看当前群欢迎消息 - [x] 查看当前群欢迎消息
- [x] 查看该群自己的权限 - [x] 个人信息查看(群组内权限,聊天频率等)
- [x] 我的信息(只是为了看看什么时候入群) - [x] 消息撤回
- [x] 更新信息(如果继续更新的话)
- [x] go-cqhttp 最新版下载和上传(不需要请删除)
- [x] 撤回
- [x] 滴滴滴-(用户对超级用户发送消息)
- [x] 金币红包/金币排行
- [x] 俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行
- [x] 网易云热评
- [x] 念首古诗
- [x] 获取 b 站视频封面
- [x] 通过 PID 获取图片
- [x] 功能统计可视化 - [x] 功能统计可视化
- [x] 词云
- [x] 关于 - [x] 关于
- [x] 三种样式的帮助菜单
### 已实现的管理员功能 ### 管理员功能
- [x] 管理员帮助
- [x] 更新群组成员信息 - [x] 更新群组成员信息
- [x] 95%的群功能开关 - [x] 95%的群功能开关
- [x] 查看群内被动技能状态 - [x] 查看群内被动技能状态
- [x] 自定义群欢迎消息(是真寻的不是管家的!) - [x] 自定义群欢迎消息(是真寻的不是管家的!)
- [x] .ban/.unban(支持设置 ban 时长)= 黑白名单 - [x] ban/unban(支持设置 ban 时长)= 群组及用户的黑名单
- [x] 刷屏禁言相关:刷屏检测设置/设置禁言时长/设置检测次数 - [x] 休息吧/醒来(群组内真寻状态)
- [x] 上传图片/连续上传图片 (上传图片至指定图库)
- [x] 移动图片 (同上)
- [x] 删除图片 (同上)
- [x] 群内 B 站订阅
- [x] 词条设置
- [x] 休息吧/醒来
### 已实现的超级用户功能 ### 超级用户功能
- [x] 超级用户帮助
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员) - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
- [x] 群组管理,退群指令等
- [x] 开启/关闭指定群的广播通知
- [x] 广播 - [x] 广播
- [x] 自检(检查系统状态) - [x] 自检(检查系统状态)
- [x] 所有群组/所有好友 - [x] 所有群组/所有好友
- [x] 退出指定群 - [x] 退出指定群
- [x] 更新好友信息/更新群信息 - [x] 更新好友信息/更新群信息
- [x] /t(对用户进行回复或发送消息)
- [x] 上传/删除/修改商品(需要编写对应的商品功能)
- [x] 节日红包发送
- [x] 修改群权限 - [x] 修改群权限
- [x] ban - [x] 检查更新
- [x] 更新色图
- [x] 更新价格/更加图片(csgo 开箱)
- [x] 重载原神/方舟/赛马娘/坎公骑冠剑卡池
- [x] 更新原神今日素材/更新原神资源信息
- [x] PIX 相关操作
- [x] 检查更新真寻
- [x] 重启 - [x] 重启
- [x] 添加/删除/查看群白名单 - [x] 添加/删除/查看群白名单
- [x] 功能开关(更多设置) - [x] 功能开关(更多设置)
- [x] 功能状态 - [x] 功能状态
- [x] b 了 - [x] 执行 SQL
- [x] 执行 sql
- [x] 重载配置 - [x] 重载配置
- [x] 清理临时数据 - [x] 清理临时数据
- [x] 增删群认证 - [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求 - [x] 同意/拒绝好友/群聊请求
- [x] 配置重载 - [x] 添加/移除/更新插件/插件商店(plugins 库以及扩展库)
- [x] WebUI API(对真寻前端的支持)
#### 超级用户的被动技能 #### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群) - [x] 邀请入群提醒(别人邀请真寻入群,可配置自动同意)
- [x] 添加好友提醒(别人添加真寻好友) - [x] 添加好友提醒(别人添加真寻好友,可配置自动同意)
### 已实现的被动技能 ### 被动技能
- [x] 进群欢迎消息
- [x] 群早晚安 - [x] 群早晚安
- [x] 每日开箱重置提醒
- [x] b 站转发解析(解析 b 站分享信息,支持 bv,bilibili 链接,b 站手机端转发卡片,cv,b23.tv),且 5 分钟内不解析相同 url
- [x] 丢人爬(爬表情包)
- [x] epic 通知(每日发送 epic 免费游戏链接)
- [x] 原神黄历提醒
- [x] 复读
### 已实现的看不见的技能 ### 看不见的技能
- [x] 刷屏禁言检测
- [x] 功能调用统计 - [x] 功能调用统计
- [x] 聊天记录统计
- [x] 检测恶意触发命令(将被最高权限 ban 掉 30 分钟,只有最高权限(9 级)可以进行 unban) - [x] 检测恶意触发命令(将被最高权限 ban 掉 30 分钟,只有最高权限(9 级)可以进行 unban)
- [x] 自动同意好友请求,加群请求将会提醒管理员,退群提示,加群欢迎等等 - [x] 自动同意好友/群组请求,加群请求将会提醒管理员,退群提示,加群欢迎等等
- [x] 群聊时间检测(当群聊最后一人发言时间大于当前 36 小时后将关闭该群所有通知(即被动技能)) - [x] 群聊时间检测(当群聊最后一人发言时间大于当前 48 小时后将关闭该群所有通知(即被动技能))
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限 - [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统 - [x] 群权限系统
- [x] 定时更新权限 - [x] 定时更新权限
- [x] 自动配置重载 - [x] 自动配置重载
- [x] 强制入群保护
- [x] 自定备份(可配置)
- [x] 笨蛋检测(当使用功能名称当指令时真寻会跳出来狠狠嘲笑并帮助)
</details> --> ### 更多插件...
## [爱发电](https://afdian.net/@HibiKier) - [更多插件](https://github.com/zhenxun-org/zhenxun_bot_plugins)
- [第三方插件索引库](https://github.com/zhenxun-org/zhenxun_bot_plugins_index)
</details>
## [爱发电](https://afdian.com/a/HibiKier)
<details> <details>
<summary>爱发电 以及 感谢投喂 </summary> <summary>爱发电 以及 感谢投喂 </summary>
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png"> <img width="365px" height="450px" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/afd.jpg">
### 感谢名单 ### 感谢名单
@@ -369,6 +307,7 @@ python bot.py
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377) [哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
[yajiwa](https://github.com/yajiwa) [yajiwa](https://github.com/yajiwa)
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377) [爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
...
</details> </details>
+1 -1
View File
@@ -1 +1 @@
__version__: v0.2.4-fix1 __version__: v0.2.4-fix2
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

@@ -235,7 +235,10 @@ class AuthChecker:
return return
if plugin := await PluginInfo.get_or_none(module_path=module_path): if plugin := await PluginInfo.get_or_none(module_path=module_path):
if plugin.plugin_type == PluginType.HIDDEN: if plugin.plugin_type == PluginType.HIDDEN:
logger.debug("插件为HIDDEN,已跳过...") logger.debug(
f"插件: {plugin.name}:{plugin.module} "
"为HIDDEN,已跳过权限检查..."
)
return return
try: try:
cost_gold = await self.auth_cost(user, plugin, session) cost_gold = await self.auth_cost(user, plugin, session)
+1 -1
View File
@@ -32,7 +32,7 @@ async def _(
if plugin := matcher.plugin: if plugin := matcher.plugin:
if metadata := plugin.metadata: if metadata := plugin.metadata:
extra = metadata.extra extra = metadata.extra
if extra.get("plugin_type") in [PluginType.HIDDEN, PluginType.DEPENDANT]: if extra.get("plugin_type") in [PluginType.HIDDEN]:
return return
user_id = session.id1 user_id = session.id1
group_id = session.id3 or session.id2 group_id = session.id3 or session.id2
@@ -0,0 +1,23 @@
from typing import Any
from nonebot.adapters import Bot
from zhenxun.services.log import logger
from zhenxun.utils.manager.message_manager import MessageManager
@Bot.on_called_api
async def handle_api_result(
bot: Bot, exception: Exception | None, api: str, data: dict[str, Any], result: Any
):
if not exception and api == "send_msg":
try:
if (uid := data.get("user_id")) and (msg_id := result.get("message_id")):
MessageManager.add(str(uid), str(msg_id))
logger.debug(
f"收集消息id,user_id: {uid}, msg_id: {msg_id}", "msg_hook"
)
except Exception as e:
logger.warning(
f"收集消息id发生错误...data: {data}, result: {result}", "msg_hook", e=e
)
+1 -1
View File
@@ -46,7 +46,7 @@ async def _(
nickname = user_id nickname = user_id
try: try:
result = await get_user_info( result = await get_user_info(
session, bot, user_id, session.group.id if session.group else None, nickname session, user_id, session.group.id if session.group else None, nickname
) )
await MessageUtils.build_message(result).send(at_sender=True) await MessageUtils.build_message(result).send(at_sender=True)
logger.info("获取用户信息", arparma.header_result, session=session) logger.info("获取用户信息", arparma.header_result, session=session)
+3 -4
View File
@@ -1,7 +1,6 @@
import random import random
from datetime import datetime, timedelta from datetime import datetime, timedelta
from nonebot.adapters import Bot
from tortoise.functions import Count from tortoise.functions import Count
from tortoise.expressions import RawSQL from tortoise.expressions import RawSQL
from nonebot_plugin_uninfo import Uninfo from nonebot_plugin_uninfo import Uninfo
@@ -131,7 +130,7 @@ async def get_chat_history(
async def get_user_info( async def get_user_info(
session: Uninfo, bot: Bot, user_id: str, group_id: str | None, nickname: str session: Uninfo, user_id: str, group_id: str | None, nickname: str
) -> bytes: ) -> bytes:
"""获取用户个人信息 """获取用户个人信息
@@ -145,7 +144,7 @@ async def get_user_info(
返回: 返回:
bytes: 图片数据 bytes: 图片数据
""" """
platform = PlatformUtils.get_platform(bot) or "" platform = PlatformUtils.get_platform(session) or "qq"
ava_url = PlatformUtils.get_user_avatar_url(user_id, platform) ava_url = PlatformUtils.get_user_avatar_url(user_id, platform)
user = await UserConsole.get_user(user_id, platform) user = await UserConsole.get_user(user_id, platform)
level = await LevelUser.get_user_level(user_id, group_id) level = await LevelUser.get_user_level(user_id, group_id)
@@ -164,7 +163,7 @@ async def get_user_info(
data = { data = {
"date": now.date(), "date": now.date(),
"weather": weather, "weather": weather,
"ava_url": session.user.avatar, "ava_url": ava_url,
"nickname": nickname, "nickname": nickname,
"title": "勇 者", "title": "勇 者",
"race": random.choice(RACE), "race": random.choice(RACE),
+22 -24
View File
@@ -1,30 +1,31 @@
import random import random
from typing import Any, List from typing import Any
from nonebot import on_regex from nonebot import on_regex
from nonebot.adapters import Bot
from nonebot.params import Depends, RegexGroup
from nonebot.plugin import PluginMetadata
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot_plugin_alconna import Alconna, Option, on_alconna, store_true from nonebot.adapters import Bot
from nonebot.plugin import PluginMetadata
from nonebot.params import Depends, RegexGroup
from nonebot_plugin_session import EventSession from nonebot_plugin_session import EventSession
from nonebot_plugin_userinfo import EventUserInfo, UserInfo from nonebot_plugin_userinfo import UserInfo, EventUserInfo
from nonebot_plugin_alconna import Option, Alconna, on_alconna, store_true
from zhenxun.configs.config import BotConfig, Config from zhenxun.services.log import logger
from zhenxun.configs.utils import PluginExtraData, RegisterConfig from zhenxun.utils.enum import PluginType
from zhenxun.utils.depends import UserName
from zhenxun.utils.message import MessageUtils
from zhenxun.models.ban_console import BanConsole from zhenxun.models.ban_console import BanConsole
from zhenxun.models.friend_user import FriendUser from zhenxun.models.friend_user import FriendUser
from zhenxun.configs.config import Config, BotConfig
from zhenxun.models.group_member_info import GroupInfoUser from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.services.log import logger from zhenxun.configs.utils import RegisterConfig, PluginExtraData
from zhenxun.utils.depends import UserName
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
__plugin_meta__ = PluginMetadata( __plugin_meta__ = PluginMetadata(
name="昵称系统", name="昵称系统",
description="区区昵称,才不想叫呢!", description="区区昵称,才不想叫呢!",
usage=f""" usage=f"""
个人昵称,将替换{BotConfig.self_nickname}称呼你的名称,群聊 与 私聊 昵称相互独立,全局昵称设置将更改您目前所有群聊中及私聊的昵称 个人昵称,将替换{BotConfig.self_nickname}称呼你的名称,群聊 与 私聊 昵称相互独立,
全局昵称设置将更改您目前所有群聊中及私聊的昵称
指令: 指令:
以后叫我 [昵称]: 设置当前群聊/私聊的昵称 以后叫我 [昵称]: 设置当前群聊/私聊的昵称
全局昵称设置 [昵称]: 设置当前所有群聊和私聊的昵称 全局昵称设置 [昵称]: 设置当前所有群聊和私聊的昵称
@@ -39,9 +40,10 @@ __plugin_meta__ = PluginMetadata(
RegisterConfig( RegisterConfig(
key="BLACK_WORD", key="BLACK_WORD",
value=["爸", "爹", "爷", "父"], value=["爸", "爹", "爷", "父"],
help="昵称所屏蔽的关键词,已设置的昵称会被替换为 *,未设置的昵称会在设置时提示", help="昵称所屏蔽的关键词,已设置的昵称会被替换为 *,"
"未设置的昵称会在设置时提示",
default_value=None, default_value=None,
type=List[str], type=list[str],
) )
], ],
).dict(), ).dict(),
@@ -167,9 +169,8 @@ async def _(
): ):
if session.id1: if session.id1:
(name,) = reg_group (name,) = reg_group
if len(name) < 5: if len(name) < 5 and random.random() < 0.3:
if random.random() < 0.3: name = "~".join(name)
name = "~".join(name)
if gid := session.id3 or session.id2: if gid := session.id3 or session.id2:
await GroupInfoUser.set_user_nickname( await GroupInfoUser.set_user_nickname(
session.id1, session.id1,
@@ -181,9 +182,6 @@ async def _(
session.platform, session.platform,
) )
logger.info(f"设置群昵称成功: {name}", "昵称设置", session=session) logger.info(f"设置群昵称成功: {name}", "昵称设置", session=session)
await MessageUtils.build_message(
random.choice(CALL_NAME).format(name)
).finish(reply_to=True)
else: else:
await FriendUser.set_user_nickname( await FriendUser.set_user_nickname(
session.id1, session.id1,
@@ -194,9 +192,9 @@ async def _(
session.platform, session.platform,
) )
logger.info(f"设置私聊昵称成功: {name}", "昵称设置", session=session) logger.info(f"设置私聊昵称成功: {name}", "昵称设置", session=session)
await MessageUtils.build_message( await MessageUtils.build_message(random.choice(CALL_NAME).format(name)).finish(
random.choice(CALL_NAME).format(name) reply_to=True
).finish(reply_to=True) )
await MessageUtils.build_message("用户id为空...").send() await MessageUtils.build_message("用户id为空...").send()
@@ -49,9 +49,8 @@ class GroupManager:
group: GroupConsole group: GroupConsole
""" """
if group: if group:
await GroupConsole.filter( group.group_flag = 1
group_id=group_id, channel_id__isnull=True await group.save(update_fields=["group_flag"])
).update(group_flag=1)
else: else:
block_plugin = "" block_plugin = ""
if plugin_list := await PluginInfo.filter(default_status=False).all(): if plugin_list := await PluginInfo.filter(default_status=False).all():
@@ -113,7 +112,7 @@ class GroupManager:
@classmethod @classmethod
async def add_bot( async def add_bot(
cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole | None cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole
): ):
"""拉入bot """拉入bot
@@ -123,7 +122,11 @@ class GroupManager:
group_id: 群组id group_id: 群组id
group: GroupConsole group: GroupConsole
""" """
if base_config.get("flag") and operator_id not in bot.config.superusers: if (
base_config.get("flag")
and operator_id not in bot.config.superusers
and group.group_flag != 1
):
"""退出群组""" """退出群组"""
try: try:
if result_msg := base_config.get("message"): if result_msg := base_config.get("message"):
+1 -1
View File
@@ -184,7 +184,7 @@ async def _(
elif isinstance(result, UniMessage): elif isinstance(result, UniMessage):
await result.finish(reply_to=True) await result.finish(reply_to=True)
except GoodsNotFound: except GoodsNotFound:
await MessageUtils.build_message(f"没有找到道具 {name} 或道具数量不足...").send( await MessageUtils.build_message(f"没有找到道具 {name.result} 或道具数量不足...").send(
reply_to=True reply_to=True
) )
+61
View File
@@ -0,0 +1,61 @@
from nonebot.rule import Rule
from nonebot.adapters import Bot, Event
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna.uniseg.tools import reply_fetch
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
from zhenxun.configs.utils import PluginExtraData
from zhenxun.utils.manager.message_manager import MessageManager
__plugin_meta__ = PluginMetadata(
name="消息撤回",
description="撤回自己触发的消息撤回,不允许撤回其他人触发消息的撤回哦",
usage="""
引用消息 撤回
""".strip(),
extra=PluginExtraData(author="HibiKier", version="0.1", menu_type="其他").dict(),
)
def reply_check() -> Rule:
"""
检查是否存在回复消息
返回:
Rule: Rule
"""
async def _rule(bot: Bot, event: Event, session: Uninfo):
if event.get_type() == "message":
return (
bool(await reply_fetch(event, bot))
and PlatformUtils.get_platform(session) == "qq"
)
return False
return Rule(_rule)
_matcher = on_alconna(Alconna("撤回"), priority=5, block=True, rule=reply_check())
@_matcher.handle()
async def _(bot: Bot, event: Event, session: Uninfo, arparma: Arparma):
if reply := await reply_fetch(event, bot):
if (
MessageManager.check(session.user.id, reply.id)
or session.user.id in bot.config.superusers
):
try:
await bot.delete_msg(message_id=reply.id)
logger.info("撤回消息", arparma.header_result, session=session)
except Exception:
await MessageUtils.build_message("撤回失败,可能消息已过期...").send()
else:
await MessageUtils.build_message(
"权限不足,不是你触发的消息不要胡乱撤回哦..."
).send()
+6 -13
View File
@@ -288,13 +288,6 @@ class ConfigsManager:
config.value = value config.value = value
config.default_value = default_value config.default_value = default_value
else: else:
_module = None
if ":" in module:
module_split = module.split(":")
if len(module_split) < 2:
raise ValueError(f"module: {module} 填写错误")
_module = module_split[-1]
module = module_split[0]
key = key.upper() key = key.upper()
if not self._data.get(module): if not self._data.get(module):
self._data[module] = ConfigGroup(module=module) self._data[module] = ConfigGroup(module=module)
@@ -311,7 +304,6 @@ class ConfigsManager:
key: str, key: str,
value: Any, value: Any,
auto_save: bool = False, auto_save: bool = False,
save_simple_data: bool = True,
): ):
"""设置配置值 """设置配置值
@@ -320,15 +312,16 @@ class ConfigsManager:
key: 配置名称 key: 配置名称
value: 值 value: 值
auto_save: 自动保存. auto_save: 自动保存.
save_simple_data: 保存至config.yaml.
""" """
key = key.upper()
if module in self._data: if module in self._data:
data = self._data[module].configs.get(key) if self._data[module].configs.get(key):
if data and data != value:
self._data[module].configs[key].value = value self._data[module].configs[key].value = value
self._simple_data[module][key] = value else:
self.add_plugin_config(module, key, value)
self._simple_data[module][key] = value
if auto_save: if auto_save:
self.save(save_simple_data=save_simple_data) self.save(save_simple_data=True)
def get_config(self, module: str, key: str, default: Any = None) -> Any: def get_config(self, module: str, key: str, default: Any = None) -> Any:
"""获取指定配置值 """获取指定配置值
+5 -7
View File
@@ -1,5 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Literal, Tuple from typing import Literal
from tortoise import fields from tortoise import fields
from tortoise.functions import Count from tortoise.functions import Count
@@ -27,7 +27,7 @@ class ChatHistory(Model):
platform = fields.CharField(255, null=True) platform = fields.CharField(255, null=True)
"""平台""" """平台"""
class Meta: class Meta: # type: ignore
table = "chat_history" table = "chat_history"
table_description = "聊天记录数据表" table_description = "聊天记录数据表"
@@ -53,7 +53,7 @@ class ChatHistory(Model):
query = query.filter(create_time__range=date_scope) query = query.filter(create_time__range=date_scope)
return list( return list(
await query.annotate(count=Count("user_id")) await query.annotate(count=Count("user_id"))
.order_by(o + "count") .order_by(f"{o}count")
.group_by("user_id") .group_by("user_id")
.limit(limit) .limit(limit)
.values_list("user_id", "count") .values_list("user_id", "count")
@@ -74,9 +74,7 @@ class ChatHistory(Model):
) )
else: else:
message = await cls.all().order_by("create_time").first() message = await cls.all().order_by("create_time").first()
if message: return message.create_time if message else None
return message.create_time
return None
@classmethod @classmethod
async def get_message( async def get_message(
@@ -85,7 +83,7 @@ class ChatHistory(Model):
gid: str, gid: str,
type_: Literal["user", "group"], type_: Literal["user", "group"],
msg_type: Literal["private", "group"] | None = None, msg_type: Literal["private", "group"] | None = None,
days: int | Tuple[datetime, datetime] | None = None, days: int | tuple[datetime, datetime] | None = None,
) -> list[Self]: ) -> list[Self]:
"""获取消息查询query """获取消息查询query
+9 -11
View File
@@ -10,7 +10,6 @@ from ._build_image import BuildImage
class RowStyle(BaseModel): class RowStyle(BaseModel):
font: FreeTypeFont | str | Path | None = "HYWenHei-85W.ttf" font: FreeTypeFont | str | Path | None = "HYWenHei-85W.ttf"
"""字体""" """字体"""
font_size: int = 20 font_size: int = 20
@@ -23,7 +22,6 @@ class RowStyle(BaseModel):
class ImageTemplate: class ImageTemplate:
color_list = ["#C2CEFE", "#FFA94C", "#3FE6A0", "#D1D4F5"] # noqa: RUF012 color_list = ["#C2CEFE", "#FFA94C", "#3FE6A0", "#D1D4F5"] # noqa: RUF012
@classmethod @classmethod
@@ -88,7 +86,7 @@ class ImageTemplate:
head_text: str, head_text: str,
tip_text: str | None, tip_text: str | None,
column_name: list[str], column_name: list[str],
data_list: list[list[str | tuple[Path | BuildImage, int, int]]], data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
row_space: int = 35, row_space: int = 35,
column_space: int = 30, column_space: int = 30,
padding: int = 5, padding: int = 5,
@@ -137,7 +135,7 @@ class ImageTemplate:
async def table( async def table(
cls, cls,
column_name: list[str], column_name: list[str],
data_list: list[list[str | tuple[Path | BuildImage, int, int]]], data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
row_space: int = 25, row_space: int = 25,
column_space: int = 10, column_space: int = 10,
padding: int = 5, padding: int = 5,
@@ -161,9 +159,11 @@ class ImageTemplate:
column_data = [] column_data = []
for i in range(len(column_name)): for i in range(len(column_name)):
c = [] c = []
for lst in data_list: for item in data_list:
if len(lst) > i: if len(item) > i:
c.append(lst[i]) c.append(
item[i] if isinstance(item[i], tuple | list) else str(item[i])
)
else: else:
c.append("") c.append("")
column_data.append(c) column_data.append(c)
@@ -176,7 +176,7 @@ class ImageTemplate:
if isinstance(s, tuple): if isinstance(s, tuple):
w = s[1] w = s[1]
else: else:
w, _ = BuildImage.get_text_size(s, font) w, _ = BuildImage.get_text_size(str(s), font)
if w > _temp["width"]: if w > _temp["width"]:
_temp["width"] = w _temp["width"] = w
build_data_list.append(_temp) build_data_list.append(_temp)
@@ -199,7 +199,7 @@ class ImageTemplate:
style = RowStyle(font=font) style = RowStyle(font=font)
if text_style: if text_style:
style = text_style(column_name[i], item) style = text_style(column_name[i], item)
if isinstance(item, tuple): if isinstance(item, tuple | list):
"""图片""" """图片"""
data, width, height = item data, width, height = item
image_ = None image_ = None
@@ -221,8 +221,6 @@ class ImageTemplate:
) )
cur_h += base_h + row_space cur_h += base_h + row_space
column_image_list.append(background) column_image_list.append(background)
# height = max([bk.height for bk in column_image_list])
# width = sum([bk.width for bk in column_image_list])
return await BuildImage.auto_paste( return await BuildImage.auto_paste(
column_image_list, len(column_image_list), column_space column_image_list, len(column_image_list), column_space
) )
+12 -2
View File
@@ -431,14 +431,20 @@ class AsyncHttpx:
class AsyncPlaywright: class AsyncPlaywright:
@classmethod @classmethod
@asynccontextmanager @asynccontextmanager
async def new_page(cls, **kwargs) -> AsyncGenerator[Page, None]: async def new_page(
cls, cookies: list[dict[str, Any]] | dict[str, Any] | None = None, **kwargs
) -> AsyncGenerator[Page, None]:
"""获取一个新页面 """获取一个新页面
参数: 参数:
user_agent: 请求头 cookies: cookies
""" """
browser = await get_browser() browser = await get_browser()
ctx = await browser.new_context(**kwargs) ctx = await browser.new_context(**kwargs)
if cookies:
if isinstance(cookies, dict):
cookies = [cookies]
await ctx.add_cookies(cookies) # type: ignore
page = await ctx.new_page() page = await ctx.new_page()
try: try:
yield page yield page
@@ -461,6 +467,7 @@ class AsyncPlaywright:
timeout: float | None = None, timeout: float | None = None,
type_: Literal["jpeg", "png"] | None = None, type_: Literal["jpeg", "png"] | None = None,
user_agent: str | None = None, user_agent: str | None = None,
cookies: list[dict[str, Any]] | dict[str, Any] | None = None,
**kwargs, **kwargs,
) -> UniMessage | None: ) -> UniMessage | None:
"""截图,该方法仅用于简单快捷截图,复杂截图请操作 page """截图,该方法仅用于简单快捷截图,复杂截图请操作 page
@@ -474,6 +481,8 @@ class AsyncPlaywright:
wait_until: 等待类型 wait_until: 等待类型
timeout: 超时限制 timeout: 超时限制
type_: 保存类型 type_: 保存类型
user_agent: user_agent
cookies: cookies
""" """
if viewport_size is None: if viewport_size is None:
viewport_size = {"width": 2560, "height": 1080} viewport_size = {"width": 2560, "height": 1080}
@@ -482,6 +491,7 @@ class AsyncPlaywright:
wait_time = wait_time * 1000 if wait_time else None wait_time = wait_time * 1000 if wait_time else None
element_list = [element] if isinstance(element, str) else element element_list = [element] if isinstance(element, str) else element
async with cls.new_page( async with cls.new_page(
cookies,
viewport=viewport_size, viewport=viewport_size,
user_agent=user_agent, user_agent=user_agent,
**kwargs, **kwargs,
+27
View File
@@ -0,0 +1,27 @@
from typing import ClassVar
class MessageManager:
data: ClassVar[dict[str, list[str]]] = {}
@classmethod
def add(cls, uid: str, msg_id: str):
if uid not in cls.data:
cls.data[uid] = []
cls.data[uid].append(msg_id)
cls.remove_check(uid)
@classmethod
def check(cls, uid: str, msg_id: str) -> bool:
return msg_id in cls.data.get(uid, [])
@classmethod
def remove_check(cls, uid: str):
if len(cls.data[uid]) > 200:
cls.data[uid] = cls.data[uid][100:]
@classmethod
def get(cls, uid: str) -> list[str]:
if uid in cls.data:
return cls.data[uid]
return []
+42 -2
View File
@@ -4,7 +4,17 @@ from pathlib import Path
import nonebot import nonebot
from pydantic import BaseModel from pydantic import BaseModel
from nonebot.adapters.onebot.v11 import Message, MessageSegment from nonebot.adapters.onebot.v11 import Message, MessageSegment
from nonebot_plugin_alconna import At, Text, AtAll, Image, Video, Voice, UniMessage from nonebot_plugin_alconna import (
At,
Text,
AtAll,
Image,
Video,
Voice,
Reference,
CustomNode,
UniMessage,
)
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.configs.config import BotConfig from zhenxun.configs.config import BotConfig
@@ -89,6 +99,36 @@ class MessageUtils:
message_list += cls.__build_message(_data) # type: ignore message_list += cls.__build_message(_data) # type: ignore
return UniMessage(message_list) return UniMessage(message_list)
@classmethod
def alc_forward_msg(
cls,
msg_list: list,
uin: str,
name: str,
) -> UniMessage:
"""生成自定义合并消息
参数:
msg_list: 消息列表
uin: 发送者 QQ
name: 自定义名称
返回:
list[dict]: 转发消息
"""
node_list = []
for _message in msg_list:
if isinstance(_message, list):
for i in range(len(_message.copy())):
if isinstance(_message[i], Path):
_message[i] = Image(
raw=BuildImage.open(_message[i]).pic2bytes()
)
node_list.append(
CustomNode(uid=uin, name=name, content=UniMessage(_message))
)
return UniMessage(Reference(nodes=node_list))
@classmethod @classmethod
def custom_forward_msg( def custom_forward_msg(
cls, cls,
@@ -138,7 +178,7 @@ class MessageUtils:
if isinstance(r, Text): if isinstance(r, Text):
s += str(r) s += str(r)
elif isinstance(r, Image): elif isinstance(r, Image):
if v := r.url or r.path: if v := r.url or r.path or r.raw:
s += MessageSegment.image(v) s += MessageSegment.image(v)
elif isinstance(r_list, Image): elif isinstance(r_list, Image):
if v := r_list.url or r_list.path: if v := r_list.url or r_list.path:
+1
View File
@@ -189,6 +189,7 @@ def change_pixiv_image_links(
url = ( url = (
url.replace("i.pximg.net", nginx_url) url.replace("i.pximg.net", nginx_url)
.replace("i.pixiv.cat", nginx_url) .replace("i.pixiv.cat", nginx_url)
.replace("i.pixiv.re", nginx_url)
.replace("_webp", "") .replace("_webp", "")
) )
return url return url