Compare commits

..
9 Commits
Author SHA1 Message Date
HibiKier 75abe23896 Update __version__ 2022-01-05 23:43:19 +08:00
HibiKier ad7a3e6261 Update update_info.json 2022-01-05 23:43:05 +08:00
HibiKier f1e359c666 Update README.md 2022-01-05 23:18:12 +08:00
HibiKier 0bfd71295e Delete x 2022-01-05 23:15:54 +08:00
HibiKier 379ad77791 Add files via upload 2022-01-05 23:15:41 +08:00
HibiKier 236bc40153 Create x 2022-01-05 23:14:05 +08:00
HibiKier c2a54a8164 Update update_info.json 2022-01-05 23:07:03 +08:00
HibiKier 4dbab21853 Update README.md 2022-01-05 22:57:29 +08:00
HibiKier 3063c5bc02 Update README.md 2022-01-05 22:57:08 +08:00
881 changed files with 11165 additions and 21117 deletions
-23
View File
@@ -1,23 +0,0 @@
---
name: Issue template
about: " issue template's purpose here."
title: ''
labels: ''
assignees: ''
---
### 系统版本:Ubuntu 20.04
### 真寻版本:0.1.5.3
### 错误截图
[img]
### 日志截图
[img]
### 错误说明
发生了xx错误...
-38
View File
@@ -1,38 +0,0 @@
name: Generate poetry.lock
on:
push:
branches:
- "main"
paths:
- 'pyproject.toml'
jobs:
poetry_lock:
name: Generate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: he0119/setup-python@main
with:
python-version: "3.9"
- name: poetry lock
run: |
rm -f poetry.lock
sed -i.bak "8,11d" pyproject.toml
poetry lock
mv pyproject.toml.bak pyproject.toml
# commit
- name: commit file
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add poetry.lock
git commit -m "Auto generate poetry.lock" -a
# push
- name: push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
-9
View File
@@ -1,9 +0,0 @@
### 系统版本:Ubuntu 20.04
### 真寻版本:0.1.5.3
### 错误截图
[img]
### 错误说明
发生了xx错误...
+3 -19
View File
@@ -138,26 +138,10 @@ dmypy.json
# Cython debug symbols # Cython debug symbols
cython_debug/ cython_debug/
demo.py
test.py test.py
server_ip.py server.py
member_activity_handle.py member_activity_handle.py
Yu-Gi-Oh/ Yu-Gi-Oh/
search_image/
black_word/
csgo/ csgo/
fantasy_card/
data/
log/
backup/
extensive_plugin/
test/
bot.py
data/
.env
.env.dev
resources/
!/configs/config.py
!/configs/config.yaml
!/.env
!/.env.dev
!/plugins/csgo_server/
!/plugins/activity/
+19 -577
View File
@@ -18,37 +18,30 @@
## 声明 ## 声明
此项目仅用于学习交流,请勿用于非法用途 此项目仅用于学习交流,请勿用于非法用途
# Nonebot2
<img style="height: 200px;width: 200px;" src="https://camo.githubusercontent.com/0ef71e86056da694c540790aa4a4e314396884d6c4fdb95362a7538b27a1b034/68747470733a2f2f76322e6e6f6e65626f742e6465762f6c6f676f2e706e67">
非常 [ **[NICE](https://github.com/nonebot/nonebot2)** ] 的OneBot框架
## 未完成的文档 ## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/) # [传送门](https://hibikier.github.io/zhenxun_bot/)
## 真寻的帮助 ## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令' 请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
<br>
## 普通帮助图片 普通帮助图片: <br>
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png) ![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## Web UI
[zhenxun_bot_webui](https://github.com/HibiKier/zhenxun_bot_webui)
## 一键安装脚本
[zhenxun_bot-deploy](https://github.com/AkashiCoin/zhenxun_bot-deploy)
## 提供符合真寻标准的插件仓库 ## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot) [AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
## 来点优点? ## 来点优点?
一.作为bot:
* 实现了许多功能,且提供了大量功能管理命令 * 实现了许多功能,且提供了大量功能管理命令
* __..... 更多详细请通过`传送门`查看文档!__
二.作为框架?:
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改 * 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息` * 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__` * 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
* 基本完善的权限管理
* __..... 更多详细请通过`传送门`查看文档!__ * __..... 更多详细请通过`传送门`查看文档!__
@@ -78,8 +71,6 @@
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件) - [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询 - [x] 原神便笺查询
- [x] 原神玩家查询 - [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包 - [x] 金币红包
- [x] 微博热搜 - [x] 微博热搜
- [x] B站主播/UP/番剧订阅 - [x] B站主播/UP/番剧订阅
@@ -114,7 +105,7 @@
- [x] 获取b站视频封面 - [x] 获取b站视频封面
- [x] 通过PID获取图片 - [x] 通过PID获取图片
- [x] 功能统计可视化 - [x] 功能统计可视化
- [x] 词云
- [x] 关于 - [x] 关于
### 已实现的管理员功能 ### 已实现的管理员功能
@@ -128,7 +119,7 @@
- [x] 移动图片 (同上) - [x] 移动图片 (同上)
- [x] 删除图片 (同上) - [x] 删除图片 (同上)
- [x] 群内B站订阅 - [x] 群内B站订阅
- [x] 词条设置 - [x] 群词条
- [x] 休息吧/醒来 - [x] 休息吧/醒来
### 已实现的超级用户功能 ### 已实现的超级用户功能
@@ -160,7 +151,6 @@
- [x] 清理临时数据 - [x] 清理临时数据
- [x] 增删群认证 - [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求 - [x] 同意/拒绝好友/群聊请求
- [x] 配置重载
#### 超级用户的被动技能 #### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群) - [x] 邀请入群提醒(别人邀请真寻入群)
@@ -185,7 +175,6 @@
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限 - [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统 - [x] 群权限系统
- [x] 定时更新权限 - [x] 定时更新权限
- [x] 自动配置重载
</details> </details>
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话 ## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
@@ -198,7 +187,7 @@
# 配置gocq # 配置gocq
在 https://github.com/Mrs4s/go-cqhttp 下载Releases最新版本,运行后选择反向代理, 在 https://github.com/Mrs4s/go-cqhttp 下载Releases最新版本,运行后选择反向代理,
后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/onebot/v11/ws 后将gocq的配置文件config.yml中的universal改为universal: ws://127.0.0.1:8080/cqhttp/ws
# 获取代码 # 获取代码
git clone https://github.com/HibiKier/zhenxun_bot.git git clone https://github.com/HibiKier/zhenxun_bot.git
@@ -207,14 +196,12 @@ git clone https://github.com/HibiKier/zhenxun_bot.git
cd zhenxun_bot cd zhenxun_bot
# 安装依赖 # 安装依赖
pip install poetry # 安装 poetry pip install -r requirements.txt
poetry install # 安装依赖
# 进行基础配置 # 进行基础配置
####请查看 配置 部分#### ####请查看 配置 部分####
# 开始运行 # 开始运行
poetry shell # 进入虚拟环境
python bot.py python bot.py
``` ```
@@ -234,558 +221,11 @@ python bot.py
``` ```
## 使用Docker
__Docker 单机版(仅真寻Bot)__
**点击下方的 GitHub 徽标查看教程**
[![Github](https://shields.io/badge/GITHUB-Sakuracio-4476AF?logo=github&style=for-the-badge)](https://github.com/Sakuracio/zhenxun_bot_docker)
[![DOCKER](https://shields.io/badge/docker-hibikier/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](https://hub.docker.com/r/hibikier/zhenxun_bot)
__Docker 全量版(包含 真寻Bot PostgreSQL数据库 go-cqhttp webui等)__
[![Github](https://shields.io/badge/GITHUB-SinKy--Yan-4476AF?logo=github&style=for-the-badge)](https://github.com/SinKy-Yan/zhenxunbot-docker)
[![DOCKER](https://shields.io/badge/docker-jyishit/zhenxun_bot-4476AF?logo=docker&style=for-the-badge)](https://hub.docker.com/r/jyishit/zhenxun_bot)
**点击上方的 GitHub 徽标查看教程**
PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能无法正常启动全量版容器**
## [爱发电](https://afdian.net/@HibiKier)
<details>
<summary>爱发电 以及 感谢投喂 </summary>
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
### 感谢名单
(可以告诉我你的 __github__ 地址,我偷偷换掉0v|)
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
[爱发电用户_4jrf](https://afdian.net/u/6b2cdcc817c611ed949152540025c377)
[爱发电用户_TBsd](https://afdian.net/u/db638b60217911ed9efd52540025c377)
[烟寒若雨](https://afdian.net/u/067bd2161eec11eda62b52540025c377)
[ln](https://afdian.net/u/b51914ba1c6611ed8a4e52540025c377)
[爱发电用户_b9S4](https://afdian.net/u/3d8f30581a2911edba6d52540025c377)
[爱发电用户_c58s](https://afdian.net/u/a6ad8dda195e11ed9a4152540025c377)
[爱发电用户_eNr9](https://afdian.net/u/05fdb41c0c9a11ed814952540025c377)
[MangataAkihi](https://github.com/Sakuracio)
[炀](https://afdian.net/u/69b76e9ec77b11ec874f52540025c377)
[爱发电用户_Bc6j](https://afdian.net/u/8546be24f44111eca64052540025c377)
[大魔王](https://github.com/xipesoy)
[CopilotLaLaLa](https://github.com/CopilotLaLaLa)
[嘿小欧](https://afdian.net/u/daa4bec4f24911ec82e552540025c377)
[回忆的秋千](https://afdian.net/u/e315d9c6f14f11ecbeef52540025c377)
[十年くん](https://github.com/shinianj)
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
[yajiwa](https://github.com/yajiwa)
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
</details>
## 更新 ## 更新
### 2022/9/16 ### 2021/1/5 \[v0.0.8.0]
* fix: bilibili_sub, azur_draw_card [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1090)
* 修复原神资源查询查询完毕后图片存储错误
* b站订阅发送 与 b站订阅 使用相同开关,即:关闭b站订阅
### 2022/9/10
* 自定义群欢迎消息参数不完全时提示报错
* 修改bt插件的url地址 [@pull/1067](https://github.com/HibiKier/zhenxun_bot/pull/1067)
### 2022/9/8
* 添加插件数据初始化判断
### 2022/9/4
* 旧词条提供图片迁移(需要重新获取old_model文件,并将数据库中user_qq为0的数据删除)
### 2022/9/3
* 原神玩家查询增加须弥地区 [@pull/1053](https://github.com/HibiKier/zhenxun_bot/pull/1053)
* av号覆盖全面,且修复av号链接 [@pull/1033](https://github.com/HibiKier/zhenxun_bot/pull/1033)
* 修复词条含有CQ回答的模糊匹配无法被解析
* 禁言检测图片在内存中获取图片hash
* B站订阅在群里中任意群管理员可以统一管理(原来为管理员1无法删除管理员2的订阅)
* 修复原神资源查询地图api数据变更导致更新的地图不完全
### 2022/8/27
* 修复签到积分双倍后,日志记录获得积分变4倍问题 [@pull/1044](https://github.com/HibiKier/zhenxun_bot/pull/1044)
### 2022/8/26
* 修复群管理员无法添加词条
* 修复词条关键词"问"前空格问题
### 2022/8/23
* 修了下模糊匹配 issue#1026 [@pull/1026](https://github.com/HibiKier/zhenxun_bot/pull/1026)
### 2022/8/22
* 修复首次安装时词条旧表出错(因为根本就没有这张表!)
* 取消配置替换定时任务,统一存储
* 对米游社cookie进行判断,整合米游社签到信息 [@pull/1014](https://github.com/HibiKier/zhenxun_bot/pull/1014)
* 修正尘歌壶和质变仪图片获取地址 [@pull/1010](https://github.com/HibiKier/zhenxun_bot/pull/1010)
* 修复词库问答 **很多** 问题[@pull/1012](https://github.com/HibiKier/zhenxun_bot/pull/1012)
### 2022/8/21 \[v0.1.6.3]
* 重构群词条,改为词库Plus,增加 精准|模糊|正则 问题匹配,问题与回答均支持at,image,face,超级用户额外提供 全局|私聊 词库设置,数据迁移目前只提供了问题和回答都是纯文本的词条
* 修复b站转发解析av号无法解析
* B站订阅直播订阅支持短号
* 开箱提供重置开箱命令,重置今日所有开箱数据(重置次数,并不会删除今日已开箱记录)
* 提供全局字典GDict,通过from utils.manager import GDict导入
* 适配omega 13w张图的数据结构表(建议删表重导)
* 除首次启动外将配置替换加入单次定时任务,加快启动速度
* fix: WordBank.check() [@pull/1008](https://github.com/HibiKier/zhenxun_bot/pull/1008)
* 改进插件 `我有一个朋友`,避免触发过于频繁 [@pull/1001](https://github.com/HibiKier/zhenxun_bot/pull/1001)
* 原神便笺新增洞天宝钱和参量质变仪提示 [@pull/1005](https://github.com/HibiKier/zhenxun_bot/pull/1005)
* 新增米游社签到功能,自动领取(白嫖)米游币 [@pull/991](https://github.com/HibiKier/zhenxun_bot/pull/991)
### 2022/8/14
* 修复epic未获取到时间时出错
* 修复订阅主播时动态获取的id是直播间id
### 2022/8/8
* 修复赛马娘重载卡池失败的问题 [@pull/969](https://github.com/HibiKier/zhenxun_bot/pull/969)
### 2022/8/3
* 修复 bili动态链接在投稿视频时URL和分割线连在一起 [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/961)
* 更新 Epic 免费游戏商城链接拼接规则 [@pull/957](https://github.com/HibiKier/zhenxun_bot/pull/957)
### 2022/8/6
* 修复了原神自动签到返回invalid request的问题,新增查看我的cookie命令 [@pull/971](https://github.com/HibiKier/zhenxun_bot/pull/971)
### 2022/7/31
* 对nonebot.beta4中PluginMeta进行解析
* 修改webui元祖类型无法正确转换类型
* 查看订阅以图片形式发送
* 修复pix无图时不会提醒而是出错
### 2022/7/30
* 替换了cos和bt的url [@pull/951](https://github.com/HibiKier/zhenxun_bot/pull/951)
* 发言记录统计添加日消息统计 [@pull/953](https://github.com/HibiKier/zhenxun_bot/pull/953)
### 2022/7/24 \[v0.1.6.2]
* 订阅up动态提供直链
### 2022/7/23
* 优化开箱次数判断
### 2022/7/16
* cos提供连发命令
* 修复B站视频解析中b23解析错误
### 2022/7/13
* B站订阅提供配置项`LIVE_MSG_AT_ALL`,`UP_MSG_AT_ALL`:控制UP动态投稿和直播是否AT全体
* 修改了Omega图库检测的数据库文件名
* game_utils提供了Game类
### 2022/7/7
* 微博热搜选择单条热搜时也会检测时效性 [@pull/891](https://github.com/HibiKier/zhenxun_bot/pull/891)
### 2022/7/4
* 修复商品未设置限购时无法购买
* 修复B站订阅UP动态 [@pull/886](https://github.com/HibiKier/zhenxun_bot/pull/886)
* 修复色图命令带tag时配置项`MAX_ONCE_NUM2FORWARD`无法生效
### 2022/7/3 \[v0.1.6.1]
* BagUser提供方法`check_props`方法,用于判断用户是否含有该道具并是否使用
* 优化商品显示图片,提供限制,限购,折扣提示
* 修复图库内图片无法被连续删除的问题 [@pull/879](https://github.com/HibiKier/zhenxun_bot/pull/879)
* 色图提供配置项`MAX_ONCE_NUM2FORWARD`:群聊中单次发送图片数量达到指定时使用合并转发
* 优化修复了商品修改命令
* B站订阅UP动态改为详情页截图
### 2022/6/28
* 修复色图插件api修改无法搜索图片 [@pull/866](https://github.com/HibiKier/zhenxun_bot/pull/866)
### 2022/6/26
* 替换了bt URL
* PIX当使用pid查询图片时,会发送该pid下所有图片
* 商店提供了每日购买次数限制
### 2022/6/24
* 对WSL虚拟机 代理 添加写法 [@pull/847](https://github.com/HibiKier/zhenxun_bot/pull/847)
### 2022/6/21
* update parse_bilibili [@pull/840](https://github.com/HibiKier/zhenxun_bot/pull/840)
### 2022/6/19 \[v0.1.6.0]
* 暂时使用hook修复webui中plugins2setting修改时会改变plugins2setting.cmd为字符串
* 修复原神树脂重复提醒的bug [@pull/828](https://github.com/HibiKier/zhenxun_bot/pull/828)
* 词条优化问题展示,修改含机器人昵称和@逻辑 [@pull/835](https://github.com/HibiKier/zhenxun_bot/pull/835)
### 2022/6/18
* 修复昵称系统`BLACK_WORD`为空时造成报错
* 修复特殊头像时背景透明化出错
* 修复text2image纯文本时换行时颜色不统一
* 优化webUI,当有插件出错时不会影响其他插件显示
* 优化Config类型注释 [@pull/830](https://github.com/HibiKier/zhenxun_bot/pull/830)
* 优化browser
### 2022/6/17
* 修复wordbank的一些小问题 @pull/820
### 2022/6/14
* AsyncHttpx提供可关闭 ssl 验证参数 [@pull/812](https://github.com/HibiKier/zhenxun_bot/pull/812)
* fix: 自动更新群组 [@pull/810](https://github.com/HibiKier/zhenxun_bot/pull/810)
* fix: 修复添加词条时对answer中图片的处理 [@pull/806](https://github.com/HibiKier/zhenxun_bot/pull/806)
* 修复pix中指定num无法生效
* 替换了bt URL
### 2022/6/13
* Windows 重启适配 [@pull/804](https://github.com/HibiKier/zhenxun_bot/pull/804)
* 色图等提供修改md5方法 [@pull/800](https://github.com/HibiKier/zhenxun_bot/pull/800)
* webui中token.json文件写入补充 [@pull/798](https://github.com/HibiKier/zhenxun_bot/pull/798)
* 修复识图某些图片可能会出错
### 2022/6/11
* pix当只有一个tag时且为数字时可以通过添加'#'防止判定为数量
### 2022/6/9
* 修复b站订阅同群二人以上相同订阅时发送多次信息
* 修复超级用户帮助中缺少了 ‘插件商店’ 相关帮助
* 昵称系统提供了详细帮助
### 2022/6/5 \[v0.1.5.9]
* webui修复plugin2setting中cmd从list转变为str
* 当命令`我的金币`被风控时将以图片形式发送
* fix gold_redbag [@pull/763](https://github.com/HibiKier/zhenxun_bot/pull/763)
* 金币红包功能增加更多封面 [@pull/764](https://github.com/HibiKier/zhenxun_bot/pull/764)
### 2022/6/3
* 插件仓库在已安装插件边上会提示\[已安装]
* 修复ShopRegister kwargs某些字段无效
* 调整了一下查看所有请求中的年龄绘制 [@pull/745](https://github.com/HibiKier/zhenxun_bot/pull/745)
* 修复原神树脂提醒bug [@pull/756](https://github.com/HibiKier/zhenxun_bot/pull/756)
### 2022/5/31
* 修复开启/关闭全部功能时帮助图片未重绘 [@pull/721](https://github.com/HibiKier/zhenxun_bot/pull/721)
* bot_friend_group将group_handle.修改为friend_handle. [@pull/711](https://github.com/HibiKier/zhenxun_bot/pull/711)
* 修复发红包图片背景未透明化;修复原神树脂提醒参数错误 [@pull/712](https://github.com/HibiKier/zhenxun_bot/pull/712)
* 修复抽卡插件:方舟更新UP池信息时,若公告的第一个池子过期会导致无法更新UP池信息 [@pull/707](https://github.com/HibiKier/zhenxun_bot/pull/707)
* 商店插件判断是否有requirements.txt文件 [@pull/705](https://github.com/HibiKier/zhenxun_bot/pull/705)
* 删除原神玩家查询api返回变更的多余键值
* 优化了text2image方法
### 2022/5/29 \[v0.1.5.8]
* 提供了真寻适配仓库的插件 安装/卸载 操作
* 暂时关闭了插件资源清空
* 通过指令安装插件时会在插件目录下生成plugin_info.json记录当前插件信息
### 2022/5/28
* 修复私聊无法添加昵称
* 修复原神玩家查询层岩巨渊地下矿区没开时报错
* 修复 ```休息吧``` 无法阻断戳一戳
* 当图库无图片时,戳一戳将略过发送图片
* 新增搜图提供配置项```ALLOW_GROUP_R18```:允许在群聊中使用r18参数
* 新增自动更新插件```UPDATE_REMIND```:新版本提醒,原配置项```AUTO_UPDATE_ZHENXUN```改为自动更新升级
* black_word新增当群权限为-1时不再检测该群
* 修复非超级用户绑定原神cookie会被black_word阻拦
* 修复微博热搜报错,发红包小bug [@pull/688](https://github.com/HibiKier/zhenxun_bot/pull/688)
* 更多的中文提示
### 2022/5/26
* 修复\[滴滴滴]会被转义成&#91;滴滴滴&#93;导致无法触发的问题
* 将一些错误以中文提示输出
* 更新BT搜索源地址 [@pull/668](https://github.com/HibiKier/zhenxun_bot/pull/668)
* 更新抽卡插件 [@pull/667](https://github.com/HibiKier/zhenxun_bot/pull/667)
### 2022/5/25
* 修复webui中CountLimit字段limit_type类型错误
* 修改nickname插件:一处文案错误,添加敏感词 [@pull/624](https://github.com/HibiKier/zhenxun_bot/pull/624)
* gamedraw的ba卡池搬运了过来并且进行了真寻的适配 [@pull/617](https://github.com/HibiKier/zhenxun_bot/pull/617)
* feat: stream downloading and progress [@pull/607](https://github.com/HibiKier/zhenxun_bot/pull/607)
* 修改翻译插件,添加缺失的冒号 [@pull/602](https://github.com/HibiKier/zhenxun_bot/pull/602)
* 修复碧蓝航线/明日方舟up池解析出错的问题 [@pull/610](https://github.com/HibiKier/zhenxun_bot/pull/602)
### 2022/5/24
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
### 2022/5/23 \[v0.1.5.6]
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
* 修复开箱更新价格错误传参
* 修复pix无法正确查询uid
* 新增色图插件添加配置项```ALLOW_GROUP_R18```:允许群聊中使用色图r
* 新增PIX插件添加配置项```ALLOW_GROUP_SETU```:允许非超级用户使用-s参数
* 新增PIX插件添加配置项```ALLOW_GROUP_R18```:允许非超级用户使用-r参数
### 2022/5/22 \[v0.1.5.4]
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
* 修复了epic有时获取新免费游戏消息时获取不到图片
* 修复好感度满时签到出错(虽然是不可能满的
* 修复原神资源图标下载路径错误
* 修复自动更新群组可能失败
### 2022/5/21
* 修复搜番无结果时报错无正确反馈
* 解锁了windows上无法使用playwright的限制
* 修复p搜对应pid有多张图时出错,改为连续发送图片
* 修复p搜对数字的错误判断
* 修复添加商品折扣无法正确添加
* 修复了bilibili订阅直播间订阅up名称不一致的问题
* 修复原神玩家查询没开地图时报错
* 最低priority修改为 999
* 修复刷屏检测失效
* 修复刷屏检测设置命令无法生效
* 优化刷屏显示设置禁言时长显示,并改为分钟
* 修复了多连开箱无法指定武器箱
* 修复识番链接无法正确获取
* 新增真寻入群时即刻刷新权限
* 提高了微博热搜截图的等待时间
### 2022/5/19
* fix: mihoyo bbs api changed [@pull/357](https://github.com/HibiKier/zhenxun_bot/pull/357)
* Add word_clouds [@pull/265](https://github.com/HibiKier/zhenxun_bot/pull/265)
* Fix wrong live streamer name [@pull/284](https://github.com/HibiKier/zhenxun_bot/pull/284)
### 2022/5/16
* 词条支持图片和@问题 [@pull/160](https://github.com/HibiKier/zhenxun_bot/pull/160)
### 2022/5/15
* 修复了商店商品无法正确添加
* 修复了多张色图无法正确发送
### 2022/5/14
* 修复B站动态生成失败的问题 [@pull/159](https://github.com/HibiKier/zhenxun_bot/pull/159)
### 2022/5/11
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
### 2022/5/9 \[v0.1.5.3]
* 替换了疫情API
* 修复了私聊.ban/.unban出错
### 2022/5/5
* 修改bilibili_sub插件在windows平台下报错 [@pull/153](https://github.com/HibiKier/zhenxun_bot/pull/153)
### 2022/5/3 \[v0.1.5.2]
* 商品使用函数可以添加特定参数,例如:user_id, group_id, ShopParam等以及自己提供的参数
* 添加商品注册装饰器shop_register
* 修复商品函数kwargs无法获取参数值
### 2022/5/1
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
* 新增敏感词检测(全新反击系统,是时候重拳出击了
### 2022/4/26
* 修复了群白名单无法正确添加
* 优化了管理员帮助图片,背景图层将位于最下层
* 修复了树脂140时不断提醒(未测试
* 新增了消息记录的消息排行
* WebUI新增CPU,内存,磁盘监控
* WebUI新增资源文件夹统计可视化
### 2022/4/12
* 修复b了命令私聊出错
### 2022/4/10 \[v0.1.4.7]
* 新增消息记录模块
* 丰富处理请求操作提示
* web ui新增配置项修改
### 2022/4/9
* fix: 更新问题,戳一戳图片路径问题 [@pull/144](https://github.com/HibiKier/zhenxun_bot/pull/144)
### 2022/4/8
* 修复原神玩家查询
### 2022/4/6
* update search_type [@pull/143](https://github.com/HibiKier/zhenxun_bot/pull/143)
### 2022/4/5 \[v0.1.4.6]
* 修复web修改插件后帮助图片生成错误
### 2022/4/4 \[v0.1.4.5]
* 替换了bt搜索URL
* 优化使用playwright的相关代码
* 原神玩家查询新增层岩巨渊探索
* 修复原神便笺角色头像黑框
* 修复同意群聊请求错误
* 提供webui方面的api
* 新增web-ui(前端简易管理页面插件)插件
### 2022/3/21
* 修复statistics_handle.py乱码
### 2022/3/18 \[v0.1.4.4]
* 修复戳一戳无法功能关闭与ban禁用
* 新增图片搜索 search_image
### 2022/3/7
* 优化增删权限插件
### 2022/3/6
* 修复树脂提醒无法开启
* 修复p搜图片路径错误
### 2022/3/3 \[v0.1.4.3]
* 修复手动同意群聊请求依旧退出
### 2022/3/1 \[v0.1.4.2]
* 0.1.4内容
### 2022/2/27 \[v0.1.4.1]
* 优化抽卡
### 2022/2/25 \[v0.1.4]
* PIX提供配置MAX_ONCE_NUM2FORWARD:当单次发送图片超过指定张数且在群聊时,将转为合并消息
* 修复点歌无法正确发送
* 修复我有一个朋友有时文本会包含CQ码
* 修复群欢消息被动控制文本未删除 [@pull/124](https://github.com/HibiKier/zhenxun_bot/pull/124)
* message_builder.image不再提供参数:abspath
### 2022/2/23
* 插件状态将从已加载插件模块中读取
* 修复b站订阅插件订阅失败问题
* 修复重启命令无法使用
### 2022/2/21 \[v0.1.3.2]
* 群权限为-1时,超级用户发送的命令依旧生效
* 当群权限为-1时,被动技能也将不会发送
* 修复功能开关,b站转发解析,复读 ignore无法使用
* 修复色图下载文件名与路径错误
* 修复被动技能提醒有时无法删除控制文本
### 2022/2/20 \[v0.1.3.1]
* 修复pix下载临时文件目录错误
* 修复AI,天气,发送图片ignore导致无法使用
* 修复纯文本被动技能提醒有时无法删除控制文本
### 2022/2/19 \[v0.1.3] (nonebot beta2!)
* 由于nonebot升级版本,提供更新建议(__该次升级将会导致nonebot.beta1以下的插件无法使用__
* 保证services,utils,configs,plugins,basic_plugins,文件夹均为最新
* 根目录有pyproject.toml与poetry.lock
* 执行命令:
* pip3 install poetry
* poetry install
* poetry shell
* playwright install chromium
* python3 bot.py
* 适配nonebot.beta2
* 删除图片搜索 nonebot_plugin_picsearcher
* 替换cos api
* 原神签到树脂提醒新增绑定群里,在某群绑定uid就会在某群发送提醒信息(有好友则私聊,需要重新绑定uid
* 修改update_info.json
* 修复原神资源查询下载数据失败时导致报错
* 优化BuildImage.circle()锯齿问题 [@pull/109](https://github.com/HibiKier/zhenxun_bot/pull/109)
* epic restful 替换 [@pull/119](https://github.com/HibiKier/zhenxun_bot/pull/119)
* fix: 修复远古时期残留的epic推送问题 [@pull/122](https://github.com/HibiKier/zhenxun_bot/pull/122)
### 2022/2/11
* 修复pix不使用反代无法下载图片
### 2022/2/10 \[v0.1.1]
* 修复购买道具出错
### 2022/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
* 修改了发送本地图库的matcher,改为on_message
* register_use可以通过返回值发送消息
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2022/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
* 修复/t回复带空格切分
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
* 修复签到图片中信息并未使用配置文件中的色图概率
* 修改原神大地图合成方式,改为先压缩再合成
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
* 数据库中所有belonging_group统一修改为group_id
* 商店将registered_use和register_goods更名为register_use何register_goods
* 商品注册提供了kwargs参数提供:
* bot
* event
* 特殊字段
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2022/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币 * 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins * 商店插件将作为内置插件移动至basic_plugins
@@ -839,8 +279,10 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
__..... 更多更新信息请查看文档__ __..... 更多更新信息请查看文档__
## Todo ## Todo
- [x] web管理 - [ ] docker容器
- [ ] web管理
## 感谢 ## 感谢
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议 [botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
@@ -854,5 +296,5 @@ __..... 更多更新信息请查看文档__
[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件 [H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件 [NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库 [Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人 [Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于HoshinoBot/NoneBot2的原神UID查询插件 [KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.6.3-fix1 __version__: v0.0.8.1
@@ -1,6 +1,7 @@
from nonebot import on_notice from nonebot import on_notice
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import GroupAdminNoticeEvent from nonebot.adapters.cqhttp import Bot, GroupAdminNoticeEvent
from nonebot.typing import T_State
from models.level_user import LevelUser from models.level_user import LevelUser
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from configs.config import Config from configs.config import Config
@@ -15,7 +16,7 @@ admin_notice = on_notice(priority=5)
@admin_notice.handle() @admin_notice.handle()
async def _(event: GroupAdminNoticeEvent): async def _(bot: Bot, event: GroupAdminNoticeEvent, state: T_State):
try: try:
nickname = ( nickname = (
await GroupInfoUser.get_member_info(event.user_id, event.group_id) await GroupInfoUser.get_member_info(event.user_id, event.group_id)
@@ -1,9 +1,9 @@
from nonebot import on_command from nonebot import on_command
from utils.utils import get_message_img from utils.utils import get_message_text, get_message_imgs
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message from nonebot.typing import T_State
from nonebot.params import CommandArg from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from ._data_source import custom_group_welcome from .data_source import custom_group_welcome
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from configs.config import Config from configs.config import Config
from services.log import logger from services.log import logger
@@ -35,14 +35,14 @@ custom_welcome = on_command(
@custom_welcome.handle() @custom_welcome.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = arg.extract_plain_text().strip()
img = get_message_img(event.json())
if not msg and not img:
await custom_welcome.finish(__plugin_usage__)
try: try:
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
if not msg and not imgs:
await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send( await custom_welcome.send(
await custom_group_welcome(msg, img, event.user_id, event.group_id), await custom_group_welcome(msg, imgs, event.user_id, event.group_id),
at_sender=True, at_sender=True,
) )
logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}") logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}")
@@ -1,323 +1,319 @@
from typing import List from typing import List
from nonebot.adapters.onebot.v11.message import MessageSegment from nonebot.adapters.cqhttp.message import MessageSegment
from services.log import logger from services.log import logger
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from utils.message_builder import image from utils.message_builder import image
from utils.utils import get_bot, get_matchers from utils.utils import get_local_proxy, get_bot
from pathlib import Path from pathlib import Path
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from datetime import datetime from datetime import datetime
from services.db_context import db from services.db_context import db
from models.level_user import LevelUser from models.level_user import LevelUser
from configs.config import Config from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
import asyncio import asyncio
import time import time
import os import os
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
async def group_current_status(group_id: int) -> str: async def group_current_status(group_id: int) -> str:
""" """
获取当前所有通知的开关 获取当前所有通知的开关
:param group_id: 群号 :param group_id: 群号
""" """
rst = "[被动技能 状态]\n" rst = "[被动技能 状态]\n"
_data = group_manager.get_task_data() _data = group_manager.get_task_data()
for task in _data.keys(): for task in _data.keys():
rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n' rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n'
return rst.strip() return rst.strip()
custom_welcome_msg_json = ( custom_welcome_msg_json = (
Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json"
) )
async def custom_group_welcome( async def custom_group_welcome(
msg: str, imgs: List[str], user_id: int, group_id: int msg: str, imgs: List[str], user_id: int, group_id: int
) -> str: ) -> str:
""" """
替换群欢迎消息 替换群欢迎消息
:param msg: 欢迎消息文本 :param msg: 欢迎消息文本
:param imgs: 欢迎消息图片,只取第一张 :param imgs: 欢迎消息图片,只取第一张
:param user_id: 用户id,用于log记录 :param user_id: 用户id,用于log记录
:param group_id: 群号 :param group_id: 群号
""" """
img_result = "" img_result = ""
img = imgs[0] if imgs else "" img = imgs[0] if imgs else ""
result = "" result = ""
if (DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").exists(): if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"):
(DATA_PATH / f"custom_welcome_msg/{group_id}.jpg").unlink() os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
if not custom_welcome_msg_json.exists(): if not custom_welcome_msg_json.exists():
custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True) custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True)
data = {} data = {}
else: else:
try: try:
data = json.load(open(custom_welcome_msg_json, "r")) data = json.load(open(custom_welcome_msg_json, "r"))
except FileNotFoundError: except FileNotFoundError:
data = {} data = {}
try: try:
if msg: if msg:
data[str(group_id)] = str(msg) data[str(group_id)] = str(msg)
json.dump( json.dump(
data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False
) )
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg result += msg
if img: if img:
await AsyncHttpx.download_file( await AsyncHttpx.download_file(
img, DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg" img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"
) )
img_result = image(DATA_PATH / "custom_welcome_msg" / f"{group_id}.jpg") img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e: except Exception as e:
logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}") logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}")
return "替换群消息失败.." return "替换群消息失败.."
return f"替换群欢迎消息成功:\n{result}" + img_result return f"替换群欢迎消息成功:\n{result}" + img_result
task_data = None task_data = None
async def change_group_switch(cmd: str, group_id: int, is_super: bool = False): async def change_group_switch(cmd: str, group_id: int, is_super: bool = False):
global task_data global task_data
""" """
修改群功能状态 修改群功能状态
:param cmd: 功能名称 :param cmd: 功能名称
:param group_id: 群号 :param group_id: 群号
:param is_super: 是否位超级用户,超级用户用于私聊开关功能状态 :param is_super: 是否位超级用户,超级用户用于私聊开关功能状态
""" """
if not task_data: if not task_data:
task_data = group_manager.get_task_data() task_data = group_manager.get_task_data()
group_help_file = DATA_PATH / "group_help" / f"{group_id}.png" group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
status = cmd[:2] status = cmd[:2]
cmd = cmd[2:] cmd = cmd[2:]
type_ = "plugin" type_ = "plugin"
modules = plugins2settings_manager.get_plugin_module(cmd, True) modules = plugins2settings_manager.get_plugin_module(cmd, True)
if cmd == "全部被动": if cmd == "全部被动":
for task in task_data: for task in task_data:
if status == "开启": if status == "开启":
if not await group_manager.check_group_task_status(group_id, task): if not await group_manager.check_group_task_status(group_id, task):
await group_manager.open_group_task(group_id, task) await group_manager.open_group_task(group_id, task)
else: else:
if await group_manager.check_group_task_status(group_id, task): if await group_manager.check_group_task_status(group_id, task):
await group_manager.close_group_task(group_id, task) await group_manager.close_group_task(group_id, task)
if group_help_file.exists(): if group_help_file.exists():
group_help_file.unlink() group_help_file.unlink()
return f"已 {status} 全部被动技能!" return f"已 {status} 全部被动技能!"
if cmd == "全部功能": if cmd == "全部功能":
for f in plugins2settings_manager.get_data(): for f in plugins2settings_manager.get_data():
if status == "开启": if status == "开启":
group_manager.unblock_plugin(f, group_id) group_manager.unblock_plugin(f, group_id)
else: else:
group_manager.block_plugin(f, group_id) group_manager.block_plugin(f, group_id)
if group_help_file.exists(): return f"已 {status} 全部功能!"
group_help_file.unlink() if cmd in [task_data[x] for x in task_data.keys()]:
return f"已 {status} 全部功能!" type_ = "task"
if cmd in [task_data[x] for x in task_data.keys()]: modules = [x for x in task_data.keys() if task_data[x] == cmd]
type_ = "task" for module in modules:
modules = [x for x in task_data.keys() if task_data[x] == cmd] if is_super:
for module in modules: module = f"{module}:super"
if is_super: if status == "开启":
module = f"{module}:super" if type_ == "task":
if status == "开启": if await group_manager.check_group_task_status(group_id, module):
if type_ == "task": return f"被动 {task_data[module]} 正处于开启状态!不要重复开启."
if await group_manager.check_group_task_status(group_id, module): await group_manager.open_group_task(group_id, module)
return f"被动 {task_data[module]} 正处于开启状态!不要重复开启." else:
await group_manager.open_group_task(group_id, module) if group_manager.get_plugin_status(module, group_id):
else: return f"功能 {cmd} 正处于开启状态!不要重复开启."
if group_manager.get_plugin_status(module, group_id): group_manager.unblock_plugin(module, group_id)
return f"功能 {cmd} 正处于开启状态!不要重复开启." else:
group_manager.unblock_plugin(module, group_id) if type_ == "task":
else: if not await group_manager.check_group_task_status(group_id, module):
if type_ == "task": return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭."
if not await group_manager.check_group_task_status(group_id, module): await group_manager.close_group_task(group_id, module)
return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭." else:
await group_manager.close_group_task(group_id, module) if not group_manager.get_plugin_status(module, group_id):
else: return f"功能 {cmd} 正处于关闭状态!不要重复关闭."
if not group_manager.get_plugin_status(module, group_id): group_manager.block_plugin(module, group_id)
return f"功能 {cmd} 正处于关闭状态!不要重复关闭." if group_help_file.exists():
group_manager.block_plugin(module, group_id) group_help_file.unlink()
if group_help_file.exists(): if is_super:
group_help_file.unlink() for file in os.listdir(Path(DATA_PATH) / "group_help"):
if is_super: file = Path(DATA_PATH) / "group_help" / file
for file in os.listdir(DATA_PATH / "group_help"): file.unlink()
file = DATA_PATH / "group_help" / file else:
file.unlink() _help_image = Path(DATA_PATH) / "group_help" / f"{group_id}.png"
else: if _help_image.exists():
_help_image = DATA_PATH / "group_help" / f"{group_id}.png" _help_image.unlink()
if _help_image.exists(): return f"{status} {cmd} 功能!"
_help_image.unlink()
return f"{status} {cmd} 功能!"
def set_plugin_status(cmd: str, block_type: str = "all"):
"""
def set_plugin_status(cmd: str, block_type: str = "all"): 设置插件功能状态(超级用户使用)
""" :param cmd: 功能名称
设置插件功能状态(超级用户使用) :param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊
:param cmd: 功能名称 """
:param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊 status = cmd[:2]
""" cmd = cmd[2:]
status = cmd[:2] module = plugins2settings_manager.get_plugin_module(cmd)
cmd = cmd[2:] if status == "开启":
module = plugins2settings_manager.get_plugin_module(cmd) plugins_manager.unblock_plugin(module)
if status == "开启": else:
plugins_manager.unblock_plugin(module) plugins_manager.block_plugin(module, block_type=block_type)
else: for file in os.listdir(Path(DATA_PATH) / "group_help"):
plugins_manager.block_plugin(module, block_type=block_type) file = Path(DATA_PATH) / "group_help" / file
for file in os.listdir(DATA_PATH / "group_help"): file.unlink()
file = DATA_PATH / "group_help" / file
file.unlink()
async def get_plugin_status():
"""
async def get_plugin_status(): 获取功能状态
""" """
获取功能状态 return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
"""
return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status)
def _get_plugin_status() -> MessageSegment:
"""
def _get_plugin_status() -> MessageSegment: 合成功能状态图片
""" """
合成功能状态图片 rst = "\t功能\n"
""" flag_str = "状态".rjust(4) + "\n"
rst = "\t功能\n" for module in plugins_manager.get_data():
flag_str = "状态".rjust(4) + "\n" flag = plugins_manager.get_plugin_block_type(module)
tmp_name = [] flag = flag.upper() + " CLOSE" if flag else "OPEN"
for matcher in get_matchers(): try:
if matcher.plugin_name not in tmp_name: plugin_name = plugins_manager.get(module)["plugin_name"]
tmp_name.append(matcher.plugin_name) if (
module = matcher.plugin_name "[Hidden]" in plugin_name
flag = plugins_manager.get_plugin_block_type(module) or "[Admin]" in plugin_name
flag = flag.upper() + " CLOSE" if flag else "OPEN" or "[Superuser]" in plugin_name
try: ):
plugin_name = plugins_manager.get(module)["plugin_name"] continue
if ( rst += f"{plugin_name}"
"[Hidden]" in plugin_name except KeyError:
or "[Admin]" in plugin_name rst += f"{module}"
or "[Superuser]" in plugin_name if plugins_manager.get(module)["error"]:
): rst += "[ERROR]"
continue rst += "\n"
rst += f"{plugin_name}" flag_str += f"{flag}\n"
except KeyError: height = len(rst.split("\n")) * 24
rst += f"{module}" a = BuildImage(250, height, font_size=20)
if plugins_manager.get(module)["error"]: a.text((10, 10), rst)
rst += "[ERROR]" b = BuildImage(200, height, font_size=20)
rst += "\n" b.text((10, 10), flag_str)
flag_str += f"{flag}\n" A = BuildImage(500, height)
height = len(rst.split("\n")) * 24 A.paste(a)
a = BuildImage(250, height, font_size=20) A.paste(b, (270, 0))
a.text((10, 10), rst) return image(b64=A.pic2bs4())
b = BuildImage(200, height, font_size=20)
b.text((10, 10), flag_str)
A = BuildImage(500, height) async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool:
A.paste(a) """
A.paste(b, (270, 0)) 更新群成员信息
return image(b64=A.pic2bs4()) :param group_id: 群号
:param remind_superuser: 失败信息提醒超级用户
"""
async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool: bot = get_bot()
""" _group_user_list = await bot.get_group_member_list(group_id=group_id)
更新群成员信息 _error_member_list = []
:param group_id: 群号 _exist_member_list = []
:param remind_superuser: 失败信息提醒超级用户 # try:
""" for user_info in _group_user_list:
bot = get_bot() if user_info["card"] == "":
_group_user_list = await bot.get_group_member_list(group_id=group_id) nickname = user_info["nickname"]
_error_member_list = [] else:
_exist_member_list = [] nickname = user_info["card"]
# try: async with db.transaction():
for user_info in _group_user_list: # 更新权限
nickname = user_info["card"] or user_info["nickname"] if (
async with db.transaction(): user_info["role"]
# 更新权限 in [
if ( "owner",
user_info["role"] "admin",
in [ ]
"owner", and not await LevelUser.is_group_flag(user_info["user_id"], group_id)
"admin", ):
] await LevelUser.set_level(
and not await LevelUser.is_group_flag(user_info["user_id"], group_id) user_info["user_id"],
): user_info["group_id"],
await LevelUser.set_level( Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
user_info["user_id"], )
user_info["group_id"], if str(user_info["user_id"]) in bot.config.superusers:
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"), await LevelUser.set_level(
) user_info["user_id"], user_info["group_id"], 9
if str(user_info["user_id"]) in bot.config.superusers: )
await LevelUser.set_level( user = await GroupInfoUser.get_member_info(
user_info["user_id"], user_info["group_id"], 9 user_info["user_id"], user_info["group_id"]
) )
user = await GroupInfoUser.get_member_info( if user:
user_info["user_id"], user_info["group_id"] if user.user_name != nickname:
) await user.update(user_name=nickname).apply()
if user: logger.info(
if user.user_name != nickname: f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功"
await user.update(user_name=nickname).apply() )
logger.info( _exist_member_list.append(int(user_info["user_id"]))
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功" continue
) join_time = datetime.strptime(
_exist_member_list.append(int(user_info["user_id"])) time.strftime(
continue "%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"])
join_time = datetime.strptime( ),
time.strftime( "%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"]) )
), if await GroupInfoUser.add_member_info(
"%Y-%m-%d %H:%M:%S", user_info["user_id"],
) user_info["group_id"],
if await GroupInfoUser.add_member_info( nickname,
user_info["user_id"], join_time,
user_info["group_id"], ):
nickname, _exist_member_list.append(int(user_info["user_id"]))
join_time, logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
): else:
_exist_member_list.append(int(user_info["user_id"])) _error_member_list.append(
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n"
else: )
_error_member_list.append( _del_member_list = list(
f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n" set(_exist_member_list).difference(
) set(await GroupInfoUser.get_group_member_id_list(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:
) if await GroupInfoUser.delete_member_info(del_user, group_id):
if _del_member_list: logger.info(f"退群用户{del_user} 所属{group_id} 已删除")
for del_user in _del_member_list: else:
if await GroupInfoUser.delete_member_info(del_user, group_id): logger.info(f"退群用户{del_user} 所属{group_id} 删除失败")
logger.info(f"退群用户{del_user} 所属{group_id} 已删除") if _error_member_list and remind_superuser:
else: result = ""
logger.info(f"退群用户{del_user} 所属{group_id} 删除失败") for error_user in _error_member_list:
if _error_member_list and remind_superuser: result += error_user
result = "" await bot.send_private_msg(
for error_user in _error_member_list: user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
result += error_user )
await bot.send_private_msg( return True
user_id=int(list(bot.config.superusers)[0]), message=result[:-1]
)
return True def set_group_bot_status(group_id: int, status: bool) -> str:
"""
设置群聊bot开关状态
def set_group_bot_status(group_id: int, status: bool) -> str: :param group_id: 群号
""" :param status: 状态
设置群聊bot开关状态 """
:param group_id: 群号 if status:
:param status: 状态 if group_manager.check_group_bot_status(group_id):
""" return "我还醒着呢!"
if status: group_manager.turn_on_group_bot_status(group_id)
if group_manager.check_group_bot_status(group_id): return "呜..醒来了..."
return "我还醒着呢!" else:
group_manager.turn_on_group_bot_status(group_id) group_manager.shutdown_group_bot_status(group_id)
return "呜..醒来了..." # for x in group_manager.get_task_data():
else: # group_manager.close_group_task(group_id, x)
group_manager.shutdown_group_bot_status(group_id) return "那我先睡觉了..."
# for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x)
return "那我先睡觉了..."
+10 -13
View File
@@ -1,4 +1,5 @@
from nonebot.adapters.onebot.v11 import Event from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.manager import group_manager, plugins2settings_manager from utils.manager import group_manager, plugins2settings_manager
from utils.utils import get_message_text from utils.utils import get_message_text
from services.log import logger from services.log import logger
@@ -6,10 +7,12 @@ from services.log import logger
cmd = [] cmd = []
def switch_rule(event: Event) -> bool: def switch_rule(bot: Bot, event: Event, state: T_State) -> bool:
""" """
检测文本是否是关闭功能命令 检测文本是否是关闭功能命令
:param bot: pass
:param event: pass :param event: pass
:param state: pass
""" """
global cmd global cmd
try: try:
@@ -24,17 +27,11 @@ def switch_rule(event: Event) -> bool:
_data = plugins2settings_manager.get_data() _data = plugins2settings_manager.get_data()
for key in _data: for key in _data:
try: try:
if isinstance(_data[key]["cmd"], list): for x in _data[key]["cmd"]:
for x in _data[key]["cmd"]: cmd.append(f"开启{x}")
cmd.append(f"开启{x}") cmd.append(f"关闭{x}")
cmd.append(f"关闭{x}") 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: except KeyError:
pass pass
msg = get_message_text(event.json()).split() msg = get_message_text(event.json()).split()
+17 -16
View File
@@ -1,7 +1,7 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, GROUP
from nonebot import on_command, on_message, on_regex from nonebot import on_command, on_message, on_regex
from nonebot.params import RegexGroup from nonebot.typing import T_State
from ._data_source import ( from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP
from .data_source import (
change_group_switch, change_group_switch,
set_plugin_status, set_plugin_status,
get_plugin_status, get_plugin_status,
@@ -12,8 +12,8 @@ from services.log import logger
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from typing import Tuple, Any
from .rule import switch_rule from .rule import switch_rule
import re
__zx_plugin_name__ = "群功能开关 [Admin]" __zx_plugin_name__ = "群功能开关 [Admin]"
@@ -65,7 +65,7 @@ group_status = on_regex("^(休息吧|醒来)$", permission=GROUP, priority=5, bl
@switch_rule_matcher.handle() @switch_rule_matcher.handle()
async def _(bot: Bot, event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
_cmd = get_message_text(event.json()).split()[0] _cmd = get_message_text(event.json()).split()[0]
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id)) await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id))
@@ -77,7 +77,7 @@ async def _(bot: Bot, event: MessageEvent):
if is_number(block_type): if is_number(block_type):
if not int(block_type) in [ if not int(block_type) in [
g["group_id"] g["group_id"]
for g in await bot.get_group_list() for g in await bot.get_group_list(self_id=int(bot.self_id))
]: ]:
await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}") await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}")
await change_group_switch(_cmd, int(block_type), True) await change_group_switch(_cmd, int(block_type), True)
@@ -104,21 +104,22 @@ async def _(bot: Bot, event: MessageEvent):
@plugins_status.handle() @plugins_status.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
await plugins_status.send(await get_plugin_status()) await plugins_status.send(await get_plugin_status())
@group_task_status.handle() @group_task_status.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await group_task_status.send(await group_current_status(event.group_id)) await group_task_status.send(await group_current_status(event.group_id))
@group_status.handle() @group_status.handle()
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
cmd = reg_group[0] r = re.search("^(休息吧|醒来)$", get_message_text(event.json()))
if cmd == "休息吧": if r:
msg = set_group_bot_status(event.group_id, False) if r.group(1) == "休息吧":
else: msg = set_group_bot_status(event.group_id, False)
msg = set_group_bot_status(event.group_id, True) else:
await group_status.send(msg) msg = set_group_bot_status(event.group_id, True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{cmd}") await group_status.send(msg)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用总开关命令:{r.group(1)}")
@@ -1,5 +1,5 @@
from utils.utils import scheduler, get_bot from utils.utils import scheduler, get_bot
from ._data_source import update_member_info from .data_source import update_member_info
from services.log import logger from services.log import logger
from models.group_info import GroupInfo from models.group_info import GroupInfo
from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError
@@ -1,7 +1,7 @@
from nonebot import on_command, on_notice from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent from nonebot.typing import T_State
from ._data_source import update_member_info from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from .data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]" __zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -25,7 +25,7 @@ refresh_member_group = on_command(
@refresh_member_group.handle() @refresh_member_group.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if await update_member_info(event.group_id): if await update_member_info(event.group_id):
await refresh_member_group.finish("更新群员信息成功!", at_sender=True) await refresh_member_group.finish("更新群员信息成功!", at_sender=True)
else: else:
@@ -36,6 +36,6 @@ group_increase_handle = on_notice(priority=1, block=False)
@group_increase_handle.handle() @group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent): async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
if event.user_id == int(bot.self_id): if event.user_id == int(bot.self_id):
await update_member_info(event.group_id) await update_member_info(event.group_id)
+1 -1
View File
@@ -1,7 +1,7 @@
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters import Bot from nonebot.adapters import Bot
from nonebot.adapters.onebot.v11 import GroupMessageEvent from nonebot.adapters.cqhttp import GroupMessageEvent
from utils.message_builder import image from utils.message_builder import image
from .data_source import create_help_image, admin_help_image from .data_source import create_help_image, admin_help_image
+14 -10
View File
@@ -3,16 +3,18 @@ from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
from utils.manager import group_manager from utils.manager import group_manager
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.cqhttp import Bot
from pathlib import Path
from nonebot import Driver from nonebot import Driver
import asyncio
import nonebot import nonebot
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
background = IMAGE_PATH / "background" / "0.png" background = Path(IMAGE_PATH) / "background" / "0.png"
admin_help_image = IMAGE_PATH / 'admin_help_img.png' admin_help_image = Path(IMAGE_PATH) / 'admin_help_img.png'
@driver.on_bot_connect @driver.on_bot_connect
@@ -26,10 +28,12 @@ async def create_help_image():
""" """
创建管理员帮助图片 创建管理员帮助图片
""" """
await _create_help_image() await asyncio.get_event_loop().run_in_executor(
None, _create_help_image
)
async def _create_help_image(): def _create_help_image():
""" """
创建管理员帮助图片 创建管理员帮助图片
""" """
@@ -38,7 +42,7 @@ async def _create_help_image():
width = 0 width = 0
_plugin_level = {} _plugin_level = {}
for matcher in _matchers: for matcher in _matchers:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module _module = _plugin.module
try: try:
plugin_name = _module.__getattribute__("__zx_plugin_name__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -67,7 +71,7 @@ async def _create_help_image():
x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23 x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23
width = width if width > x else x width = width if width > x else x
except AttributeError: except AttributeError:
logger.warning(f"获取管理插件 {matcher.plugin_name}: {plugin_name} 设置失败...") logger.warning(f"获取管理插件 {matcher.module}: {plugin_name} 设置失败...")
help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \ help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \
"[权限等级] 管理员帮助:\n\n" "[权限等级] 管理员帮助:\n\n"
x = list(_plugin_level.keys()) x = list(_plugin_level.keys())
@@ -82,9 +86,9 @@ async def _create_help_image():
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
A = BuildImage(width, height, font_size=24) A = BuildImage(width, height, font_size=24)
_background = BuildImage(width, height, background=background) _background = BuildImage(width, height, background=background)
await A.apaste(_background, alpha=True) A.text((150, 110), help_str)
await A.atext((150, 110), help_str) A.paste(_background, alpha=True)
await A.asave(admin_help_image) A.save(admin_help_image)
logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令') logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令')
+17 -15
View File
@@ -4,8 +4,9 @@ from nonebot import on_message
from services.log import logger from services.log import logger
from models.group_info import GroupInfo from models.group_info import GroupInfo
from models.friend_user import FriendUser from models.friend_user import FriendUser
from nonebot.adapters.onebot.v11 import ActionFailed from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from utils.manager import group_manager
from pathlib import Path from pathlib import Path
import shutil import shutil
@@ -41,7 +42,7 @@ Config.add_plugin_config(
) )
cx = on_message(priority=9999, block=False, rule=lambda: False) cx = on_message(priority=9, block=False)
# 早上好 # 早上好
@@ -56,11 +57,12 @@ async def _():
gl = await bot.get_group_list() gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl] gl = [g["group_id"] for g in gl]
for g in gl: for g in gl:
result = image("zao.jpg", "zhenxun") if await group_manager.check_group_task_status(g, 'zwa'):
try: result = image("zao.jpg", "zhenxun")
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result) try:
except ActionFailed: await bot.send_group_msg(group_id=g, message="早上好" + result)
logger.warning(f"{g} 发送早安失败") except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送早安")
except Exception as e: except Exception as e:
logger.error(f"早晚安错误 e:{e}") logger.error(f"早晚安错误 e:{e}")
@@ -77,13 +79,14 @@ async def _():
gl = await bot.get_group_list() gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl] gl = [g["group_id"] for g in gl]
for g in gl: for g in gl:
result = image("sleep.jpg", "zhenxun") if await group_manager.check_group_task_status(g, 'zwa'):
try: result = image("sleep.jpg", "zhenxun")
await bot.send_group_msg( try:
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result await bot.send_group_msg(
) group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result
except ActionFailed: )
logger.warning(f"{g} 发送晚安失败") except ActionFailed:
logger.warning(f"{g} 群被禁言中,无法发送晚安")
except Exception as e: except Exception as e:
logger.error(f"早晚安错误 e:{e}") logger.error(f"早晚安错误 e:{e}")
@@ -106,7 +109,6 @@ async def _():
group_info["group_name"], group_info["group_name"],
group_info["max_member_count"], group_info["max_member_count"],
group_info["member_count"], group_info["member_count"],
1
) )
logger.info(f"自动更新群组 {g} 信息成功") logger.info(f"自动更新群组 {g} 信息成功")
except Exception as e: except Exception as e:
+111 -76
View File
@@ -1,17 +1,14 @@
from typing import Tuple from nonebot import on_command
from configs.config import NICKNAME, Config
from models.ban_user import BanUser from models.ban_user import BanUser
from models.level_user import LevelUser from models.level_user import LevelUser
from nonebot import on_command from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import (Bot, GroupMessageEvent, Message, from nonebot.adapters.cqhttp import Bot
MessageEvent, PrivateMessageEvent) from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent, MessageEvent
from nonebot.params import Command, CommandArg from utils.utils import get_message_at, get_message_text, is_number
from configs.config import NICKNAME, Config
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from services.log import logger from services.log import logger
from utils.utils import get_message_at, is_number
from .data_source import a_ban, parse_ban_time
__zx_plugin_name__ = "封禁Ban用户 [Admin]" __zx_plugin_name__ = "封禁Ban用户 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -36,19 +33,19 @@ usage:
示例:b了 1234567 示例:b了 1234567
示例:.ban 12345567 示例:.ban 12345567
""".strip() """.strip()
__plugin_des__ = "你被逮捕了!丢进小黑屋!" __plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = [".ban [at] ?[小时] ?[分钟]", ".unban [at]", "b了 [at] [_superuser]"] __plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = 'HibiKier'
__plugin_settings__ = { __plugin_settings__ = {
"admin_level": Config.get_config("ban", "BAN_LEVEL"), "admin_level": Config.get_config("ban", "BAN_LEVEL"),
"cmd": [".ban", ".unban", "ban", "unban"], "cmd": ['.ban', '.unban', 'ban', 'unban']
} }
__plugin_configs__ = { __plugin_configs__ = {
"BAN_LEVEL [LEVEL]": { "BAN_LEVEL [LEVEL]": {
"value": 5, "value": 5,
"help": "ban/unban所需要的管理员权限等级", "help": "ban/unban所需要的管理员权限等级",
"default_value": 5, "default_value": 5
} }
} }
@@ -60,44 +57,66 @@ ban = on_command(
block=True, block=True,
) )
super_ban = on_command("b了", permission=SUPERUSER, priority=5, block=True) super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True)
@ban.handle() @ban.handle()
async def _( async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
bot: Bot,
event: GroupMessageEvent,
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
cmd = cmd[0]
result = "" result = ""
qq = get_message_at(event.json()) qq = get_message_at(event.json())
if qq: if qq:
qq = qq[0] qq = qq[0]
user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user_name["card"] or user_name["nickname"] user_name = user_name['card'] if user_name['card'] else user_name['nickname']
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
time = parse_ban_time(msg) if msg:
if isinstance(time, str): msg = msg.split()
await ban.finish(time, at_sender=True) if len(msg) == 2:
if cmd in [".ban", "/ban"]: if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
await ban.finish("参数必须是数字!", at_sender=True)
time = int(msg[0]) * 60 * 60 + int(msg[1]) * 60
else:
if not is_number(msg[0].strip()):
await ban.finish("参数必须是数字!", at_sender=True)
time = int(msg[0]) * 60 * 60
else:
time = -1
if state["_prefix"]["raw_command"] in [".ban", "/ban"]:
if ( if (
await LevelUser.get_user_level(event.user_id, event.group_id) await LevelUser.get_user_level(event.user_id, event.group_id)
<= await LevelUser.get_user_level(qq, event.group_id) <= await LevelUser.get_user_level(qq, event.group_id)
and str(event.user_id) not in bot.config.superusers and str(event.user_id) not in bot.config.superusers
): ):
await ban.finish( await ban.finish(
f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!", f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!",
at_sender=True, at_sender=True,
) )
result = await a_ban(qq, time, user_name, event) if await BanUser.ban(
qq, await LevelUser.get_user_level(event.user_id, event.group_id), time
):
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟"
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time/60} 分钟后解封"
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
else:
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
else: else:
if ( if (
await BanUser.check_ban_level( await BanUser.check_ban_level(
qq, await LevelUser.get_user_level(event.user_id, event.group_id) qq, await LevelUser.get_user_level(event.user_id, event.group_id)
) )
and str(event.user_id) not in bot.config.superusers and str(event.user_id) not in bot.config.superusers
): ):
await ban.finish( await ban.finish(
f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True
@@ -111,62 +130,78 @@ async def _(
result = f"{user_name} 不在黑名单!" result = f"{user_name} 不在黑名单!"
else: else:
await ban.finish("艾特人了吗??", at_sender=True) await ban.finish("艾特人了吗??", at_sender=True)
await ban.send(result, at_sender=True) await ban.finish(result, at_sender=True)
@ban.handle() @ban.handle()
async def _( async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
bot: Bot, if str(event.user_id) in bot.config.superusers:
event: PrivateMessageEvent, msg = get_message_text(event.json())
cmd: Tuple[str, ...] = Command(), msg = msg.split()
arg: Message = CommandArg(), if is_number(msg[0]):
): qq = int(msg[0])
cmd = cmd[0] if state["_prefix"]["raw_command"] in [".ban", "/ban"]:
msg = arg.extract_plain_text().strip() hour = 0
if msg: minute = 0
if str(event.user_id) in bot.config.superusers: if len(msg) > 1 and is_number(msg[1]):
msg_splt = msg.split() hour = int(msg[1])
if is_number(msg_splt[0]): if len(msg) > 2 and is_number(msg[2]):
minute = int(msg[2])
qq = int(msg_splt[0]) time = hour * 60 * 60 + minute * 60
msg = msg_splt[1:] time = time if time else -1
if cmd in [".ban", "/ban"]: if await BanUser.ban(
time = parse_ban_time(" ".join(msg)) qq, 9, time
if isinstance(time, str): ):
await ban.finish(time) logger.info(
result = await a_ban(qq, time, str(qq), event, 9) f"USER {event.user_id} 将 USER {qq} 封禁 时长 {time/60} 分钟"
else: )
if await BanUser.unban(qq): result = f"已经将 {qq} 加入{NICKNAME}的黑名单了!"
logger.info(f"USER {event.user_id} 将 USER {qq} 解禁") if time != -1:
result = f"已经把 {qq} 从黑名单中删除了!" result += f"将在 {time/60} 分钟后解封"
else: else:
result = f"{qq} 不在黑名单!" result += f"将在 ∞ 分钟后解封"
await ban.send(result) await ban.send(result)
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
else:
time += " 分钟"
await ban.send(f"{qq} 已在黑名单!预计 {time}后解封")
else: else:
await ban.finish( if await BanUser.unban(qq):
"qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?", at_sender=True logger.info(
) f"USER {event.user_id} 将 USER {qq} 解禁"
)
result = f"已经把 {qq} 从黑名单中删除了!"
else:
result = f"{qq} 不在黑名单!"
await ban.send(result)
else:
await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True)
@super_ban.handle() @super_ban.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
user_name = ""
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json()) qq = get_message_at(event.json())
if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user["card"] or user["nickname"]
else: else:
qq = arg.extract_plain_text().strip() qq = get_message_text(event.json())
if not is_number(qq): if not is_number(qq):
await super_ban.finish("对象qq必须为纯数字...") await super_ban.finish("对象qq必须为纯数字...")
qq = int(qq) qq = [qq]
user_name = qq
if qq: if qq:
qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)
user_name = user['card'] if user['card'] else user['nickname']
if not await BanUser.ban(qq, 10, 99999999): if not await BanUser.ban(qq, 10, 99999999):
await BanUser.unban(qq) await BanUser.unban(qq)
await BanUser.ban(qq, 10, 99999999) await BanUser.ban(qq, 10, 99999999)
await ban.send(f"已将 {user_name} 拉入黑名单!") await ban.send(f"已将 {user_name} 拉入黑名单!")
else: else:
await super_ban.send("需要添加被super ban的对象,可以使用at或者指定qq..") await super_ban.send('需要艾特被super ban的对象..')
-62
View File
@@ -1,62 +0,0 @@
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent
from configs.config import NICKNAME
from models.level_user import LevelUser
from utils.utils import is_number
from models.ban_user import BanUser
from services.log import logger
from typing import Union
def parse_ban_time(msg: str) -> Union[int, str]:
"""
解析ban时长
:param msg: 文本消息
"""
if not msg:
return -1
msg = msg.split()
if len(msg) == 1:
if not is_number(msg[0].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60
else:
if not is_number(msg[0].strip()) or not is_number(msg[1].strip()):
return "参数必须是数字!"
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
async def a_ban(
qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None
) -> str:
"""
ban
:param qq: qq
:param time: ban时长
:param user_name: ban用户昵称
:param event: event
:param ban_level: ban级别
"""
if isinstance(event, GroupMessageEvent):
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if await BanUser.ban(qq, ban_level, time):
logger.info(
f"USER {event.user_id} GROUP"
f" {event.group_id if isinstance(event, GroupMessageEvent) else ''} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
)
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1:
result += f"将在 {time / 60} 分钟后解封"
else:
result += f"将在 ∞ 分钟后解封"
else:
time = await BanUser.check_ban_time(qq)
if is_number(time):
time = abs(int(time))
if time < 60:
time = str(time) + " 秒"
else:
time = str(int(time / 60)) + " 分钟"
else:
time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封"
return result
+11 -11
View File
@@ -1,14 +1,13 @@
from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.params import CommandArg import asyncio
from utils.utils import get_message_img from utils.utils import get_message_text, get_message_imgs
from services.log import logger from services.log import logger
from utils.message_builder import image from utils.message_builder import image
from utils.manager import group_manager from utils.manager import group_manager
from configs.config import Config from configs.config import Config
import asyncio
__zx_plugin_name__ = "广播 [Superuser]" __zx_plugin_name__ = "广播 [Superuser]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -34,13 +33,14 @@ broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@broadcast.handle() @broadcast.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: Event, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
img_list = get_message_img(event.json()) imgs = get_message_imgs(event.json())
rst = "" rst = ""
for img in img_list: for img in imgs:
rst += image(img) rst += image(img)
gl = await bot.get_group_list() sid = bot.self_id
gl = await bot.get_group_list(self_id=sid)
gl = [ gl = [
g["group_id"] g["group_id"]
for g in gl for g in gl
@@ -56,7 +56,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊") await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊")
x += 0.25 x += 0.25
try: try:
await bot.send_group_msg(group_id=g, message=msg + rst) await bot.send_group_msg(self_id=sid, group_id=g, message=msg + rst)
logger.info(f"GROUP {g} 投递广播成功") logger.info(f"GROUP {g} 投递广播成功")
except Exception as e: except Exception as e:
logger.error(f"GROUP {g} 投递广播失败:{type(e)}") logger.error(f"GROUP {g} 投递广播失败:{type(e)}")
-3
View File
@@ -1,3 +0,0 @@
import nonebot
nonebot.load_plugins("basic_plugins/chat_history")
-6
View File
@@ -1,6 +0,0 @@
from nonebot.adapters.onebot.v11 import Event, MessageEvent
from configs.config import Config
def rule(event: Event) -> bool:
return Config.get_config("chat_history", "FLAG") and isinstance(event, MessageEvent)
@@ -1,39 +0,0 @@
from configs.config import Config
from models.chat_history import ChatHistory
from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from utils.utils import get_message_text
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
)
chat_history = on_message(rule=rule, priority=1, block=False)
@chat_history.handle()
async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg(
event.user_id, event.group_id, str(event.get_message()), get_message_text(event.json())
)
else:
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), get_message_text(event.json()))
# @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,114 +0,0 @@
from datetime import datetime, timedelta
import pytz
from models.chat_history import ChatHistory
from models.group_member_info import GroupInfoUser
from nonebot import on_regex
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from nonebot.params import RegexGroup
from utils.image_utils import BuildImage, text2image
from utils.utils import is_number
from utils.message_builder import image
from typing import Tuple, Any
__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:
try:
user_name = (await GroupInfoUser.get_member_info(uid, gid)).user_name
except AttributeError:
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))
+32 -43
View File
@@ -1,16 +1,15 @@
from nonebot import on_notice, on_request from nonebot import on_notice, on_request
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from models.level_user import LevelUser
from utils.message_builder import image from utils.message_builder import image
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from datetime import datetime from datetime import datetime
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
ActionFailed,
GroupIncreaseNoticeEvent, GroupIncreaseNoticeEvent,
GroupDecreaseNoticeEvent, GroupDecreaseNoticeEvent,
) )
from nonebot.adapters.cqhttp.exception import ActionFailed
from utils.manager import group_manager, plugins2settings_manager, requests_manager from utils.manager import group_manager, plugins2settings_manager, requests_manager
from configs.config import NICKNAME from configs.config import NICKNAME
from models.group_info import GroupInfo from models.group_info import GroupInfo
@@ -67,7 +66,7 @@ add_group = on_request(priority=1, block=False)
@group_increase_handle.handle() @group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent): async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
if event.user_id == int(bot.self_id): if event.user_id == int(bot.self_id):
group = await GroupInfo.get_group_info(event.group_id) group = await GroupInfo.get_group_info(event.group_id)
# 群聊不存在或被强制拉群,退出该群 # 群聊不存在或被强制拉群,退出该群
@@ -97,23 +96,6 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
for plugin in data.keys(): for plugin in data.keys():
if not data[plugin]["default_status"]: if not data[plugin]["default_status"]:
group_manager.block_plugin(plugin, event.group_id) group_manager.block_plugin(plugin, event.group_id)
# 即刻刷新权限
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
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
Config.get_config("admin_bot_manage", "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
)
else: else:
join_time = datetime.now() join_time = datetime.now()
user_info = await bot.get_group_member_info( user_info = await bot.get_group_member_info(
@@ -130,7 +112,9 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败")
# 群欢迎消息 # 群欢迎消息
if _flmt.check(event.group_id): if await group_manager.check_group_task_status(
event.group_id, "group_welcome"
) and _flmt.check(event.group_id):
_flmt.start_cd(event.group_id) _flmt.start_cd(event.group_id)
msg = "" msg = ""
img = "" img = ""
@@ -145,23 +129,23 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
if msg.find("[at]") != -1: if msg.find("[at]") != -1:
msg = msg.replace("[at]", "") msg = msg.replace("[at]", "")
at_flag = True at_flag = True
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists(): if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"):
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg") img = image(
abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"
)
if msg or img: if msg or img:
msg = msg.strip() + img
msg = "\n" + msg if at_flag else msg
await group_increase_handle.send( await group_increase_handle.send(
"[[_task|group_welcome]]" + msg, at_sender=at_flag "\n" + msg.strip() + img, at_sender=at_flag
) )
else: else:
await group_increase_handle.send( await group_increase_handle.send(
"[[_task|group_welcome]]新人快跑啊!!本群现状↓(快使用自定义!)" "新人快跑啊!!本群现状↓(快使用自定义!)"
+ image(random.choice(os.listdir(IMAGE_PATH / "qxz")), "qxz") + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz")
) )
@group_decrease_handle.handle() @group_decrease_handle.handle()
async def _(bot: Bot, event: GroupDecreaseNoticeEvent): async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict):
# 被踢出群 # 被踢出群
if event.sub_type == "kick_me": if event.sub_type == "kick_me":
group_id = event.group_id group_id = event.group_id
@@ -196,16 +180,21 @@ async def _(bot: Bot, event: GroupDecreaseNoticeEvent):
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功")
else: else:
logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败")
rst = "" if await group_manager.check_group_task_status(
if event.sub_type == "leave": event.group_id, "refund_group_remind"
rst = f"{user_name}离开了我们..." ):
if event.sub_type == "kick": rst = ""
operator = await bot.get_group_member_info( if event.sub_type == "leave":
user_id=event.operator_id, group_id=event.group_id rst = f"{user_name}离开了我们..."
) if event.sub_type == "kick":
operator_name = operator["card"] if operator["card"] else operator["nickname"] operator = await bot.get_group_member_info(
rst = f"{user_name} 被 {operator_name} 送走了." user_id=event.operator_id, group_id=event.group_id
try: )
await group_decrease_handle.send(f"[[_task|refund_group_remind]]{rst}") operator_name = (
except ActionFailed: operator["card"] if operator["card"] else operator["nickname"]
return )
rst = f"{user_name} 被 {operator_name} 送走了."
try:
await group_decrease_handle.send(f"{rst}")
except ActionFailed:
return
+9 -9
View File
@@ -1,24 +1,24 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
MessageEvent, MessageEvent,
GroupMessageEvent, GroupMessageEvent
Message
) )
from nonebot.params import CommandArg
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.rule import to_me from nonebot.rule import to_me
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from utils.message_builder import image from utils.message_builder import image
from .data_source import create_help_img, get_plugin_help from .data_source import create_help_img, get_plugin_help
from utils.utils import get_message_text
from pathlib import Path
import os import os
__zx_plugin_name__ = "帮助" __zx_plugin_name__ = "帮助"
group_help_path = DATA_PATH / "group_help" group_help_path = Path(DATA_PATH) / "group_help"
help_image = IMAGE_PATH / "help.png" help_image = Path(IMAGE_PATH) / "help.png"
simple_help_image = IMAGE_PATH / "simple_help.png" simple_help_image = Path(IMAGE_PATH) / "simple_help.png"
if help_image.exists(): if help_image.exists():
help_image.unlink() help_image.unlink()
if simple_help_image.exists(): if simple_help_image.exists():
@@ -44,8 +44,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@simple_help.handle() @simple_help.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
is_super = False is_super = False
if msg: if msg:
if '-super' in msg: if '-super' in msg:
+18 -23
View File
@@ -16,9 +16,9 @@ import nonebot
import os import os
random_bk_path = IMAGE_PATH / "background" / "help" / "simple_help" random_bk_path = Path(IMAGE_PATH) / "background" / "help" / "simple_help"
background = IMAGE_PATH / "background" / "0.png" background = Path(IMAGE_PATH) / "background" / "0.png"
async def create_help_img( async def create_help_img(
@@ -44,7 +44,7 @@ def _create_help_img(
:param help_image: 图片路径 :param help_image: 图片路径
:param simple_help_image: 简易帮助图片路径 :param simple_help_image: 简易帮助图片路径
""" """
_matchers = get_matchers(True) _matchers = get_matchers()
width = 0 width = 0
matchers_data = {} matchers_data = {}
_des_tmp = {} _des_tmp = {}
@@ -55,16 +55,12 @@ def _create_help_img(
# 插件分类 # 插件分类
for matcher in _matchers: for matcher in _matchers:
plugin_name = None plugin_name = None
_plugin = matcher.plugin _plugin = nonebot.plugin.get_plugin(matcher.module)
metadata = _plugin.metadata
if not _plugin:
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
continue
_module = _plugin.module _module = _plugin.module
try: try:
plugin_name = metadata.name if metadata else _module.__getattribute__("__zx_plugin_name__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
try: try:
plugin_des = metadata.description if metadata else _module.__getattribute__("__plugin_des__") plugin_des = _module.__getattribute__("__plugin_des__")
except AttributeError: except AttributeError:
plugin_des = "_" plugin_des = "_"
if ( if (
@@ -78,10 +74,10 @@ def _create_help_img(
plugin_type = ("normal",) plugin_type = ("normal",)
text_type = 0 text_type = 0
if plugins2settings_manager.get( if plugins2settings_manager.get(
matcher.plugin_name matcher.module
) and plugins2settings_manager[matcher.plugin_name].get("plugin_type"): ) and plugins2settings_manager[matcher.module].get("plugin_type"):
plugin_type = tuple( plugin_type = tuple(
plugins2settings_manager.get_plugin_data(matcher.plugin_name)[ plugins2settings_manager.get_plugin_data(matcher.module)[
"plugin_type" "plugin_type"
] ]
) )
@@ -115,7 +111,7 @@ def _create_help_img(
logger.warning(f"{type(e)}: {e}") logger.warning(f"{type(e)}: {e}")
else: else:
matchers_data[plugin_type][plugin_name] = { matchers_data[plugin_type][plugin_name] = {
"modules": matcher.plugin_name, "module": matcher.module,
"des": plugin_des, "des": plugin_des,
"cmd": plugin_cmd, "cmd": plugin_cmd,
"text_type": text_type, "text_type": text_type,
@@ -133,7 +129,7 @@ def _create_help_img(
_des_tmp[plugin_des] = plugin_name _des_tmp[plugin_des] = plugin_name
except AttributeError as e: except AttributeError as e:
if plugin_name not in _plugin_name_tmp: if plugin_name not in _plugin_name_tmp:
logger.warning(f"获取功能 {matcher.plugin_name}: {plugin_name} 设置失败...e:{e}") logger.warning(f"获取功能 {matcher.module}: {plugin_name} 设置失败...e:{e}")
if plugin_name not in _plugin_name_tmp: if plugin_name not in _plugin_name_tmp:
_plugin_name_tmp.append(plugin_name) _plugin_name_tmp.append(plugin_name)
help_img_list = [] help_img_list = []
@@ -151,16 +147,16 @@ def _create_help_img(
# 禁用flag # 禁用flag
flag = True flag = True
if plugins_manager.get_plugin_status( if plugins_manager.get_plugin_status(
matchers_data[type_][k]["modules"], "all" matchers_data[type_][k]["module"], "all"
): ):
flag = False flag = False
if group_id: if group_id:
flag = flag and plugins_manager.get_plugin_status( flag = flag and plugins_manager.get_plugin_status(
matchers_data[type_][k]["modules"], "group" matchers_data[type_][k]["module"], "group"
) )
simple_help_str += ( simple_help_str += (
f"{i+1}.{k}<|_|~|>" f"{i+1}.{k}<|_|~|>"
f"{group_manager.get_plugin_status(matchers_data[type_][k]['modules'], group_id) if group_id else '_'}|" f"{group_manager.get_plugin_status(matchers_data[type_][k]['module'], group_id) if group_id else '_'}|"
f"{flag}\n" f"{flag}\n"
) )
if matchers_data[type_][k]["text_type"] == 1: if matchers_data[type_][k]["text_type"] == 1:
@@ -281,7 +277,7 @@ def _create_help_img(
0, 0,
plain_text=msg, plain_text=msg,
font_size=24, font_size=24,
font="HYWenHei-85W.ttf", font="yuanshen.ttf",
) )
B.paste(text, (w, h), True) B.paste(text, (w, h), True)
h += 50 h += 50
@@ -293,7 +289,7 @@ def _create_help_img(
0, 0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
font_size=24, font_size=24,
font="HYWenHei-85W.ttf", font="yuanshen.ttf",
font_color=(231, 74, 57) font_color=(231, 74, 57)
), ),
(300, 10), (300, 10),
@@ -344,14 +340,13 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
if module: if module:
try: try:
plugin = nonebot.plugin.get_plugin(module) plugin = nonebot.plugin.get_plugin(module)
metadata = plugin.metadata
if plugin: if plugin:
if is_super: if is_super:
result = plugin.module.__getattribute__( result = plugin.module.__getattribute__(
"__plugin_superuser_usage__" "__plugin_superuser_usage__"
) )
else: else:
result = metadata.usage if metadata else plugin.module.__getattribute__("__plugin_usage__") result = plugin.module.__getattribute__("__plugin_usage__")
if result: if result:
width = 0 width = 0
for x in result.split("\n"): for x in result.split("\n"):
@@ -362,7 +357,7 @@ def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]:
bk = BuildImage( bk = BuildImage(
width, width,
height, height,
background=IMAGE_PATH / "background" / "1.png", background=Path(IMAGE_PATH) / "background" / "1.png",
) )
A.paste(bk, alpha=True) A.paste(bk, alpha=True)
A.text((int(width * 0.048), int(height * 0.21)), result) A.text((int(width * 0.048), int(height * 0.21)), result)
+83 -70
View File
@@ -1,5 +1,6 @@
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from models.friend_user import FriendUser from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from models.bag_user import BagUser from models.bag_user import BagUser
@@ -10,25 +11,19 @@ from utils.manager import (
plugins_manager, plugins_manager,
plugins2cd_manager, plugins2cd_manager,
plugins2block_manager, plugins2block_manager,
plugins2count_manager, plugins2count_manager
)
from ._utils import (
set_block_limit_false,
status_message_manager,
ignore_rst_module,
other_limit_plugins,
) )
from .utils import set_block_limit_false, status_message_manager
from nonebot.typing import T_State from nonebot.typing import T_State
from typing import Optional from typing import Optional
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
ActionFailed,
MessageEvent, MessageEvent,
GroupMessageEvent, GroupMessageEvent,
PokeNotifyEvent, PokeNotifyEvent,
PrivateMessageEvent, PrivateMessageEvent,
Message, Message,
Event, Event
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser from models.ban_user import BanUser
@@ -42,38 +37,35 @@ _flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD"))
ignore_rst_module = ["ai", "poke", "dialogue"]
# 权限检测 # 权限检测
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
module = matcher.plugin_name module = matcher.module
plugins2info_dict = plugins2settings_manager.get_data() plugins2info_dict = plugins2settings_manager.get_data()
# 功能的金币检测 ####################################### # 功能的金币检测 #######################################
# 功能的金币检测 ####################################### # 功能的金币检测 #######################################
# 功能的金币检测 ####################################### # 功能的金币检测 #######################################
cost_gold = 0 cost_gold = 0
if isinstance( if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'):
event, GroupMessageEvent cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold')
) and plugins2settings_manager.get_plugin_data(module).get("cost_gold"):
cost_gold = plugins2settings_manager.get_plugin_data(module).get("cost_gold")
if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold: if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold:
await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event) await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event)
raise IgnoredException(f"{module} 金币限制...") raise IgnoredException(f"{module} 金币限制...")
# 当插件不阻塞超级用户时,超级用户提前扣除金币 # 当插件不阻塞超级用户时,超级用户提前扣除金币
if ( if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]:
str(event.user_id) in bot.config.superusers
and not plugins2info_dict[module]["limit_superuser"]
):
await BagUser.spend_gold(event.user_id, event.group_id, cost_gold) await BagUser.spend_gold(event.user_id, event.group_id, cost_gold)
try: try:
if ( if (
(not isinstance(event, MessageEvent) and module not in other_limit_plugins) (not isinstance(event, MessageEvent) and module != "poke")
or await BanUser.is_ban(event.user_id) or await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers and str(event.user_id) not in bot.config.superusers
) or ( ) or (
str(event.user_id) in bot.config.superusers str(event.user_id) in bot.config.superusers
and plugins2info_dict.get(module) and plugins2info_dict.get(module)
and not plugins2info_dict[module]["limit_superuser"] and not plugins2info_dict[module]["limit_superuser"]
): ):
return return
except AttributeError: except AttributeError:
@@ -84,22 +76,16 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
_module = _plugin.module _module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
if ( if (
"[superuser]" in plugin_name.lower() "[superuser]" in plugin_name.lower()
and str(event.user_id) in bot.config.superusers and str(event.user_id) in bot.config.superusers
): ):
return return
except AttributeError: except AttributeError:
pass pass
# 群黑名单检测 群总开关检测 # 群黑名单检测 群总开关检测
if ( if isinstance(event, GroupMessageEvent) or matcher.module == "poke":
isinstance(event, GroupMessageEvent)
or matcher.plugin_name in other_limit_plugins
):
try: try:
if ( if group_manager.get_group_level(event.group_id) < 0:
group_manager.get_group_level(event.group_id) < 0
and str(event.user_id) not in bot.config.superusers
):
raise IgnoredException("群黑名单") raise IgnoredException("群黑名单")
if not group_manager.check_group_bot_status(event.group_id): if not group_manager.check_group_bot_status(event.group_id):
try: try:
@@ -109,16 +95,16 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
raise IgnoredException("功能总开关关闭状态") raise IgnoredException("功能总开关关闭状态")
except AttributeError: except AttributeError:
pass pass
if module in admin_manager.keys() and matcher.priority not in [1, 999]: if module in admin_manager.keys() and matcher.priority not in [1, 9]:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
# 个人权限 # 个人权限
if ( if (
not await LevelUser.check_level( not await LevelUser.check_level(
event.user_id, event.user_id,
event.group_id, event.group_id,
admin_manager.get_plugin_level(module), admin_manager.get_plugin_level(module),
) )
and admin_manager.get_plugin_level(module) > 0 and admin_manager.get_plugin_level(module) > 0
): ):
try: try:
if _flmt.check(event.user_id): if _flmt.check(event.user_id):
@@ -126,7 +112,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:"
f"{admin_manager.get_plugin_level(module)}", f"{admin_manager.get_plugin_level(module)}",
) )
except ActionFailed: except ActionFailed:
pass pass
@@ -136,7 +122,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
else: else:
if not await LevelUser.check_level( if not await LevelUser.check_level(
event.user_id, 0, admin_manager.get_plugin_level(module) event.user_id, 0, admin_manager.get_plugin_level(module)
): ):
try: try:
await bot.send_private_msg( await bot.send_private_msg(
@@ -149,17 +135,15 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if event.is_tome(): if event.is_tome():
status_message_manager.add(event.user_id) status_message_manager.add(event.user_id)
raise IgnoredException("权限不足") raise IgnoredException("权限不足")
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]: if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
# 戳一戳单独判断 # 戳一戳单独判断
if ( if isinstance(event, GroupMessageEvent) or (
isinstance(event, GroupMessageEvent) isinstance(event, PokeNotifyEvent) and event.group_id
or isinstance(event, PokeNotifyEvent)
or matcher.plugin_name in other_limit_plugins
): ):
if status_message_manager.get(event.group_id) is None: if status_message_manager.get(event.group_id) is None:
status_message_manager.delete(event.group_id) status_message_manager.delete(event.group_id)
if plugins2info_dict[module]["level"] > group_manager.get_group_level( if plugins2info_dict[module]["level"] > group_manager.get_group_level(
event.group_id event.group_id
): ):
try: try:
if _flmt_g.check(event.user_id) and module not in ignore_rst_module: if _flmt_g.check(event.user_id) and module not in ignore_rst_module:
@@ -177,7 +161,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not group_manager.get_plugin_status(module, event.group_id): if not group_manager.get_plugin_status(module, event.group_id):
try: try:
if module not in ignore_rst_module and _flmt_s.check( if module not in ignore_rst_module and _flmt_s.check(
event.group_id event.group_id
): ):
_flmt_s.start_cd(event.group_id) _flmt_s.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
@@ -193,8 +177,8 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not group_manager.get_plugin_status(f"{module}:super", event.group_id): if not group_manager.get_plugin_status(f"{module}:super", event.group_id):
try: try:
if ( if (
_flmt_s.check(event.group_id) _flmt_s.check(event.group_id)
and module not in ignore_rst_module and module not in ignore_rst_module
): ):
_flmt_s.start_cd(event.group_id) _flmt_s.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
@@ -210,8 +194,8 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not plugins_manager.get_plugin_status(module, block_type="group"): if not plugins_manager.get_plugin_status(module, block_type="group"):
try: try:
if ( if (
_flmt_c.check(event.group_id) _flmt_c.check(event.group_id)
and module not in ignore_rst_module and module not in ignore_rst_module
): ):
_flmt_c.start_cd(event.group_id) _flmt_c.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
@@ -241,14 +225,14 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
# 维护 # 维护
if not plugins_manager.get_plugin_status(module, block_type="all"): if not plugins_manager.get_plugin_status(module, block_type="all"):
if isinstance( if isinstance(
event, GroupMessageEvent event, GroupMessageEvent
) and group_manager.check_group_is_white(event.group_id): ) and group_manager.check_group_is_white(event.group_id):
return return
try: try:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
if ( if (
_flmt_c.check(event.group_id) _flmt_c.check(event.group_id)
and module not in ignore_rst_module and module not in ignore_rst_module
): ):
_flmt_c.start_cd(event.group_id) _flmt_c.start_cd(event.group_id)
await bot.send_group_msg( await bot.send_group_msg(
@@ -276,6 +260,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
# 以下为限制检测 ####################################################### # 以下为限制检测 #######################################################
# 以下为限制检测 ####################################################### # 以下为限制检测 #######################################################
# 以下为限制检测 ####################################################### # 以下为限制检测 #######################################################
# Cd # Cd
if plugins2cd_manager.check_plugin_cd_status(module): if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
@@ -283,9 +268,36 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
limit_type = plugin_cd_data["limit_type"] limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"] rst = plugin_cd_data["rst"]
if ( if (
(isinstance(event, PrivateMessageEvent) and check_type == "private") (isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group") or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
):
cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent):
cd_type_ = event.group_id
if not plugins2cd_manager.check(module, cd_type_):
if rst:
rst = await init_rst(rst, event)
await send_msg(rst, bot, event)
raise IgnoredException(f"{module} 正在cd中...")
else:
plugins2cd_manager.start_cd(module, cd_type_)
module = matcher.module
if (
isinstance(event, GroupMessageEvent)
and status_message_manager.get(event.group_id) is None
):
status_message_manager.delete(event.group_id)
# Cd
if plugins2cd_manager.check_plugin_cd_status(module):
plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module)
check_type = plugin_cd_data["check_type"]
limit_type = plugin_cd_data["limit_type"]
rst = plugin_cd_data["rst"]
if (
(isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group")
or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all"
): ):
cd_type_ = event.user_id cd_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent): if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -304,9 +316,9 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
limit_type = plugin_block_data["limit_type"] limit_type = plugin_block_data["limit_type"]
rst = plugin_block_data["rst"] rst = plugin_block_data["rst"]
if ( if (
(isinstance(event, PrivateMessageEvent) and check_type == "private") (isinstance(event, PrivateMessageEvent) and check_type == "private")
or (isinstance(event, GroupMessageEvent) and check_type == "group") or (isinstance(event, GroupMessageEvent) and check_type == "group")
or check_type == "all" or check_type == "all"
): ):
block_type_ = event.user_id block_type_ = event.user_id
if limit_type == "group" and isinstance(event, GroupMessageEvent): if limit_type == "group" and isinstance(event, GroupMessageEvent):
@@ -320,8 +332,8 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
plugins2block_manager.set_true(block_type_, module) plugins2block_manager.set_true(block_type_, module)
# Count # Count
if ( if (
plugins2count_manager.check_plugin_count_status(module) plugins2count_manager.check_plugin_count_status(module)
and event.user_id not in bot.config.superusers and event.user_id not in bot.config.superusers
): ):
plugin_count_data = plugins2count_manager.get_plugin_count_data(module) plugin_count_data = plugins2count_manager.get_plugin_count_data(module)
limit_type = plugin_count_data["limit_type"] limit_type = plugin_count_data["limit_type"]
@@ -370,15 +382,15 @@ async def _(
event: Event, event: Event,
state: T_State, state: T_State,
): ):
if not isinstance(event, MessageEvent) and matcher.plugin_name != "poke": if not isinstance(event, MessageEvent) and matcher.module != "poke":
return return
module = matcher.plugin_name module = matcher.module
set_block_limit_false(event, module) set_block_limit_false(event, module)
async def init_rst(rst: str, event: MessageEvent): async def init_rst(rst: str, event: MessageEvent):
if "[uname]" in rst: if "[uname]" in rst:
uname = event.sender.card or event.sender.nickname uname = event.sender.card if event.sender.card else event.sender.nickname
rst = rst.replace("[uname]", uname) rst = rst.replace("[uname]", uname)
if "[nickname]" in rst: if "[nickname]" in rst:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
@@ -391,3 +403,4 @@ async def init_rst(rst: str, event: MessageEvent):
if "[at]" in rst and isinstance(event, GroupMessageEvent): if "[at]" in rst and isinstance(event, GroupMessageEvent):
rst = rst.replace("[at]", str(at(event.user_id))) rst = rst.replace("[at]", str(at(event.user_id)))
return rst return rst
+19 -27
View File
@@ -1,19 +1,16 @@
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
Event,
MessageEvent, MessageEvent,
ActionFailed,
PokeNotifyEvent,
GroupMessageEvent, GroupMessageEvent,
) )
from configs.config import Config from configs.config import Config
from models.ban_user import BanUser from models.ban_user import BanUser
from utils.utils import is_number, static_flmt, FreqLimiter from utils.utils import is_number, static_flmt, FreqLimiter
from utils.message_builder import at from utils.message_builder import at
from ._utils import ignore_rst_module, other_limit_plugins
Config.add_plugin_config( Config.add_plugin_config(
@@ -28,19 +25,18 @@ _flmt = FreqLimiter(300)
# 检查是否被ban # 检查是否被ban
@run_preprocessor @run_preprocessor
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if ( try:
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent)) if (
and matcher.priority not in [1, 999] await BanUser.is_super_ban(event.user_id)
) or matcher.plugin_name in other_limit_plugins: and str(event.user_id) not in bot.config.superusers
try: ):
if ( raise IgnoredException("用户处于超级黑名单中")
await BanUser.is_super_ban(event.user_id) except AttributeError:
and str(event.user_id) not in bot.config.superusers pass
): if not isinstance(event, MessageEvent):
raise IgnoredException("用户处于超级黑名单中") return
except AttributeError: if matcher.type == "message" and matcher.priority not in [1, 9]:
pass
if ( if (
await BanUser.is_ban(event.user_id) await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers and str(event.user_id) not in bot.config.superusers
@@ -58,14 +54,10 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not static_flmt.check(event.user_id): if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id) static_flmt.start_cd(event.user_id)
if matcher.priority != 999: if matcher.priority != 9:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ( if ban_result and _flmt.check(event.user_id):
ban_result
and _flmt.check(event.user_id)
and matcher.plugin_name not in ignore_rst_module
):
_flmt.start_cd(event.user_id) _flmt.start_cd(event.user_id)
await bot.send_group_msg( await bot.send_group_msg(
group_id=event.group_id, group_id=event.group_id,
@@ -79,10 +71,10 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not static_flmt.check(event.user_id): if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id) static_flmt.start_cd(event.user_id)
if matcher.priority != 999: if matcher.priority != 9:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result and matcher.plugin_name not in ignore_rst_module: if ban_result:
await bot.send_private_msg( await bot.send_private_msg(
user_id=event.user_id, user_id=event.user_id,
message=at(event.user_id) message=at(event.user_id)
@@ -91,4 +83,4 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
) )
except ActionFailed: except ActionFailed:
pass pass
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
+3 -3
View File
@@ -1,9 +1,9 @@
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException from nonebot.message import run_preprocessor, IgnoredException
from nonebot.adapters.cqhttp.exception import ActionFailed
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
ActionFailed,
MessageEvent, MessageEvent,
GroupMessageEvent, GroupMessageEvent,
) )
@@ -25,7 +25,7 @@ _blmt = BanCheckLimiter(
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
if not isinstance(event, MessageEvent): if not isinstance(event, MessageEvent):
return return
if matcher.type == "message" and matcher.priority not in [1, 999]: if matcher.type == "message" and matcher.priority not in [1, 9]:
if state["_prefix"]["raw_command"]: if state["_prefix"]["raw_command"]:
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
if await BanUser.ban( if await BanUser.ban(
+4 -6
View File
@@ -1,8 +1,8 @@
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor, IgnoredException from nonebot.message import run_preprocessor, IgnoredException
from nonebot.typing import T_State from nonebot.typing import T_State
from ._utils import status_message_manager from .utils import status_message_manager
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
MessageEvent, MessageEvent,
PrivateMessageEvent, PrivateMessageEvent,
@@ -24,7 +24,7 @@ async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_Sta
async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
if not isinstance(event, MessageEvent): if not isinstance(event, MessageEvent):
return return
if matcher.type == "message" and matcher.plugin_name == "ai": if matcher.type == "message" and matcher.module == "ai":
if ( if (
isinstance(event, GroupMessageEvent) isinstance(event, GroupMessageEvent)
and not status_message_manager.check(event.group_id) and not status_message_manager.check(event.group_id)
@@ -35,9 +35,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
isinstance(event, PrivateMessageEvent) isinstance(event, PrivateMessageEvent)
and not status_message_manager.check(event.user_id) and not status_message_manager.check(event.user_id)
): ):
print(status_message_manager)
status_message_manager.delete(event.user_id) status_message_manager.delete(event.user_id)
raise IgnoredException("有命令就别说话了") raise IgnoredException("有命令就别说话了")
-47
View File
@@ -1,47 +0,0 @@
from nonebot.exception import MockApiException
from nonebot.adapters.onebot.v11 import Bot, Message
from utils.manager import group_manager
from typing import Dict, Any
import re
@Bot.on_calling_api
async def _(bot: Bot, api: str, data: Dict[str, Any]):
r = None
if (
(
(api == "send_msg" and data.get("message_type") == "group")
or api == "send_group_msg"
)
and (
(
r := re.search(
"^\[\[_task\|(.*)]]",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
or (
r := re.search(
"^&#91;&#91;_task\|(.*)&#93;&#93;",
data["message"].strip()
if isinstance(data["message"], str)
else str(data["message"]["text"]).strip(),
)
)
)
and r.group(1) in group_manager.get_task_data().keys()
):
task = r.group(1)
group_id = data["group_id"]
if group_manager.get_group_level(
group_id
) < 0 or not await group_manager.check_group_task_status(group_id, task):
raise MockApiException(f"被动技能 {task} 处于关闭状态...")
else:
msg = str(data["message"]).strip()
msg = msg.replace(f"&#91;&#91;_task|{task}&#93;&#93;", "").replace(
f"[[_task|{task}]]", ""
)
data["message"] = Message(msg)
+48 -52
View File
@@ -1,52 +1,48 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent
from utils.manager import plugins2block_manager, StaticData from utils.manager import plugins2block_manager, StaticData
import time import time
ignore_rst_module = ["ai", "poke", "dialogue"]
class StatusMessageManager(StaticData):
other_limit_plugins = ["poke"]
def __init__(self):
super().__init__(None)
class StatusMessageManager(StaticData):
def add(self, id_: int):
def __init__(self): self._data[id_] = time.time()
super().__init__(None)
def delete(self, id_: int):
def add(self, id_: int): if self._data.get(id_):
self._data[id_] = time.time() del self._data[id_]
def delete(self, id_: int): def check(self, id_: int, t: int = 30) -> bool:
if self._data.get(id_): if self._data.get(id_):
del self._data[id_] if time.time() - self._data[id_] > t:
del self._data[id_]
def check(self, id_: int, t: int = 30) -> bool: return True
if self._data.get(id_): return False
if time.time() - self._data[id_] > t: return True
del self._data[id_]
return True
return False status_message_manager = StatusMessageManager()
return True
def set_block_limit_false(event, module):
status_message_manager = StatusMessageManager() """
设置用户block为false
:param event: event
def set_block_limit_false(event, module): :param module: 插件模块
""" """
设置用户block为false if plugins2block_manager.check_plugin_block_status(module):
:param event: event plugin_block_data = plugins2block_manager.get_plugin_block_data(module)
:param module: 插件模块 check_type = plugin_block_data["check_type"]
""" limit_type = plugin_block_data["limit_type"]
if plugins2block_manager.check_plugin_block_status(module): if not (
plugin_block_data = plugins2block_manager.get_plugin_block_data(module) (isinstance(event, GroupMessageEvent) and check_type == "private")
check_type = plugin_block_data["check_type"] or (isinstance(event, PrivateMessageEvent) and check_type == "group")
limit_type = plugin_block_data["limit_type"] ):
if not ( block_type_ = event.user_id
(isinstance(event, GroupMessageEvent) and check_type == "private") if limit_type == "group" and isinstance(event, GroupMessageEvent):
or (isinstance(event, PrivateMessageEvent) and check_type == "group") block_type_ = event.group_id
): plugins2block_manager.set_false(block_type_, module)
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)
+2 -2
View File
@@ -2,7 +2,7 @@ from nonebot.matcher import Matcher
from nonebot.message import run_postprocessor from nonebot.message import run_postprocessor
from typing import Optional from typing import Optional
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot, Event from nonebot.adapters.cqhttp import Bot, Event
from utils.manager import withdraw_message_manager from utils.manager import withdraw_message_manager
import asyncio import asyncio
@@ -25,4 +25,4 @@ async def _(
async def _withdraw_message(bot: Bot, id_: int, time: int): async def _withdraw_message(bot: Bot, id_: int, time: int):
await asyncio.sleep(time) await asyncio.sleep(time)
await bot.delete_msg(message_id=id_) await bot.delete_msg(message_id=id_, self_id=int(bot.self_id))
+5 -3
View File
@@ -11,9 +11,11 @@ from .init_plugins_limit import (
) )
from .init import init from .init import init
from .check_plugin_status import check_plugin_status from .check_plugin_status import check_plugin_status
from nonebot.adapters.onebot.v11 import Bot from utils.manager import admin_manager
from nonebot.adapters.cqhttp import Bot
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from services.log import logger from services.log import logger
from pathlib import Path
from nonebot import Driver from nonebot import Driver
import nonebot import nonebot
@@ -27,12 +29,12 @@ driver: Driver = nonebot.get_driver()
@driver.on_startup @driver.on_startup
async def _(): def _():
""" """
初始化数据 初始化数据
""" """
_flag = False _flag = False
config_file = DATA_PATH / "configs" / "plugins2config.yaml" config_file = Path(DATA_PATH) / "configs" / "plugins2config.yaml"
if not config_file.exists(): if not config_file.exists():
_flag = True _flag = True
init() init()
@@ -1,5 +1,5 @@
from utils.manager import plugins_manager from utils.manager import plugins_manager
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.cqhttp import Bot
async def check_plugin_status(bot: Bot): async def check_plugin_status(bot: Bot):
@@ -5,6 +5,7 @@ from utils.manager import (
plugins2settings_manager, plugins2settings_manager,
plugins2block_manager, plugins2block_manager,
plugins_manager, plugins_manager,
resources_manager
) )
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
@@ -19,36 +20,26 @@ def init_none_plugin_count_manager():
""" """
清除已删除插件数据 清除已删除插件数据
""" """
modules = [x.plugin_name for x in get_matchers()] modules = [x.module for x in get_matchers()]
plugins_manager_list = list(plugins_manager.keys()) for module in none_plugin_count_manager.keys():
for module in plugins_manager_list: if module not in modules:
if module not in modules or none_plugin_count_manager.check(module): none_plugin_count_manager.add_count(module)
else:
none_plugin_count_manager.reset(module)
if none_plugin_count_manager.check(module):
try: try:
plugin_name = plugins_manager.get(module)["plugin_name"] plugin_name = plugins_manager.get(module)["plugin_name"]
except (AttributeError, KeyError): except (AttributeError, KeyError):
plugin_name = "" plugin_name = ""
if none_plugin_count_manager.check(module): try:
try: plugins2settings_manager.delete(module)
plugins2settings_manager.delete(module) plugins2count_manager.delete(module)
plugins2settings_manager.save() plugins2cd_manager.delete(module)
plugins2count_manager.delete(module) plugins2block_manager.delete(module)
plugins2count_manager.save() plugins_manager.delete(module)
plugins2cd_manager.delete(module) resources_manager.remove_resource(module)
plugins2cd_manager.save() logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
plugins2block_manager.delete(module) except Exception as e:
plugins2block_manager.save() logger.error(
plugins_manager.delete(module) f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
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} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
else:
none_plugin_count_manager.reset(module)
none_plugin_count_manager.save()
@@ -1,14 +1,11 @@
import asyncio
from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from ruamel.yaml import round_trip_load, round_trip_dump, YAML from ruamel.yaml import round_trip_load, round_trip_dump, YAML
from utils.manager import admin_manager, plugins_manager from utils.manager import admin_manager, plugins_manager
from configs.config import Config from configs.config import Config
from services.log import logger from services.log import logger
from utils.text_utils import prompt2cn
from utils.utils import get_matchers from utils.utils import get_matchers
from utils.utils import scheduler
from ruamel import yaml from ruamel import yaml
import nonebot
_yaml = YAML(typ="safe") _yaml = YAML(typ="safe")
@@ -18,56 +15,40 @@ def init_plugins_config(data_path):
""" """
初始化插件数据配置 初始化插件数据配置
""" """
plugins2config_file = data_path / "configs" / "plugins2config.yaml" plugins2config_file = Path(data_path) / "configs" / "plugins2config.yaml"
plugins2config_file.parent.mkdir(parents=True, exist_ok=True) plugins2config_file.parent.mkdir(parents=True, exist_ok=True)
_data = {} _data = {}
if plugins2config_file.exists(): if plugins2config_file.exists():
_data = _yaml.load(open(plugins2config_file, "r", encoding="utf8")) _data = _yaml.load(open(plugins2config_file, "r", encoding="utf8"))
_matchers = get_matchers(True) _matchers = get_matchers()
# 优先使用 metadata 数据
for matcher in _matchers: for matcher in _matchers:
_plugin = matcher.plugin _plugin = nonebot.plugin.get_plugin(matcher.module)
if not _plugin:
continue
metadata = _plugin.metadata
try: try:
_module = _plugin.module _module = _plugin.module
except AttributeError: except AttributeError:
continue continue
plugin_version = None try:
if metadata: plugin_version = _module.__getattribute__("__plugin_version__")
plugin_version = metadata.extra.get("version") except AttributeError:
if not plugin_version: plugin_version = None
try: try:
plugin_version = _module.__getattribute__("__plugin_version__") plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError: except AttributeError:
pass continue
if metadata and metadata.config: # 插件配置版本更新或为Version为None或不在存储配置内
plugin_configs = {} if (
for key, value in metadata.config.__fields__.items():
plugin_configs[key.upper()] = {
"value": value.default,
"default_value": value.default
}
else:
try:
plugin_configs = _module.__getattribute__("__plugin_configs__")
except AttributeError:
continue
# 插件配置版本更新或为Version为None或不在存储配置内,当使用metadata时,必定更新
if isinstance(plugin_version, str) or (
plugin_version is None plugin_version is None
or ( or (
_data.get(matcher.plugin_name) _data.get(matcher.module)
and _data[matcher.plugin_name].keys() != plugin_configs.keys() and _data[matcher.module].keys() != plugin_configs.keys()
) )
or plugin_version > plugins_manager.get(matcher.plugin_name)["version"] or plugin_version > plugins_manager.get(matcher.module)["version"]
or matcher.plugin_name not in _data.keys() or matcher.module not in _data.keys()
): ):
for key in plugin_configs: for key in plugin_configs:
if isinstance(plugin_configs[key], dict): if isinstance(plugin_configs[key], dict):
Config.add_plugin_config( Config.add_plugin_config(
matcher.plugin_name, matcher.module,
key, key,
plugin_configs[key].get("value"), plugin_configs[key].get("value"),
help_=plugin_configs[key].get("help"), help_=plugin_configs[key].get("help"),
@@ -75,14 +56,12 @@ def init_plugins_config(data_path):
_override=True, _override=True,
) )
else: else:
Config.add_plugin_config( Config.add_plugin_config(matcher.module, key, plugin_configs[key])
matcher.plugin_name, key, plugin_configs[key]
)
else: else:
plugin_configs = _data[matcher.plugin_name] plugin_configs = _data[matcher.module]
for key in plugin_configs: for key in plugin_configs:
Config.add_plugin_config( Config.add_plugin_config(
matcher.plugin_name, matcher.module,
key, key,
plugin_configs[key]["value"], plugin_configs[key]["value"],
help_=plugin_configs[key]["help"], help_=plugin_configs[key]["help"],
@@ -100,105 +79,72 @@ def init_plugins_config(data_path):
_data[plugin].yaml_set_start_comment(plugin_name, indent=2) _data[plugin].yaml_set_start_comment(plugin_name, indent=2)
# 初始化未设置的管理员权限等级 # 初始化未设置的管理员权限等级
for k, v in Config.get_admin_level_data(): for k, v in Config.get_admin_level_data():
try: admin_manager.set_admin_level(k, v)
admin_manager.set_admin_level(k, v)
except KeyError as e:
raise KeyError(f"{e} ****** 请检查是否有插件加载失败 ******")
# 存完插件基本设置 # 存完插件基本设置
with open(plugins2config_file, "w", encoding="utf8") as wf: with open(plugins2config_file, "w", encoding="utf8") as wf:
round_trip_dump( round_trip_dump(
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True _data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
) )
user_config_file = Path() / "configs" / "config.yaml" # 再开始读取用户配置
# if not user_config_file.exists(): user_config_file = Path() / "configs" / "config.yaml"
_replace_config() _data = {}
# else: _tmp_data = {}
# logger.info('五分钟后将进行配置数据替换,请注意...') if user_config_file.exists():
# scheduler.add_job( with open(user_config_file, "r", encoding="utf8") as f:
# _replace_config, _data = _yaml.load(f)
# "date", # 数据替换
# run_date=datetime.now() + timedelta(minutes=5), for plugin in Config.keys():
# id=f"_replace_config" _tmp_data[plugin] = {}
# ) for k in Config[plugin].keys():
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].keys():
try:
if _data.get(plugin) and k in _data[plugin].keys(): if _data.get(plugin) and k in _data[plugin].keys():
Config.set_config(plugin, k, _data[plugin][k]) Config.set_config(plugin, k, _data[plugin][k])
if level2module := Config.get_level2module(plugin, k): if level2module := Config.get_level2module(plugin, k):
try: admin_manager.set_admin_level(level2module, _data[plugin][k])
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) _tmp_data[plugin][k] = Config.get_config(plugin, k)
except AttributeError as e: Config.save()
raise AttributeError( temp_file = Path() / "configs" / "temp_config.yaml"
f"{e}\n" + prompt2cn("可能为config.yaml配置文件填写不规范", 46) try:
with open(temp_file, "w", encoding="utf8") as wf:
yaml.dump(
_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True
) )
Config.save() with open(temp_file, "r", encoding="utf8") as rf:
temp_file = Path() / "configs" / "temp_config.yaml" _data = round_trip_load(rf)
try: # 添加注释
with open(temp_file, "w", encoding="utf8") as wf: for plugin in _data.keys():
yaml.dump( rst = ""
_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:
plugin_data = Config.get(plugin)
for x in list(Config.get(plugin).keys()):
try:
_x = plugin_data[x].get("name")
if _x:
plugin_name = _x
except AttributeError:
pass
except (KeyError, AttributeError):
plugin_name = None plugin_name = None
if not plugin_name:
try: try:
plugin_name = plugins_manager.get(plugin)["plugin_name"] plugin_data = Config.get(plugin)
except (AttributeError, TypeError): for x in list(Config.get(plugin).keys()):
plugin_name = plugin try:
plugin_name = ( _x = plugin_data[x].get("name")
plugin_name.replace("[Hidden]", "") if _x:
.replace("[Superuser]", "") plugin_name = _x
.replace("[Admin]", "") except AttributeError:
.strip() pass
) except (KeyError, AttributeError):
rst += plugin_name + "\n" plugin_name = None
for k in _data[plugin].keys(): if not plugin_name:
rst += f'{k}: {Config[plugin][k]["help"]}' + "\n" try:
_data[plugin].yaml_set_start_comment(rst[:-1], indent=2) plugin_name = plugins_manager.get(plugin)["plugin_name"]
with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf: except (AttributeError, TypeError):
round_trip_dump( plugin_name = plugin
_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True plugin_name = (
) plugin_name.replace("[Hidden]", "")
except Exception as e: .replace("[Superuser]", "")
logger.error(f"生成简易配置注释错误 {type(e)}:{e}") .replace("[Admin]", "")
if temp_file.exists(): .strip()
temp_file.unlink() )
rst += plugin_name + "\n"
for k in _data[plugin].keys():
rst += f'{k}: {Config[plugin][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()
@@ -17,80 +17,66 @@ def init_plugins_data(data_path):
""" """
初始化插件数据信息 初始化插件数据信息
""" """
plugin2data_file = data_path / "manager" / "plugin_manager.json" plugin2data_file = Path(data_path) / "manager" / "plugin_manager.json"
plugin2data_file.parent.mkdir(parents=True, exist_ok=True) plugin2data_file.parent.mkdir(parents=True, exist_ok=True)
_data = {} _data = {}
if plugin2data_file.exists(): if plugin2data_file.exists():
_data = json.load(open(plugin2data_file, "r", encoding="utf8")) _data = json.load(open(plugin2data_file, "r", encoding="utf8"))
_matchers = get_matchers(True) _matchers = get_matchers()
for matcher in _matchers: for matcher in _matchers:
_plugin = matcher.plugin _plugin = nonebot.plugin.get_plugin(matcher.module)
if not _plugin:
continue
metadata = _plugin.metadata
try: try:
_module = _plugin.module _module = _plugin.module
except AttributeError: except AttributeError:
if matcher.plugin_name not in _data.keys(): if matcher.module not in _data.keys():
plugins_manager.add_plugin_data( plugins_manager.add_plugin_data(
matcher.plugin_name, matcher.plugin_name, error=True matcher.module, matcher.module, error=True
) )
else: else:
plugins_manager.set_module_data(matcher.plugin_name, "error", True) plugins_manager.set_module_data(matcher.module, "error", True)
plugin_data = plugins_manager.get(matcher.plugin_name) plugin_data = plugins_manager.get(matcher.module)
if plugin_data: if plugin_data:
plugins_manager.set_module_data( plugins_manager.set_module_data(
matcher.plugin_name, "version", plugin_data.get("version") matcher.module, "version", plugin_data.get("version")
) )
else: else:
plugin_version = None try:
if metadata: plugin_version = _module.__getattribute__("__plugin_version__")
plugin_version = metadata.extra.get("version") except AttributeError:
if not plugin_version: plugin_version = None
try: try:
plugin_version = _module.__getattribute__("__plugin_version__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError: except AttributeError:
pass plugin_name = matcher.module
if metadata:
plugin_name = metadata.name
else:
try:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError:
plugin_name = matcher.plugin_name
plugin_author = None
if metadata:
plugin_author = metadata.extra.get('author')
try: try:
plugin_author = _module.__getattribute__("__plugin_author__") plugin_author = _module.__getattribute__("__plugin_author__")
except AttributeError: except AttributeError:
pass plugin_author = None
if matcher.plugin_name in plugins_manager.keys(): if matcher.module in plugins_manager.keys():
plugins_manager.set_module_data(matcher.plugin_name, "error", False) plugins_manager.set_module_data(matcher.module, "error", False)
if matcher.plugin_name not in plugins_manager.keys(): if matcher.module not in plugins_manager.keys():
plugins_manager.add_plugin_data( plugins_manager.add_plugin_data(
matcher.plugin_name, matcher.module,
plugin_name=plugin_name, plugin_name=plugin_name,
author=plugin_author, author=plugin_author,
version=plugin_version, version=plugin_version,
) )
# metadata不检测version elif plugins_manager[matcher.module]["version"] is None or (
elif isinstance(plugin_version, str) or plugins_manager[matcher.plugin_name]["version"] is None or (
plugin_version is not None plugin_version is not None
and plugin_version > plugins_manager[matcher.plugin_name]["version"] and plugin_version > plugins_manager[matcher.module]["version"]
): ):
plugins_manager.set_module_data( plugins_manager.set_module_data(
matcher.plugin_name, "plugin_name", plugin_name matcher.module, "plugin_name", plugin_name
) )
plugins_manager.set_module_data(matcher.plugin_name, "author", plugin_author) plugins_manager.set_module_data(matcher.module, "author", plugin_author)
plugins_manager.set_module_data( plugins_manager.set_module_data(
matcher.plugin_name, "version", plugin_version matcher.module, "version", plugin_version
) )
if matcher.plugin_name in _data.keys(): if matcher.module in _data.keys():
plugins_manager.set_module_data( plugins_manager.set_module_data(
matcher.plugin_name, "error", _data[matcher.plugin_name]["error"] matcher.module, "error", _data[matcher.module]["error"]
) )
plugins_manager.set_module_data( plugins_manager.set_module_data(
matcher.plugin_name, "plugin_name", _data[matcher.plugin_name]["plugin_name"] matcher.module, "plugin_name", _data[matcher.module]["plugin_name"]
) )
plugins_manager.save() plugins_manager.save()
@@ -17,18 +17,18 @@ def init_plugins_cd_limit(data_path):
""" """
加载 cd 限制 加载 cd 限制
""" """
plugins2cd_file = data_path / "configs" / "plugins2cd.yaml" plugins2cd_file = Path(data_path) / "configs" / "plugins2cd.yaml"
plugins2cd_file.parent.mkdir(exist_ok=True, parents=True) plugins2cd_file.parent.mkdir(exist_ok=True, parents=True)
_data = {} _data = {}
_matchers = get_matchers() _matchers = get_matchers()
for matcher in _matchers: for matcher in _matchers:
if not plugins2cd_manager.get_plugin_cd_data(matcher.plugin_name): if not plugins2cd_manager.get_plugin_cd_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit( plugins2cd_manager.add_cd_limit(
matcher.plugin_name, **plugin_cd_limit matcher.module, data_dict=plugin_cd_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -65,18 +65,18 @@ def init_plugins_block_limit(data_path):
""" """
加载阻塞限制 加载阻塞限制
""" """
plugins2block_file = data_path / "configs" / "plugins2block.yaml" plugins2block_file = Path(data_path) / "configs" / "plugins2block.yaml"
plugins2block_file.parent.mkdir(exist_ok=True, parents=True) plugins2block_file.parent.mkdir(exist_ok=True, parents=True)
_data = {} _data = {}
_matchers = get_matchers() _matchers = get_matchers()
for matcher in _matchers: for matcher in _matchers:
if not plugins2block_manager.get_plugin_block_data(matcher.plugin_name): if not plugins2block_manager.get_plugin_block_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit( plugins2block_manager.add_block_limit(
matcher.plugin_name, **plugin_block_limit matcher.module, data_dict=plugin_block_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -113,18 +113,18 @@ def init_plugins_count_limit(data_path):
""" """
加载次数限制 加载次数限制
""" """
plugins2count_file = data_path / "configs" / "plugins2count.yaml" plugins2count_file = Path(data_path) / "configs" / "plugins2count.yaml"
plugins2count_file.parent.mkdir(exist_ok=True, parents=True) plugins2count_file.parent.mkdir(exist_ok=True, parents=True)
_data = {} _data = {}
_matchers = get_matchers() _matchers = get_matchers()
for matcher in _matchers: for matcher in _matchers:
if not plugins2count_manager.get_plugin_count_data(matcher.plugin_name): if not plugins2count_manager.get_plugin_count_data(matcher.module):
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__") plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
plugins2count_manager.add_count_limit( plugins2count_manager.add_count_limit(
matcher.plugin_name, **plugin_count_limit matcher.module, data_dict=plugin_count_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -11,13 +11,13 @@ def init_plugins_resources():
""" """
_tmp = [] _tmp = []
for matcher in get_matchers(): for matcher in get_matchers():
if matcher.plugin_name not in _tmp: if matcher.module not in _tmp:
_tmp.append(matcher.plugin_name) _tmp.append(matcher.module)
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
except AttributeError: except AttributeError:
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,资源控制未加载...") logger.warning(f"插件 {matcher.module} 加载失败...,资源控制未加载...")
else: else:
try: try:
resources = _module.__getattribute__("__plugin_resources__") resources = _module.__getattribute__("__plugin_resources__")
@@ -26,8 +26,18 @@ def init_plugins_resources():
else: else:
path = Path(_module.__getattribute__("__file__")).parent path = Path(_module.__getattribute__("__file__")).parent
for resource in resources.keys(): for resource in resources.keys():
resources_manager.add_resource( resources_manager.add_resource(matcher.module, (path / resource).absolute(), resources[resource])
matcher.plugin_name, path / resource, resources[resource]
)
resources_manager.save() resources_manager.save()
resources_manager.start_move() resources_manager.start_move()
@@ -14,108 +14,96 @@ def init_plugins_settings(data_path: str):
""" """
初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__ 初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__
""" """
plugins2settings_file = data_path / "configs" / "plugins2settings.yaml" plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True) plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers(True) _matchers = get_matchers()
_tmp_module = {} _tmp_module = {}
_tmp = [] _tmp = []
for x in plugins2settings_manager.keys(): for x in plugins2settings_manager.keys():
try: try:
_plugin = nonebot.plugin.get_plugin(x) _plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module _module = _plugin.module
metadata = _plugin.metadata plugin_name = _module.__getattribute__("__zx_plugin_name__")
plugin_name = (
metadata.name
if metadata
else _module.__getattribute__("__zx_plugin_name__")
)
_tmp_module[x] = plugin_name _tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e: except (KeyError, AttributeError) as e:
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}") logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = "" _tmp_module[x] = ""
for matcher in _matchers: for matcher in _matchers:
try: if matcher.module not in plugins2settings_manager.keys():
if matcher.plugin_name not in plugins2settings_manager.keys(): _plugin = nonebot.plugin.get_plugin(matcher.module)
_plugin = matcher.plugin try:
if not _plugin: _module = _plugin.module
continue except AttributeError:
metadata = _plugin.metadata logger.warning(f"插件 {matcher.module} 加载失败...,插件控制未加载.")
else:
try: try:
_module = _plugin.module plugin_name = _module.__getattribute__("__zx_plugin_name__")
except AttributeError: if "[admin]" in plugin_name.lower():
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
else:
try:
if metadata:
plugin_name = metadata.name
else:
plugin_name = _module.__getattribute__("__zx_plugin_name__")
if "[admin]" in plugin_name.lower():
try:
admin_settings = _module.__getattribute__(
"__plugin_settings__"
)
level = admin_settings["admin_level"]
cmd = admin_settings.get("cmd")
except (AttributeError, KeyError):
level = 5
cmd = None
if level is None:
level = 5
admin_manager.add_admin_plugin_settings(
matcher.plugin_name, cmd, level
)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.plugin_name in plugins2settings_manager.keys()
):
continue
except AttributeError:
if matcher.plugin_name not in _tmp:
logger.warning(
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
)
else:
_tmp_module[matcher.plugin_name] = plugin_name
try: try:
plugin_settings = _module.__getattribute__( admin_settings = _module.__getattribute__(
"__plugin_settings__" "__plugin_settings__"
) )
except AttributeError: level = admin_settings["admin_level"]
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]} cmd = admin_settings.get("cmd")
if not plugin_settings.get("cost_gold"): except (AttributeError, KeyError):
plugin_settings["cost_gold"] = 0 level = 5
cmd = None
if level is None:
level = 5
admin_manager.add_admin_plugin_settings(
matcher.module, cmd, level
)
if (
"[hidden]" in plugin_name.lower()
or "[admin]" in plugin_name.lower()
or "[superuser]" in plugin_name.lower()
or matcher.module in plugins2settings_manager.keys()
):
continue
except AttributeError:
if matcher.module not in _tmp:
logger.warning(
f"获取插件 {matcher.module} __zx_plugin_name__ 失败...,插件控制未加载."
)
else:
try:
_tmp_module[matcher.module] = plugin_name
plugin_settings = _module.__getattribute__(
"__plugin_settings__"
)
if plugin_settings.get('cost_gold') is None:
plugin_settings['cost_gold'] = 0
if ( if (
plugin_settings.get("cmd") is not None plugin_settings["cmd"] is not None
and plugin_name not in plugin_settings["cmd"] and plugin_name not in plugin_settings["cmd"]
): ):
plugin_settings["cmd"].append(plugin_name) plugin_settings["cmd"].append(plugin_name)
if plugins2settings_manager.get( if plugins2settings_manager.get(
matcher.plugin_name matcher.module
) and plugins2settings_manager[matcher.plugin_name].get( ) and plugins2settings_manager[matcher.module].get(
"plugin_type" "plugin_type"
): ):
plugin_type = tuple( plugin_type = tuple(
plugins2settings_manager.get_plugin_data( plugins2settings_manager.get_plugin_data(
matcher.plugin_name matcher.module
)["plugin_type"] )["plugin_type"]
) )
else: else:
try: try:
plugin_type = _module.__getattribute__("__plugin_type__") plugin_type = _module.__getattribute__(
"__plugin_type__"
)
except AttributeError: except AttributeError:
plugin_type = ("normal",) plugin_type = ("normal",)
if plugin_settings and matcher.plugin_name: if plugin_settings and matcher.module:
plugins2settings_manager.add_plugin_settings( plugins2settings_manager.add_plugin_settings(
matcher.plugin_name, matcher.module,
plugin_type=plugin_type, plugin_type=plugin_type,
**plugin_settings, **plugin_settings,
) )
except Exception as e: except AttributeError:
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}') pass
_tmp.append(matcher.plugin_name) _tmp.append(matcher.module)
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()} _tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
with open(plugins2settings_file, "w", encoding="utf8") as wf: with open(plugins2settings_file, "w", encoding="utf8") as wf:
yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True)
+6 -6
View File
@@ -1,7 +1,6 @@
from nonebot import on_request, on_message from nonebot import on_request, on_message
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
ActionFailed,
FriendRequestEvent, FriendRequestEvent,
GroupRequestEvent, GroupRequestEvent,
MessageEvent, MessageEvent,
@@ -9,6 +8,7 @@ from nonebot.adapters.onebot.v11 import (
from models.friend_user import FriendUser from models.friend_user import FriendUser
from datetime import datetime from datetime import datetime
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from nonebot.adapters.cqhttp.exception import ActionFailed
from utils.manager import requests_manager from utils.manager import requests_manager
from models.group_info import GroupInfo from models.group_info import GroupInfo
from utils.utils import scheduler from utils.utils import scheduler
@@ -25,13 +25,13 @@ __plugin_configs__ = {
friend_req = on_request(priority=5, block=True) friend_req = on_request(priority=5, block=True)
group_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) x = on_message(priority=9, block=False)
exists_data = {"private": {}, "group": {}} exists_data = {"private": {}, "group": {}}
@friend_req.handle() @friend_req.handle()
async def _(bot: Bot, event: FriendRequestEvent): async def _(bot: Bot, event: FriendRequestEvent, state: dict):
global exists_data global exists_data
if exists_data["private"].get(event.user_id): if exists_data["private"].get(event.user_id):
if time.time() - exists_data["private"][event.user_id] < 60 * 5: if time.time() - exists_data["private"][event.user_id] < 60 * 5:
@@ -66,7 +66,7 @@ async def _(bot: Bot, event: FriendRequestEvent):
@group_req.handle() @group_req.handle()
async def _(bot: Bot, event: GroupRequestEvent): async def _(bot: Bot, event: GroupRequestEvent, state: dict):
global exists_data global exists_data
if event.sub_type == "invite": if event.sub_type == "invite":
if str(event.user_id) in bot.config.superusers: if str(event.user_id) in bot.config.superusers:
@@ -125,7 +125,7 @@ async def _(bot: Bot, event: GroupRequestEvent):
@x.handle() @x.handle()
async def _(event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: dict):
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
r = re.search(r'groupcode="(.*?)"', str(event.get_message())) r = re.search(r'groupcode="(.*?)"', str(event.get_message()))
if r: if r:
+15 -21
View File
@@ -1,14 +1,15 @@
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent, Message, MessageEvent
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import get_message_text
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from models.friend_user import FriendUser from models.friend_user import FriendUser
import random
from models.ban_user import BanUser from models.ban_user import BanUser
from services.log import logger from services.log import logger
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from nonebot.params import CommandArg
import random
__zx_plugin_name__ = "昵称系统" __zx_plugin_name__ = "昵称系统"
__plugin_usage__ = f""" __plugin_usage__ = f"""
@@ -22,15 +23,9 @@ __plugin_des__ = "区区昵称,才不想叫呢!"
__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"] __plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["昵称", "昵称系统"],
}
__plugin_configs__ = { __plugin_configs__ = {
"BLACK_WORD": { "BLACK_WORD": {
"value": ["爸", "爹", "爷", "父亲"], "value": ["爸", "爹", "爷"],
"help": "昵称所屏蔽的关键词,会被替换为 *", "help": "昵称所屏蔽的关键词,会被替换为 *",
"default_value": None "default_value": None
} }
@@ -53,8 +48,8 @@ cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=Tru
@nickname.handle() @nickname.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
if not msg: if not msg:
await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True)
if len(msg) > 10: if len(msg) > 10:
@@ -63,9 +58,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True) await nickname.finish("笨蛋!休想占用我的名字!#", at_sender=True)
_tmp = "" _tmp = ""
black_word = Config.get_config("nickname", "BLACK_WORD") black_word = Config.get_config("nickname", "BLACK_WORD")
if black_word: for x in msg:
for x in msg: _tmp += "*" if x in black_word else x
_tmp += "*" if x in black_word else x
msg = _tmp msg = _tmp
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
if await GroupInfoUser.set_group_member_nickname( if await GroupInfoUser.set_group_member_nickname(
@@ -80,7 +74,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..", f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧", f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.", f"好..好.,那窝以后就叫你{msg}了.",
] ]
) )
@@ -97,7 +91,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", f"好啦好啦,我知道啦,{msg},以后就这么叫你吧",
f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}",
f"好突然,突然要叫你昵称什么的...{msg}..", f"好突然,突然要叫你昵称什么的...{msg}..",
f"{NICKNAME}会好好记住{msg}的,放心吧", f"{NICKNAME}会好好记住的{msg}的,放心吧",
f"好..好.,那窝以后就叫你{msg}了.", f"好..好.,那窝以后就叫你{msg}了.",
] ]
) )
@@ -109,7 +103,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
@my_nickname.handle() @my_nickname.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
try: try:
nickname_ = await GroupInfoUser.get_group_member_nickname( nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id event.user_id, event.group_id
@@ -130,7 +124,7 @@ async def _(event: GroupMessageEvent):
) )
) )
else: else:
nickname_ = event.sender.card or event.sender.nickname nickname_ = event.sender.card if event.sender.card else event.sender.nickname
await my_nickname.send( await my_nickname.send(
random.choice( random.choice(
["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"]
@@ -164,7 +158,7 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@cancel_nickname.handle() @cancel_nickname.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
nickname_ = await GroupInfoUser.get_group_member_nickname( nickname_ = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id event.user_id, event.group_id
) )
@@ -187,7 +181,7 @@ async def _(event: GroupMessageEvent):
@cancel_nickname.handle() @cancel_nickname.handle()
async def _(event: PrivateMessageEvent): async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
nickname_ = await FriendUser.get_friend_nickname(event.user_id) nickname_ = await FriendUser.get_friend_nickname(event.user_id)
if nickname_: if nickname_:
await cancel_nickname.send( await cancel_nickname.send(
-68
View File
@@ -1,68 +0,0 @@
from nonebot import on_command, on_regex
from .data_source import show_plugin_repo, install_plugin, uninstall_plugin, download_json
from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent
from nonebot.params import CommandArg
from utils.message_builder import image
from nonebot.permission import SUPERUSER
__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()):
arg = arg.extract_plain_text().strip()
msg = await install_plugin(arg)
await install_plugin_matcher.send(msg)
@uninstall_plugin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
arg = arg.extract_plain_text().strip()
msg = await uninstall_plugin(arg)
await install_plugin_matcher.send(msg)
@update_repo.handle()
async def _(bot: Bot, event: MessageEvent):
code = await download_json()
if code == 200:
await update_repo.finish("更新插件仓库信息成功!")
await update_repo.send("更新插件仓库信息失败!")
@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(b64=msg))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查看插件仓库"
)
-198
View File
@@ -1,198 +0,0 @@
import os
import shutil
import zipfile
from pathlib import Path
from typing import Union, Tuple
from utils.manager import plugins_manager
import ujson as json
from services import logger
from utils.http_utils import AsyncHttpx
from configs.path_config import TEMP_PATH, DATA_PATH
from utils.image_utils import text2image, BuildImage
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("开始解压插件压缩包...")
# 解压
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("解压插件压缩包完成...")
logger.debug("开始移动插件文件夹...")
if (extensive_plugin_path / f"{name}").exists():
logger.debug("extensive_plugin目录下文件夹已存在,删除该目录插件文件夹...")
shutil.rmtree(
(extensive_plugin_path / f"{name}").absolute(), ignore_errors=True
)
extract_path.rename(extensive_plugin_path / f"{name}")
tmp = ""
if "pyproject.toml" in os.listdir(extensive_plugin_path / f"{name}"):
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
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}"):
tmp = "检测到该插件含有额外依赖,当前安装无法保证依赖完全安装成功。"
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("移动插件文件夹完成...")
logger.info(f"成功安装插件 {name} 成功!\n{tmp}")
return f"成功安装插件 {name},请重启真寻!"
except Exception as e:
logger.error(f"安装插件 {name} 失败 {type(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"删除插件 {name} 失败 {type(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"))
load_plugin_list = plugins_manager.get_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_manager.get_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"下载插件库压缩包失败或解压失败 {type(e)}:{e}")
return 999
async def get_plugin_name(name: int) -> Tuple[str, int]:
"""
通过下标获取插件名
:param name: 下标
"""
name = int(name)
if not data:
await show_plugin_repo()
if name < 1 or name > len(data.keys()):
return "下标超过上下限!", 999
name = list(data.keys())[name - 1]
return name, 200
+7 -110
View File
@@ -1,22 +1,14 @@
import random
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
PostgresSyntaxError,
)
from nonebot import Driver from nonebot import Driver
from services.db_context import db from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from models.group_info import GroupInfo from models.group_info import GroupInfo
from models.bag_user import BagUser from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.onebot.v11 import Bot
from services.log import logger from services.log import logger
from configs.path_config import TEXT_PATH from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from utils.manager import GDict
from utils.utils import scheduler from utils.utils import scheduler
from pathlib import Path
import nonebot import nonebot
try: try:
@@ -34,7 +26,7 @@ async def update_city():
部分插件需要中国省份城市 部分插件需要中国省份城市
这里直接更新,避免插件内代码重复 这里直接更新,避免插件内代码重复
""" """
china_city = TEXT_PATH / "china_city.json" china_city = Path(TEXT_PATH) / "china_city.json"
data = {} data = {}
if not china_city.exists(): if not china_city.exists():
try: try:
@@ -69,90 +61,15 @@ async def _():
""" """
数据库表结构变换 数据库表结构变换
""" """
_flag = []
sql_str = [ sql_str = [
( "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
"group_info",
), # group_info表添加一个group_flag
(
"ALTER TABLE bag_users rename belonging_group To group_id;",
"bag_users",
), # 将 bag_users 的 belonging_group 改为 group_id
(
"ALTER TABLE group_info_users rename belonging_group To group_id;",
"group_info_users",
),
(
"ALTER TABLE sign_group_users rename belonging_group To group_id;",
"sign_group_users",
),
(
"ALTER TABLE open_cases_users rename belonging_group To group_id;",
"open_cases_users",
),
(
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
"bag_users",
), # bag_users 新增字段 property 替代 props
(
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD bind_group Integer;",
"genshin"
), # 新增原神群号绑定字段
(
"ALTER TABLE genshin ADD login_ticket VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社login_ticket绑定字段
(
"ALTER TABLE genshin ADD stuid VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stuid绑定字段
(
"ALTER TABLE genshin ADD stoken VARCHAR(255) DEFAULT '';",
"genshin"
), # 新增米游社stoken绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
), # 新增纯文本
(
"ALTER TABLE goods_info ADD daily_limit Integer DEFAULT 0;",
"goods_info"
), # 新增纯文本
(
"ALTER TABLE goods_info ADD daily_purchase_limit Json DEFAULT '{}';",
"goods_info"
), # 新增纯文本
] ]
for sql in sql_str + GDict.get('run_sql', []): for sql in sql_str:
try: try:
if isinstance(sql, str):
flag = f'{random.randint(1, 10000)}'
else:
flag = sql[1]
sql = sql[0]
query = db.text(sql) query = db.text(sql)
await db.first(query) await db.first(query)
logger.info(f"完成sql操作:{sql}") except DuplicateColumnError:
_flag.append(flag)
except (DuplicateColumnError, UndefinedColumnError):
pass pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# bag_user 将文本转为字典格式
await __database_script(_flag)
@driver.on_bot_connect @driver.on_bot_connect
@@ -188,26 +105,6 @@ async def _(bot: Bot):
logger.info(f"移除不存在的群聊信息:{group_id}") logger.info(f"移除不存在的群聊信息:{group_id}")
async def __database_script(_flag: List[str]):
# bag_user 将文本转为字典格式
if "bag_users" in _flag:
for x in await BagUser.get_all_users():
props = {}
if x.props:
for prop in [p for p in x.props.split(",") if p]:
if props.get(prop):
props[prop] += 1
else:
props[prop] = 1
logger.info(
f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
)
await x.update(
property=props,
props="",
).apply()
# 自动更新城市列表 # 自动更新城市列表
@scheduler.scheduled_job( @scheduler.scheduled_job(
"cron", "cron",
+18 -30
View File
@@ -1,13 +1,12 @@
from nonebot import on_command from nonebot import on_command
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.params import CommandArg from nonebot.typing import T_State
from utils.utils import is_number from utils.utils import get_message_text, is_number
from models.bag_user import BagUser from models.bag_user import BagUser
from services.db_context import db from services.db_context import db
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from models.goods_info import GoodsInfo from models.goods_info import GoodsInfo
import time
__zx_plugin_name__ = "商店 - 购买道具" __zx_plugin_name__ = "商店 - 购买道具"
@@ -21,7 +20,7 @@ usage:
""".strip() """.strip()
__plugin_des__ = "商店 - 购买道具" __plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"] __plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ("商店",) __plugin_type__ = ('商店',)
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
@@ -30,43 +29,36 @@ __plugin_settings__ = {
"limit_superuser": False, "limit_superuser": False,
"cmd": ["商店", "购买道具"], "cmd": ["商店", "购买道具"],
} }
__plugin_cd_limit__ = {"cd": 3}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP) buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@buy.handle() @buy.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
goods = None goods = None
if arg.extract_plain_text().strip() in ["神秘药水"]: if get_message_text(event.json()) in ["神秘药水"]:
await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True) await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True)
goods_list = [ goods_lst = await GoodsInfo.get_all_goods()
x goods_name_lst = [x.goods_name for x in goods_lst]
for x in await GoodsInfo.get_all_goods() msg = get_message_text(event.json()).split()
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 num = 1
if len(msg) > 1: if len(msg) > 1:
if is_number(msg[1]) and int(msg[1]) > 0: if is_number(msg[1]) and int(msg[1]) > 0:
num = int(msg[1]) num = int(msg[1])
else: else:
await buy.finish("购买的数量要是数字且大于0!", at_sender=True) await buy.finish("购买的数量要是数字且大于0!", at_sender=True)
# print(msg, num)
if is_number(msg[0]): if is_number(msg[0]):
msg = int(msg[0]) msg = int(msg[0])
if msg > len(goods_name_list) or msg < 1: if msg > len(goods_lst) or msg < 1:
await buy.finish("请输入正确的商品id!", at_sender=True) await buy.finish("请输入正确的商品id!", at_sender=True)
goods = goods_list[msg - 1] goods = goods_lst[msg - 1]
else: else:
if msg[0] in goods_name_list: if msg[0] in goods_name_lst:
for i in range(len(goods_name_list)): for i in range(len(goods_name_lst)):
if msg[0] == goods_name_list[i]: if msg[0] == goods_name_lst[i]:
goods = goods_list[i] goods = goods_lst[i]
break break
else: else:
await buy.finish("请输入正确的商品名称!") await buy.finish("请输入正确的商品名称!")
@@ -77,11 +69,7 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
await BagUser.get_gold(event.user_id, event.group_id) await BagUser.get_gold(event.user_id, event.group_id)
) < goods.goods_price * num * goods.goods_discount: ) < goods.goods_price * num * goods.goods_discount:
await buy.finish("您的金币好像不太够哦", at_sender=True) await buy.finish("您的金币好像不太够哦", at_sender=True)
flag, n = await GoodsInfo.check_user_daily_purchase(goods, event.user_id, event.group_id, num) if await BagUser.buy_props(event.user_id, event.group_id, goods, num):
if flag:
await buy.finish(f"该次购买将超过每日次数限制,目前该道具还可以购买{n}次哦", at_sender=True)
if await BagUser.buy_property(event.user_id, event.group_id, goods, num):
await GoodsInfo.add_user_daily_purchase(goods, event.user_id, event.group_id, num)
await buy.send( await buy.send(
f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!", f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {goods.goods_name} ×{num} 成功!",
at_sender=True, at_sender=True,
+8 -13
View File
@@ -1,12 +1,11 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, ActionFailed from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.params import CommandArg from nonebot.typing import T_State
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from utils.data_utils import init_rank from utils.data_utils import init_rank
from models.bag_user import BagUser from models.bag_user import BagUser
from utils.image_utils import text2image
from utils.message_builder import image from utils.message_builder import image
from utils.utils import is_number from utils.utils import get_message_text, is_number
__zx_plugin_name__ = "商店 - 我的金币" __zx_plugin_name__ = "商店 - 我的金币"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -34,17 +33,13 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle() @my_gold.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = await BagUser.get_user_total_gold(event.user_id, event.group_id) await my_gold.finish(await BagUser.get_my_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() @gold_rank.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
num = arg.extract_plain_text().strip() num = get_message_text(event.json())
if is_number(num) and 51 > int(num) > 10: if is_number(num) and 51 > int(num) > 10:
num = int(num) num = int(num)
else: else:
+17 -6
View File
@@ -1,8 +1,9 @@
from nonebot import on_command from nonebot import on_command
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.typing import T_State
from models.bag_user import BagUser from models.bag_user import BagUser
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
__zx_plugin_name__ = "商店 - 我的道具" __zx_plugin_name__ = "商店 - 我的道具"
@@ -29,12 +30,22 @@ my_props = on_command("我的道具", priority=5, block=True, permission=GROUP)
@my_props.handle() @my_props.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
props = await BagUser.get_property(event.user_id, event.group_id) props = await BagUser.get_props(event.user_id, event.group_id)
if props: if props:
pname_list = []
pnum_list = []
rst = "" rst = ""
for i, p in enumerate(props.keys()): props = props[:-1].split(",")
rst += f"{i+1}.{p}\t×{props[p]}\n" for p in props:
if p != "":
if p in pname_list:
pnum_list[pname_list.index(p)] += 1
else:
pname_list.append(p)
pnum_list.append(1)
for i in range(len(pname_list)):
rst += f"{i+1}.{pname_list[i]}\t×{pnum_list[i]}\n"
await my_props.send("\n" + rst[:-1], at_sender=True) await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具") logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else: else:
+14 -26
View File
@@ -1,11 +1,11 @@
from .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info, GoodsInfo
from nonebot.adapters.onebot.v11 import MessageEvent, Message
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from nonebot.typing import T_State
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from .data_source import create_shop_help, delete_goods, update_goods, registered_goods, parse_goods_info
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from utils.utils import is_number, scheduler from utils.utils import get_message_text, is_number
from nonebot.params import CommandArg
from nonebot.plugin import export from nonebot.plugin import export
from services.log import logger from services.log import logger
import os import os
@@ -53,7 +53,7 @@ __plugin_block_limit__ = {
# 导出方法供其他插件使用 # 导出方法供其他插件使用
export = export() export = export()
export.register_goods = register_goods export.registered_goods = registered_goods
export.delete_goods = delete_goods export.delete_goods = delete_goods
export.update_goods = update_goods export.update_goods = update_goods
@@ -67,20 +67,20 @@ shop_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER,
@shop_help.handle() @shop_help.handle()
async def _(): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await shop_help.send(image(b64=await create_shop_help())) await shop_help.send(image(b64=await create_shop_help()))
@shop_add_goods.handle() @shop_add_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
if msg: if msg:
data = parse_goods_info(msg) data = parse_goods_info(msg)
if isinstance(data, str): if isinstance(data, str):
await shop_add_goods.finish(data) await shop_add_goods.finish(data)
if not data.get("name") or not data.get("price") or not data.get("des"): if not data.get("name") or not data.get("price") or not data.get("des"):
await shop_add_goods.finish("name:price:des 参数不可缺少!") await shop_add_goods.finish("name:price:des 参数不可缺少!")
if await register_goods(**data): if await registered_goods(**data):
await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n" await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n" f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n" f"价格:{data['price']}金币\n"
@@ -94,8 +94,8 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
@shop_del_goods.handle() @shop_del_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
if msg: if msg:
name = "" name = ""
id_ = 0 id_ = 0
@@ -115,8 +115,8 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
@shop_update_goods.handle() @shop_update_goods.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
if msg: if msg:
data = parse_goods_info(msg) data = parse_goods_info(msg)
if isinstance(data, str): if isinstance(data, str):
@@ -128,18 +128,6 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True) await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True)
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功") logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功")
else: else:
await shop_update_goods.send(name, at_sender=True) await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True)
logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败") logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败")
@scheduler.scheduled_job(
"cron",
hour=0,
minute=0,
)
async def _():
try:
await GoodsInfo.reset_daily_purchase()
logger.info("商品每日限购次数重置成功...")
except Exception as e:
logger.error(f"商品每日限购次数重置发生错误 {type(e)}:{e}")
+86 -150
View File
@@ -1,21 +1,17 @@
from PIL import Image
from models.goods_info import GoodsInfo from models.goods_info import GoodsInfo
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from models.sign_group_user import SignGroupUser from models.sign_group_user import SignGroupUser
from utils.utils import is_number from utils.utils import is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from typing import Optional, Union, Tuple from typing import Optional, Union
from configs.config import Config from configs.config import Config
from nonebot import Driver from nonebot import Driver
from nonebot.plugin import require from nonebot.plugin import require
from utils.decorator.shop import shop_register
import nonebot import nonebot
import time import time
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
use = require("use") use = require("use")
@@ -25,26 +21,35 @@ async def init_default_shop_goods():
导入内置的三个商品 导入内置的三个商品
""" """
@shop_register( async def sign_card_1(**kwargs):
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"), user_id = kwargs['user_id']
price=(30, 150, 250), group_id = kwargs['group_id']
des=(
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
),
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
daily_limit=(10, 20, 30),
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
)
async def sign_card(user_id: int, group_id: int, prob: float):
user = await SignGroupUser.ensure(user_id, group_id) user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply() await user.update(add_probability=0.1).apply()
async def sign_card_2(**kwargs):
user_id = kwargs['user_id']
group_id = kwargs['group_id']
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=0.2).apply()
@driver.on_bot_connect async def sign_card_3(**kwargs):
async def _(): user_id = kwargs['user_id']
await shop_register.load_register() group_id = kwargs['group_id']
user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=0.3).apply()
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
await registered_goods(
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
)
use.registered_use("好感度双倍加持卡Ⅰ", sign_card_1)
await registered_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
use.registered_use("好感度双倍加持卡Ⅱ", sign_card_2)
await registered_goods(
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
)
use.registered_use("好感度双倍加持卡Ⅲ", sign_card_3)
# 创建商店界面 # 创建商店界面
@@ -65,9 +70,10 @@ async def create_shop_help() -> str:
_list.append(goods) _list.append(goods)
A = BuildImage(1000, h, color="#f9f6f2") A = BuildImage(1000, h, color="#f9f6f2")
current_h = 0 current_h = 0
total_n = 0
for goods in _list: for goods in _list:
bk = BuildImage(1180, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf") bk = BuildImage(
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
)
goods_image = BuildImage( goods_image = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf" 600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
) )
@@ -79,27 +85,16 @@ async def create_shop_help() -> str:
) )
await name_image.aline((380, -5, 280, 45), "#a29ad6", 5) await name_image.aline((380, -5, 280, 45), "#a29ad6", 5)
await name_image.atext((390, 0), "售价:", center_type="by_height") await name_image.atext((390, 0), "售价:", center_type="by_height")
if goods.goods_discount != 1: await name_image.atext(
discount_price = int(goods.goods_discount * goods.goods_price) (440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height"
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( await name_image.atext(
( (
440 440
+ BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w, + BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w,
0, 0,
), ),
f" 金币", " 金币",
center_type="by_height", center_type="by_height",
) )
await name_image.acircle_corner(5) await name_image.acircle_corner(5)
@@ -107,97 +102,48 @@ async def create_shop_help() -> str:
await goods_image.atext((15, 50), f"简介:{goods.goods_description}") await goods_image.atext((15, 50), f"简介:{goods.goods_description}")
await goods_image.acircle_corner(20) await goods_image.acircle_corner(20)
await bk.apaste(goods_image, alpha=True) await bk.apaste(goods_image, alpha=True)
n = 0
_w = 550
# 添加限时图标和时间 # 添加限时图标和时间
if goods.goods_limit_time > 0: if goods.goods_limit_time > 0:
n += 140 _limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
_limit_time_logo = BuildImage( await bk.apaste(_limit_time_logo, (600, 0), True)
40, 40, background=f"{IMAGE_PATH}/other/time.png" await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True)
) limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split()
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] y_m_d = limit_time[0]
_h_m = limit_time[1].split(":") _h_m = limit_time[1].split(":")
h_m = _h_m[0] + "时 " + _h_m[1] + "分" h_m = _h_m[0] + "时 " + _h_m[1] + "分"
await bk.atext((_w + 55, 38), str(y_m_d)) await bk.atext((605, 38), str(y_m_d))
await bk.atext((_w + 65, 57), str(h_m)) await bk.atext((615, 57), str(h_m))
_w += 140 await bk.aline((550, -1, 710, -1), "#a29ad6", 5)
if goods.goods_discount != 1: await bk.aline((550, 80, 710, 80), "#a29ad6", 5)
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((550, -1, 550 + n, -1), "#a29ad6", 5)
await bk.aline((550, 80, 550 + n, 80), "#a29ad6", 5)
# 添加限时图标和时间
idx += 1 idx += 1
await A.apaste(bk, (0, current_h), True) await A.apaste(bk, (0, current_h), True)
current_h += 90 current_h += 90
w = 850 w = 1000
if total_n:
w += total_n
h = A.h + 230 + 100 h = A.h + 230 + 100
h = 1000 if h < 1000 else h h = 1000 if h < 1000 else h
shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png") shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png")
shop = BuildImage(w, h, font_size=20, color="#f9f6f2") shop = BuildImage(w, h, font_size=20, color="#f9f6f2")
zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan_3.png") shop.paste(A, (20, 230))
zx_img.transpose(Image.FLIP_LEFT_RIGHT) zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan.png")
zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242)) zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242))
await shop.apaste(zx_img, (0, 100)) await shop.apaste(zx_img, (780, 100))
shop.paste(A, (20 + zx_img.w, 230))
await shop.apaste(shop_logo, (450, 30), True) await shop.apaste(shop_logo, (450, 30), True)
shop.text( shop.text(
(int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170), (int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170),
"注【通过 序号 或者 商品名称 购买】", "注【通过 序号 或者 商品名称 购买】",
) )
shop.text((20 + zx_img.w, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~") shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~")
return shop.pic2bs4() return shop.pic2bs4()
async def register_goods( async def registered_goods(
name: str, name: str,
price: int, price: int,
des: str, des: str,
discount: Optional[float] = 1, discount: Optional[float] = 1,
limit_time: Optional[int] = 0, limit_time: Optional[int] = 0,
daily_limit: Optional[int] = 0, **kwargs,
) -> bool: ):
""" """
添加商品 添加商品
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时 例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
@@ -208,21 +154,21 @@ async def register_goods(
:param des: 商品简介 :param des: 商品简介
:param discount: 商品折扣 :param discount: 商品折扣
:param limit_time: 商品限时销售时间,单位为小时 :param limit_time: 商品限时销售时间,单位为小时
:param daily_limit: 每日购买次数限制 :param kwargs: kwargs
:return: 是否添加成功 :return: 是否添加成功
""" """
if not await GoodsInfo.get_goods_info(name): if kwargs:
limit_time = float(limit_time) if limit_time else limit_time name = kwargs.get("name")
discount = discount if discount is not None else 1 price = kwargs.get("price")
limit_time = ( des = kwargs.get("des")
int(time.time() + limit_time * 60 * 60) discount = kwargs.get("discount")
if limit_time is not None and limit_time != 0 limit_time = kwargs.get("time_limit")
else 0 limit_time = float(limit_time) if limit_time else limit_time
) discount = discount if discount is None else 1
return await GoodsInfo.add_goods( limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0
name, int(price), des, float(discount), limit_time, daily_limit return await GoodsInfo.add_goods(
) name, int(price), des, float(discount), limit_time
return False )
# 删除商品 # 删除商品
@@ -250,7 +196,7 @@ async def delete_goods(name: str, id_: int) -> "str, str, int":
# 更新商品信息 # 更新商品信息
async def update_goods(**kwargs) -> Tuple[bool, str, str]: async def update_goods(**kwargs) -> "str, str, int":
""" """
更新商品信息 更新商品信息
:param kwargs: kwargs :param kwargs: kwargs
@@ -260,18 +206,17 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
goods_lst = await GoodsInfo.get_all_goods() goods_lst = await GoodsInfo.get_all_goods()
if is_number(kwargs["name"]): if is_number(kwargs["name"]):
if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst): if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst):
return False, "序号错误,没有该序号的商品...", "" return "序号错误,没有该序号的商品...", "", 999
goods = goods_lst[int(kwargs["name"]) - 1] goods = goods_lst[int(kwargs["name"]) - 1]
else: else:
goods = await GoodsInfo.get_goods_info(kwargs["name"]) goods = await GoodsInfo.get_goods_info(kwargs["name"])
if not goods: if not goods:
return False, "名称错误,没有该名称的商品...", "" return "名称错误,没有该名称的商品...", "", 999
name: str = goods.goods_name name = goods.goods_name
price = goods.goods_price price = goods.goods_price
des = goods.goods_description des = goods.goods_description
discount = goods.goods_discount discount = goods.goods_discount
limit_time = goods.goods_limit_time limit_time = goods.goods_limit_time
daily_limit = goods.daily_limit
new_time = 0 new_time = 0
tmp = "" tmp = ""
if kwargs.get("price"): if kwargs.get("price"):
@@ -284,29 +229,23 @@ async def update_goods(**kwargs) -> Tuple[bool, str, str]:
tmp += f'折扣:{discount} --> {kwargs["discount"]}\n' tmp += f'折扣:{discount} --> {kwargs["discount"]}\n'
discount = kwargs["discount"] discount = kwargs["discount"]
if kwargs.get("limit_time"): if kwargs.get("limit_time"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = time.strftime( new_time = time.strftime(
"%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60), time.localtime(time.time() + int(kwargs["limit_time"] * 60 * 60)),
) if kwargs["limit_time"] != 0 else 0 )
tmp += f"限时至: {new_time}\n" if new_time else "取消了限时\n" tmp += f"折扣至: {new_time}\n"
limit_time = kwargs["limit_time"] limit_time = kwargs["limit_time"]
if kwargs.get("daily_limit"): return (
tmp += f'每日购买限制:{daily_limit} --> {kwargs["daily_limit"]}\n' if daily_limit else "取消了购买限制\n" await GoodsInfo.update_goods(
daily_limit = int(kwargs["daily_limit"]) name,
await GoodsInfo.update_goods( int(price),
name, des,
int(price), float(discount),
des, int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
float(discount),
int(
time.time() + limit_time * 60 * 60
if limit_time != 0 and new_time
else 0
), ),
daily_limit name,
tmp[:-1],
) )
return True, name, tmp[:-1],
def parse_goods_info(msg: str) -> Union[dict, str]: def parse_goods_info(msg: str) -> Union[dict, str]:
@@ -322,6 +261,7 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
sp = x.split(":", maxsplit=1) sp = x.split(":", maxsplit=1)
if str(sp[1]).strip(): if str(sp[1]).strip():
sp[1] = sp[1].strip() sp[1] = sp[1].strip()
print(sp)
if sp[0] == "name": if sp[0] == "name":
data["name"] = sp[1] data["name"] = sp[1]
elif sp[0] == "price": elif sp[0] == "price":
@@ -336,10 +276,6 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
data["discount"] = sp[1] data["discount"] = sp[1]
elif sp[0] == "limit_time": elif sp[0] == "limit_time":
if not is_number(sp[1]) or float(sp[1]) < 0: if not is_number(sp[1]) or float(sp[1]) < 0:
return "limit_time参数不合法,必须为数字且大于0!" return "limit_time参数不合法,必须大于0!"
data["limit_time"] = sp[1] 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 return data
+30 -35
View File
@@ -1,13 +1,13 @@
from nonebot import on_command from nonebot import on_command
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.params import CommandArg from nonebot.typing import T_State
from utils.utils import is_number from utils.utils import is_number, get_message_text
from models.bag_user import BagUser from models.bag_user import BagUser
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp.permission import GROUP
from services.db_context import db from services.db_context import db
from nonebot.plugin import export from nonebot.plugin import export
from .data_source import effect, register_use, func_manager from .data_source import effect, registered_use
__zx_plugin_name__ = "商店 - 使用道具" __zx_plugin_name__ = "商店 - 使用道具"
@@ -15,7 +15,7 @@ __plugin_usage__ = """
usage: usage:
普通的使用道具 普通的使用道具
指令: 指令:
使用道具 [序号或道具名称] ?[数量]=1 使用道具 [序号或道具名称]
* 序号以 ”我的道具“ 为准 * * 序号以 ”我的道具“ 为准 *
""".strip() """.strip()
__plugin_des__ = "商店 - 使用道具" __plugin_des__ = "商店 - 使用道具"
@@ -32,50 +32,45 @@ __plugin_settings__ = {
# 导出方法供其他插件使用 # 导出方法供其他插件使用
export = export() export = export()
export.register_use = register_use export.registered_use = registered_use
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP) use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@use_props.handle() @use_props.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
num = 1 if msg in ["", "帮助"]:
msg_sp = msg.split() await use_props.finish(__plugin_usage__)
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0: props = await BagUser.get_props(event.user_id, event.group_id)
num = int(msg.split()[-1]) if props:
msg = " ".join(msg.split()[:-1])
property_ = await BagUser.get_property(event.user_id, event.group_id)
if property_:
async with db.transaction(): async with db.transaction():
pname_list = []
props = props[:-1].split(",")
for p in props:
if p != "":
if p not in pname_list:
pname_list.append(p)
if is_number(msg): if is_number(msg):
if 0 < int(msg) <= len(property_): if 0 < int(msg) <= len(pname_list):
name = list(property_.keys())[int(msg) - 1] name = pname_list[int(msg) - 1]
else: else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True) await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else: else:
if msg not in property_.keys(): if msg not in pname_list:
await use_props.finish("道具名称错误!", at_sender=True) await use_props.finish("道具名称错误!", at_sender=True)
name = msg name = msg
_user_prop_count = property_[name] if await BagUser.del_props(
if num > _user_prop_count: event.user_id, event.group_id, name
await use_props.finish(f"道具数量不足,无法使用{num}次!") ) and await effect(event.user_id, event.group_id, name):
if num > (n := func_manager.get_max_num_limit(name)): await use_props.send(f"使用道具 {name} 成功!", at_sender=True)
await use_props.finish(f"该道具单次只能使用 {n} 个!")
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):
await use_props.send(msg, at_sender=True)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功" f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功"
) )
else: else:
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True) await use_props.send(f"使用道具 {name} 失败!", at_sender=True)
logger.info( logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败" f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败"
) )
else: else:
await use_props.send("您的背包里没有任何的道具噢", at_sender=True) await use_props.send("您的背包里没有任何的道具噢~", at_sender=True)
+27 -152
View File
@@ -1,170 +1,45 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
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
from types import MappingProxyType
import inspect
import asyncio import asyncio
from services.log import logger
_use_func_data = {}
class GoodsUseFuncManager: async def effect(user_id: int, group_id: int, goods_name: str) -> bool:
def __init__(self):
self._data = {}
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
async def use(
self, param: ShopParam, **kwargs
) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param param: BaseModel
:param kwargs: kwargs
"""
def parse_args(args_: MappingProxyType):
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_
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"](
*parse_args(args)
)
else:
return self._data[goods_name]["func"](
*parse_args(args)
)
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,
)
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):
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,
}
)
func_manager = GoodsUseFuncManager()
async def effect(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
) -> Optional[Union[str, MessageSegment]]:
""" """
商品生效 商品生效
:param bot: Bot :param user_id: 用户id
:param event: GroupMessageEvent :param group_id: 群号
:param goods_name: 商品名称 :param goods_name: 商品名称
:param num: 使用数量
:return: 使用是否成功 :return: 使用是否成功
""" """
# 优先使用注册的商品插件 # 优先使用注册的商品插件
# try: try:
if func_manager.exists(goods_name): if _use_func_data.get(goods_name):
_kwargs = func_manager.get_kwargs(goods_name) _kwargs = _use_func_data[goods_name]["kwargs"]
return await func_manager.use( _kwargs["goods_name"] = goods_name
func_manager.init_model(goods_name, bot, event, num), _kwargs["user_id"] = user_id
**{ _kwargs["group_id"] = group_id
**_kwargs, if asyncio.iscoroutinefunction(_use_func_data[goods_name]["func"]):
"_bot": bot, await _use_func_data[goods_name]["func"](
"event": event, **_kwargs,
"group_id": event.group_id, )
"user_id": event.user_id, else:
"num": num, _use_func_data[goods_name]["func"](
"goods_name": goods_name, **_kwargs,
}, )
) return True
# except Exception as e: except Exception as e:
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}") logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return None return False
def register_use(goods_name: str, func, **kwargs): def registered_use(goods_name: str, func, **kwargs):
""" """
注册商品使用方法 注册商品使用方法
:param goods_name: 商品名称 :param goods_name: 商品名称
:param func: 使用函数 :param func: 使用函数
:param kwargs: kwargs :param kwargs: kwargs
""" """
if func_manager.exists(goods_name): if goods_name in _use_func_data.keys():
raise ValueError("该商品使用函数已被注册!") raise ValueError("该商品使用函数已被注册!")
# 发送使用成功信息 _use_func_data[goods_name] = {"func": func, "kwargs": kwargs}
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} 的使用函数")
+42 -43
View File
@@ -1,10 +1,9 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.adapters.onebot.v11 import Bot, Message from nonebot.typing import T_State
from nonebot.params import Command, CommandArg from nonebot.adapters.cqhttp import Bot, MessageEvent, Message
from typing import Tuple
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import is_number from utils.utils import get_message_text, is_number
from utils.manager import requests_manager from utils.manager import requests_manager
from utils.message_builder import image from utils.message_builder import image
from models.group_info import GroupInfo from models.group_info import GroupInfo
@@ -58,54 +57,59 @@ cls_request = on_command("查看所有请求", permission=SUPERUSER, priority=1,
@cls_group.handle() @cls_group.handle()
async def _(bot: Bot): async def _(bot: Bot, event: MessageEvent, state: T_State):
gl = await bot.get_group_list() gl = await bot.get_group_list(self_id=int(bot.self_id))
msg = ["{group_id} {group_name}".format_map(g) for g in gl] msg = ["{group_id} {group_name}".format_map(g) for g in gl]
msg = "\n".join(msg) msg = "\n".join(msg)
msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg
await cls_group.send(msg) await bot.send_private_msg(
self_id=int(bot.self_id),
user_id=int(list(bot.config.superusers)[0]),
message=msg,
)
@cls_friend.handle() @cls_friend.handle()
async def _(bot: Bot): async def _(bot: Bot, event: MessageEvent, state: T_State):
gl = await bot.get_friend_list() gl = await bot.get_friend_list(self_id=int(bot.self_id))
msg = ["{user_id} {nickname}".format_map(g) for g in gl] msg = ["{user_id} {nickname}".format_map(g) for g in gl]
msg = "\n".join(msg) msg = "\n".join(msg)
msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg
await cls_friend.send(msg) await bot.send_private_msg(
self_id=int(bot.self_id),
user_id=int(list(bot.config.superusers)[0]),
message=msg,
)
@friend_handle.handle() @friend_handle.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
cmd = cmd[0] id_ = get_message_text(event.json())
id_ = arg.extract_plain_text().strip()
if is_number(id_): if is_number(id_):
id_ = int(id_) id_ = int(id_)
if cmd[:2] == "同意": if state["_prefix"]["raw_command"][:2] == "同意":
flag = await requests_manager.approve(bot, id_, "private") if await requests_manager.approve(bot, id_, "private"):
await friend_handle.send("同意好友请求成功..")
else:
await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..")
else: else:
flag = await requests_manager.refused(bot, id_, "private") if await requests_manager.refused(bot, id_, "private"):
if flag == 1: await friend_handle.send("拒绝好友请求成功..")
await friend_handle.send(f"{cmd[:2]}好友请求失败,该请求已失效..") else:
requests_manager.delete_request(id_, "private") await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..")
elif flag == 2:
await friend_handle.send(f"{cmd[:2]}好友请求失败,未找到此id的请求..")
else:
await friend_handle.send(f"{cmd[:2]}好友请求成功!")
else: else:
await friend_handle.send("id必须为纯数字!") await friend_handle.send("id必须为纯数字!")
@group_handle.handle() @group_handle.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
cmd = cmd[0] id_ = get_message_text(event.json())
id_ = arg.extract_plain_text().strip()
flag = None
if is_number(id_): if is_number(id_):
id_ = int(id_) id_ = int(id_)
if cmd[:2] == "同意": if state["_prefix"]["raw_command"][:2] == "同意":
rid = requests_manager.get_group_id(id_) rid = await requests_manager.approve(bot, id_, "group")
if rid: if rid:
await friend_handle.send("同意群聊请求成功..")
if await GroupInfo.get_group_info(rid): if await GroupInfo.get_group_info(rid):
await GroupInfo.set_group_flag(rid, 1) await GroupInfo.set_group_flag(rid, 1)
else: else:
@@ -117,24 +121,19 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
group_info["member_count"], group_info["member_count"],
1 1
) )
flag = await requests_manager.approve(bot, id_, "group")
else: else:
await group_handle.send("同意群聊请求失败,未找到此id的请求..") await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..")
else: else:
flag = await requests_manager.refused(bot, id_, "group") if await requests_manager.refused(bot, id_, "group"):
if flag == 1: await friend_handle.send("拒绝群聊请求成功..")
await group_handle.send(f"{cmd[:2]}群聊请求失败,该请求已失效..") else:
requests_manager.delete_request(id_, "group") await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..")
elif flag == 2:
await group_handle.send(f"{cmd[:2]}群聊请求失败,未找到此id的请求..")
else:
await group_handle.send(f"{cmd[:2]}群聊请求成功!")
else: else:
await group_handle.send("id必须为纯数字!") await friend_handle.send("id必须为纯数字!")
@cls_request.handle() @cls_request.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
_str = "" _str = ""
for type_ in ["private", "group"]: for type_ in ["private", "group"]:
msg = await requests_manager.show(type_) msg = await requests_manager.show(type_)
@@ -148,6 +147,6 @@ async def _():
@clear_request.handle() @clear_request.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
requests_manager.clear() requests_manager.clear()
await clear_request.send("已清空所有好友/群聊请求..") await cls_request.send("已清空所有好友/群聊请求..")
+10 -9
View File
@@ -1,5 +1,7 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from configs.path_config import TEMP_PATH from configs.path_config import TEMP_PATH
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import scheduler from utils.utils import scheduler
@@ -33,7 +35,7 @@ resources_manager.add_temp_dir(TEMP_PATH)
@clear_data.handle() @clear_data.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
await clear_data.send("开始清理临时数据....") await clear_data.send("开始清理临时数据....")
size = await asyncio.get_event_loop().run_in_executor(None, _clear_data) size = await asyncio.get_event_loop().run_in_executor(None, _clear_data)
await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024))
@@ -45,14 +47,13 @@ def _clear_data() -> float:
if dir_.exists(): if dir_.exists():
for file in os.listdir(dir_): for file in os.listdir(dir_):
file = dir_ / file file = dir_ / file
if file.is_file(): try:
try: if time.time() - os.path.getatime(file) > 300:
if time.time() - os.path.getatime(file) > 300: file_size = os.path.getsize(file)
file_size = os.path.getsize(file) file.unlink()
file.unlink() size += file_size
size += file_size except Exception as e:
except Exception as e: logger.error(f"清理临时数据错误...{type(e)}:{e}")
logger.error(f"清理临时数据错误...{type(e)}:{e}")
return float(size) return float(size)
+74
View File
@@ -0,0 +1,74 @@
# async def open_remind(group: int, name: str) -> str:
# _name = ""
# if name == "zwa":
# _name = "早晚安"
# if name == "dz":
# _name = "地震播报"
# if name == "hy":
# _name = "群欢迎"
# if name == "kxcz":
# _name = "开箱重置提醒"
# if name == "gb":
# _name = "广播"
# if await GroupRemind.get_status(group, name):
# return f"该群已经开启过 {_name} 通知,请勿重复开启!"
# if await GroupRemind.set_status(group, name, True):
# return f"成功开启 {_name} 通知!0v0"
# else:
# return f"开启 {_name} 通知失败了..."
#
#
# async def close_remind(group: int, name: str) -> str:
# _name = ""
# if name == "zwa":
# _name = "早晚安"
# if name == "dz":
# _name = "地震播报"
# if name == "hy":
# _name = "群欢迎"
# if name == "kxcz":
# _name = "开箱重置提醒"
# if name == "gb":
# _name = "广播"
# if not await GroupRemind.get_status(group, name):
# return f"该群已经取消过 {_name} 通知,请勿重复取消!"
# if await GroupRemind.set_status(group, name, False):
# return f"成功关闭 {_name} 通知!0v0"
# else:
# return f"关闭 {_name} 通知失败了..."
# cmd_list = ['总开关', '签到', '发送图片', '色图', '黑白草图', 'coser', '鸡汤/语录', '骂我', '开箱', '鲁迅说', '假消息', '商店系统',
# '操作图片', '查询皮肤', '天气', '疫情', '识番', '搜番', '点歌', 'pixiv', 'rss', '方舟一井', '查干员', '骰子娘', '原神一井']
#
#
# def check_group_switch_json(group_id):
# if not os.path.exists(DATA_PATH + f'rule/group_switch/'):
# os.mkdir(DATA_PATH + f'rule/group_switch/')
# if not os.path.exists(DATA_PATH + f'rule/group_switch/{group_id}.json'):
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as f:
# data = {}
# for cmd in cmd_list:
# data[cmd] = True
# f.write(json.dumps(data, ensure_ascii=False))
# else:
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'r', encoding='utf8') as f:
# try:
# data = json.load(f)
# except ValueError:
# data = {}
# if len(data.keys()) - 1 != len(cmd_list):
# for cmd in cmd_list:
# if cmd not in data.keys():
# data[cmd] = True
# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as wf:
# wf.write(json.dumps(data, ensure_ascii=False))
# reload(data)
# for file in os.listdir(DATA_PATH + 'group_help'):
# os.remove(DATA_PATH + f'group_help/{file}')
def reload(data):
static_group_dict = data
+5 -4
View File
@@ -1,9 +1,10 @@
from nonebot.adapters.onebot.v11 import Message
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from services.db_context import db from services.db_context import db
from nonebot.params import CommandArg from utils.utils import get_message_text
from services.log import logger from services.log import logger
__zx_plugin_name__ = "执行sql [Superuser]" __zx_plugin_name__ = "执行sql [Superuser]"
@@ -25,8 +26,8 @@ exec_ = on_command("exec", rule=to_me(), permission=SUPERUSER, priority=1, block
@exec_.handle() @exec_.handle()
async def _(arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
sql = arg.extract_plain_text().strip() sql = get_message_text(event.json())
async with db.transaction(): async with db.transaction():
try: try:
query = db.text(sql) query = db.text(sql)
+26 -30
View File
@@ -1,22 +1,14 @@
from nonebot.adapters.onebot.v11 import (
Bot,
MessageEvent,
GROUP,
GroupMessageEvent,
Message,
ActionFailed,
)
from nonebot import on_command, on_regex from nonebot import on_command, on_regex
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GROUP, GroupMessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import is_number from utils.utils import get_message_text, is_number
from utils.manager import group_manager, plugins2settings_manager from utils.manager import group_manager, plugins2settings_manager
from models.group_info import GroupInfo from models.group_info import GroupInfo
from services.log import logger from services.log import logger
from configs.config import NICKNAME from configs.config import NICKNAME
from nonebot.params import Command, CommandArg from nonebot.adapters.cqhttp.exception import ActionFailed
from typing import Tuple
__zx_plugin_name__ = "管理群操作 [Superuser]" __zx_plugin_name__ = "管理群操作 [Superuser]"
@@ -73,8 +65,8 @@ group_auth = on_command(
@del_group.handle() @del_group.handle()
async def _(bot: Bot, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
group_id = arg.extract_plain_text().strip() group_id = get_message_text(event.json())
if group_id: if group_id:
if is_number(group_id): if is_number(group_id):
try: try:
@@ -92,13 +84,13 @@ async def _(bot: Bot, arg: Message = CommandArg()):
@add_group_level.handle() @add_group_level.handle()
async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
group_id = 0 group_id = 0
level = 0 level = 0
if not msg: if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]") await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split() msg = msg.split(" ")
if len(msg) < 2: if len(msg) < 2:
await add_group_level.finish("参数不完全..[group] [level]") await add_group_level.finish("参数不完全..[group] [level]")
if is_number(msg[0]) and is_number(msg[1]): if is_number(msg[0]) and is_number(msg[1]):
@@ -117,7 +109,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
@my_group_level.handle() @my_group_level.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
level = group_manager.get_group_level(event.group_id) level = group_manager.get_group_level(event.group_id)
tmp = "" tmp = ""
data = plugins2settings_manager.get_data() data = plugins2settings_manager.get_data()
@@ -133,35 +125,38 @@ async def _(event: GroupMessageEvent):
@what_up_group_level.handle() @what_up_group_level.handle()
async def _(): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await what_up_group_level.finish( await what_up_group_level.finish(
f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限"
) )
@manager_group_whitelist.handle() @manager_group_whitelist.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
cmd = cmd[0] msg = get_message_text(event.json()).split()
msg = arg.extract_plain_text().strip().split() all_group = [
all_group = [g["group_id"] for g in await bot.get_group_list()] g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id))
]
group_list = [] group_list = []
for group in msg: for group in msg:
if is_number(group) and int(group) in all_group: if is_number(group) and int(group) in all_group:
group_list.append(int(group)) group_list.append(int(group))
if group_list: if group_list:
for group in group_list: for group in group_list:
if cmd in ["添加群白名单"]: if state["_prefix"]["raw_command"] in ["添加群白名单"]:
group_manager.add_group_white_list(group) group_manager.add_group_white_list(group)
else: else:
group_manager.delete_group_white_list(group) group_manager.delete_group_white_list(group)
group_list = [str(x) for x in group_list] group_list = [str(x) for x in group_list]
await manager_group_whitelist.send("已成功将 " + "\n".join(group_list) + " " + cmd) await manager_group_whitelist.send(
"已成功将 " + "\n".join(group_list) + " " + state["_prefix"]["raw_command"]
)
else: else:
await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名") await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名")
@show_group_whitelist.handle() @show_group_whitelist.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
x = group_manager.get_group_white_list() x = group_manager.get_group_white_list()
x = [str(g) for g in x] x = [str(g) for g in x]
if x: if x:
@@ -171,14 +166,13 @@ async def _():
@group_auth.handle() @group_auth.handle()
async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
cmd = cmd[0] msg = get_message_text(event.json()).split()
msg = arg.extract_plain_text().strip().split()
for group_id in msg: for group_id in msg:
if not is_number(group_id): if not is_number(group_id):
await group_auth.send(f"{group_id}非纯数字,已跳过该项..") await group_auth.send(f"{group_id}非纯数字,已跳过该项..")
group_id = int(group_id) group_id = int(group_id)
if cmd[:2] == "添加": if state["_prefix"]["raw_command"][:2] == "添加":
if await GroupInfo.get_group_info(group_id): if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 1) await GroupInfo.set_group_flag(group_id, 1)
else: else:
@@ -201,4 +195,6 @@ async def _(bot: Bot, cmd: Tuple[str, ...] = Command(), arg: Message = CommandAr
else: else:
if await GroupInfo.get_group_info(group_id): if await GroupInfo.get_group_info(group_id):
await GroupInfo.set_group_flag(group_id, 0) await GroupInfo.set_group_flag(group_id, 0)
await group_auth.send(f"已为 {group_id} {cmd[:2]}群认证..") await group_auth.send(
f'已为 {group_id} {state["_prefix"]["raw_command"][:2]}群认证..'
)
+3 -31
View File
@@ -7,9 +7,8 @@ from utils.manager import (
plugins2block_manager, plugins2block_manager,
group_manager, group_manager,
) )
from configs.config import Config from nonebot.typing import T_State
from services.log import logger from nonebot.adapters.cqhttp import Bot, MessageEvent
from utils.utils import scheduler
__zx_plugin_name__ = "重载插件配置 [Superuser]" __zx_plugin_name__ = "重载插件配置 [Superuser]"
@@ -29,18 +28,6 @@ __plugin_cmd__ = [
] ]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {
"value": False,
"help": "自动重载配置文件",
"default_value": False
},
"AUTO_RELOAD_TIME": {
"value": 180,
"help": "控制自动重载配置文件时长",
"default_value": 180
}
}
reload_plugins_manager = on_command( reload_plugins_manager = on_command(
@@ -49,24 +36,9 @@ reload_plugins_manager = on_command(
@reload_plugins_manager.handle() @reload_plugins_manager.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
plugins2settings_manager.reload() plugins2settings_manager.reload()
plugins2cd_manager.reload() plugins2cd_manager.reload()
plugins2block_manager.reload() plugins2block_manager.reload()
group_manager.reload() group_manager.reload()
Config.reload()
await reload_plugins_manager.send("重载完成...") 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,12 +1,11 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from models.level_user import LevelUser from models.level_user import LevelUser
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.typing import T_State
from utils.utils import get_message_at, is_number from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Message
from utils.utils import get_message_at, get_message_text, is_number
from services.log import logger from services.log import logger
from utils.message_builder import at from utils.message_builder import at
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "用户权限管理 [Superuser]" __zx_plugin_name__ = "用户权限管理 [Superuser]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -37,49 +36,40 @@ super_cmd = on_command(
@super_cmd.handle() @super_cmd.handle()
async def _( async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
bot: Bot, group_id = -1
event: MessageEvent, level = 0
cmd: Tuple[str, ...] = Command(),
arg: Message = CommandArg(),
):
group_id = event.group_id if isinstance(event, GroupMessageEvent) else -1
level = None
args = arg.extract_plain_text().strip().split()
qq = get_message_at(event.json())
flag = 2
try: try:
if qq: args = get_message_text(event.json()).split()
qq = qq[0] qq = get_message_at(event.json())
if cmd[0][:2] == "添加" and args and is_number(args[0]): flag = -1
level = int(args[0]) if not qq:
else: if len(args) > 2:
if cmd[0][:2] == "添加": if is_number(args[0]) and is_number(args[1]) and is_number(args[2]):
if (
len(args) > 2
and is_number(args[0])
and is_number(args[1])
and is_number(args[2])
):
qq = int(args[0]) qq = int(args[0])
group_id = int(args[1]) group_id = int(args[1])
level = int(args[2]) level = int(args[2])
flag = 1
else:
await super_cmd.finish("所有参数必须是数字!", at_sender=True)
else: else:
if len(args) > 1 and is_number(args[0]) and is_number(args[1]): await super_cmd.finish(
qq = int(args[0]) "权限参数不完全\n\t格式:添加/删除权限 [at] [level]"
group_id = int(args[1]) "\n\t格式:添加/删除权限 [qq] [group_id] [level]",
flag = 1 at_sender=True,
level = -1 if cmd[0][:2] == "删除" else level )
if group_id == -1 or not level or not qq: else:
raise IndexError() if not is_number(args[0]):
except IndexError: await super_cmd.finish("所有参数必须是数字!", at_sender=True)
await super_cmd.finish(__plugin_usage__) level = int(args[0])
try: qq = qq[0]
if cmd[0][:2] == "添加": group_id = event.group_id
flag = 2
if state["_prefix"]["raw_command"][:2] == "添加":
if await LevelUser.set_level(qq, group_id, level, 1): if await LevelUser.set_level(qq, group_id, level, 1):
result = f"添加管理成功, 权限: {level}" result = "添加管理成功, 权限: " + str(level)
else: else:
result = f"管理已存在, 更新权限: {level}" result = "管理已存在, 更新权限: " + str(level)
else: else:
if await LevelUser.delete_level(qq, event.group_id): if await LevelUser.delete_level(qq, event.group_id):
result = "删除管理成功!" result = "删除管理成功!"
@@ -90,10 +80,7 @@ async def _(
elif flag == 1: elif flag == 1:
await bot.send_group_msg( await bot.send_group_msg(
group_id=group_id, group_id=group_id,
message=Message( message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"),
f"{at(qq)}管理员修改了你的权限"
f"\n--------\n你当前的权限等级:{level if level != -1 else 0}"
),
) )
await super_cmd.send("修改成功") await super_cmd.send("修改成功")
except Exception as e: except Exception as e:
+6 -8
View File
@@ -1,12 +1,11 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import is_number from utils.utils import get_message_text, is_number
from services.log import logger from services.log import logger
from utils.manager import group_manager from utils.manager import group_manager
from nonebot.params import Command, CommandArg
from typing import Tuple
__zx_plugin_name__ = "超级用户被动开关 [Superuser]" __zx_plugin_name__ = "超级用户被动开关 [Superuser]"
@@ -35,9 +34,8 @@ oc_gb = on_command(
@oc_gb.handle() @oc_gb.handle()
async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
cmd = cmd[0] group = get_message_text(event.json())
group = arg.extract_plain_text().strip()
if group: if group:
if is_number(group): if is_number(group):
group = int(group) group = int(group)
@@ -46,7 +44,7 @@ async def _(bot: Bot, event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg
break break
else: else:
await oc_gb.finish("没有加入这个群...", at_sender=True) await oc_gb.finish("没有加入这个群...", at_sender=True)
if cmd == "开启广播通知": if state["_prefix"]["raw_command"] == "开启广播通知":
logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播") logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播")
await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True) await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True)
else: else:
@@ -1,5 +1,7 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from utils.utils import get_bot from utils.utils import get_bot
from services.log import logger from services.log import logger
@@ -32,7 +34,7 @@ update_friend_info = on_command(
@update_group_info.handle() @update_group_info.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
bot = get_bot() bot = get_bot()
gl = await bot.get_group_list() gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl] gl = [g["group_id"] for g in gl]
@@ -45,7 +47,6 @@ async def _():
group_info["group_name"], group_info["group_name"],
group_info["max_member_count"], group_info["max_member_count"],
group_info["member_count"], group_info["member_count"],
1
): ):
num += 1 num += 1
logger.info(f"自动更新群组 {g} 信息成功") logger.info(f"自动更新群组 {g} 信息成功")
@@ -56,7 +57,7 @@ async def _():
@update_friend_info.handle() @update_friend_info.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
num = 0 num = 0
rst = "" rst = ""
fl = await get_bot().get_friend_list() fl = await get_bot().get_friend_list()
+5 -2
View File
@@ -1,15 +1,18 @@
from nonebot import on_command from nonebot import on_command
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.rule import to_me from nonebot.rule import to_me
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from .data_source import create_help_image from .data_source import create_help_image
from pathlib import Path
__zx_plugin_name__ = '超级用户帮助 [Superuser]' __zx_plugin_name__ = '超级用户帮助 [Superuser]'
superuser_help_image = IMAGE_PATH / 'superuser_help.png' superuser_help_image = Path(IMAGE_PATH) / 'superuser_help.png'
if superuser_help_image.exists(): if superuser_help_image.exists():
superuser_help_image.unlink() superuser_help_image.unlink()
@@ -20,7 +23,7 @@ super_help = on_command(
@super_help.handle() @super_help.handle()
async def _(): async def _(bot: Bot, event: Event, state: T_State):
if not superuser_help_image.exists(): if not superuser_help_image.exists():
await create_help_image() await create_help_image()
x = image(superuser_help_image) x = image(superuser_help_image)
+6 -5
View File
@@ -2,7 +2,8 @@ from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from services.log import logger from services.log import logger
from utils.utils import get_matchers from utils.utils import get_matchers
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.cqhttp import Bot
from pathlib import Path
from nonebot import Driver from nonebot import Driver
import asyncio import asyncio
import nonebot import nonebot
@@ -10,9 +11,9 @@ import nonebot
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
background = IMAGE_PATH / "background" / "0.png" background = Path(IMAGE_PATH) / "background" / "0.png"
superuser_help_image = IMAGE_PATH / "superuser_help.png" superuser_help_image = Path(IMAGE_PATH) / "superuser_help.png"
@driver.on_bot_connect @driver.on_bot_connect
@@ -35,7 +36,7 @@ def _create_help_image():
for matcher in _matchers: for matcher in _matchers:
plugin_name = "" plugin_name = ""
try: try:
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.module)
_module = _plugin.module _module = _plugin.module
try: try:
plugin_name = _module.__getattribute__("__zx_plugin_name__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -67,7 +68,7 @@ def _create_help_image():
width = width if width > x else x width = width if width > x else x
except Exception as e: except Exception as e:
logger.warning( logger.warning(
f"获取超级用户插件 {matcher.plugin_name}: {plugin_name} 设置失败... {type(e)}:{e}" f"获取超级用户插件 {matcher.module}: {plugin_name} 设置失败... {type(e)}:{e}"
) )
height = len(help_str.split("\n")) * 33 height = len(help_str.split("\n")) * 33
width += 500 width += 500
+3 -1
View File
@@ -1,4 +1,6 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.message_builder import image from utils.message_builder import image
@@ -25,7 +27,7 @@ update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, b
@update_info.handle() @update_info.handle()
async def _(): async def _(bot: Bot, event: Event, state: T_State):
img = image("update_info.png") img = image("update_info.png")
if img: if img:
await update_info.finish(image("update_info.png")) await update_info.finish(image("update_info.png"))
+3 -3
View File
@@ -1,10 +1,11 @@
import nonebot import nonebot
from nonebot.adapters.onebot.v11 import Adapter from nonebot.adapters.cqhttp import Bot as CQHTTPBot
from services.db_context import init, disconnect from services.db_context import init, disconnect
nonebot.init() nonebot.init()
driver = nonebot.get_driver() driver = nonebot.get_driver()
driver.register_adapter(Adapter) driver.register_adapter("cqhttp", CQHTTPBot)
config = driver.config config = driver.config
driver.on_startup(init) driver.on_startup(init)
driver.on_shutdown(disconnect) driver.on_shutdown(disconnect)
@@ -12,7 +13,6 @@ driver.on_shutdown(disconnect)
nonebot.load_plugin("nonebot_plugin_apscheduler") nonebot.load_plugin("nonebot_plugin_apscheduler")
nonebot.load_plugins("basic_plugins") nonebot.load_plugins("basic_plugins")
nonebot.load_plugins("plugins") nonebot.load_plugins("plugins")
nonebot.load_plugins("extensive_plugin")
# 最后加载权限控制 # 最后加载权限控制
nonebot.load_plugins("basic_plugins/hooks") nonebot.load_plugins("basic_plugins/hooks")
-5
View File
@@ -2,10 +2,6 @@ from typing import Optional
from pathlib import Path from pathlib import Path
from .utils import ConfigsManager from .utils import ConfigsManager
import platform
if platform.system() == "Linux":
import os
hostip = os.popen("cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }'").read().replace("\n","")
# 回复消息名称 # 回复消息名称
NICKNAME: str = "小真寻" NICKNAME: str = "小真寻"
@@ -22,7 +18,6 @@ port: str = "" # 数据库端口
database: str = "" # 数据库名称 database: str = "" # 数据库名称
# 代理,例如 "http://127.0.0.1:7890" # 代理,例如 "http://127.0.0.1:7890"
# 如果是WLS 可以 f"http://{hostip}:7890" 使用寄主机的代理
SYSTEM_PROXY: Optional[str] = None # 全局代理 SYSTEM_PROXY: Optional[str] = None # 全局代理
+21 -25
View File
@@ -1,45 +1,41 @@
from pathlib import Path from pathlib import Path
import os
# 图片路径 # 图片路径
IMAGE_PATH = Path() / "resources" / "image" IMAGE_PATH = Path("resources/img/")
# 语音路径 # 音频路径
RECORD_PATH = Path() / "resources" / "record" VOICE_PATH = Path("resources/voice/")
# 文本路径 # 文本路径
TEXT_PATH = Path() / "resources" / "text" TEXT_PATH = Path("resources/txt/")
# 日志路径 # 日志路径
LOG_PATH = Path() / "log" LOG_PATH = Path("log/")
# 字体路径 # 字体路径
FONT_PATH = Path() / "resources" / "font" FONT_PATH = Path("resources/ttf/")
# 数据路径 # 数据路径
DATA_PATH = Path() / "data" DATA_PATH = Path("data/")
# 临时数据路径 # 临时图片路径
TEMP_PATH = Path() / "resources" / "temp" TEMP_PATH = Path("resources/img/temp/")
def load_path(): def init_path():
old_img_dir = Path() / "resources" / "img" global IMAGE_PATH, VOICE_PATH, TEXT_PATH, LOG_PATH, FONT_PATH, DATA_PATH, TEMP_PATH
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) IMAGE_PATH.mkdir(parents=True, exist_ok=True)
RECORD_PATH.mkdir(parents=True, exist_ok=True) VOICE_PATH.mkdir(parents=True, exist_ok=True)
TEXT_PATH.mkdir(parents=True, exist_ok=True) TEXT_PATH.mkdir(parents=True, exist_ok=True)
LOG_PATH.mkdir(parents=True, exist_ok=True) LOG_PATH.mkdir(parents=True, exist_ok=True)
FONT_PATH.mkdir(parents=True, exist_ok=True) FONT_PATH.mkdir(parents=True, exist_ok=True)
DATA_PATH.mkdir(parents=True, exist_ok=True) DATA_PATH.mkdir(parents=True, exist_ok=True)
TEMP_PATH.mkdir(parents=True, exist_ok=True) TEMP_PATH.mkdir(parents=True, exist_ok=True)
IMAGE_PATH = str(IMAGE_PATH.absolute()) + '/'
VOICE_PATH = str(VOICE_PATH.absolute()) + '/'
TEXT_PATH = str(TEXT_PATH.absolute()) + '/'
LOG_PATH = str(LOG_PATH.absolute()) + '/'
FONT_PATH = str(FONT_PATH.absolute()) + '/'
DATA_PATH = str(DATA_PATH.absolute()) + '/'
TEMP_PATH = str(TEMP_PATH.absolute()) + '/'
load_path()
init_path()
+6 -63
View File
@@ -2,7 +2,6 @@ from typing import Optional, Any, Union
from pathlib import Path from pathlib import Path
from ruamel.yaml import YAML from ruamel.yaml import YAML
from ruamel import yaml from ruamel import yaml
from ruamel.yaml.scanner import ScannerError
class ConfigsManager: class ConfigsManager:
@@ -12,34 +11,14 @@ class ConfigsManager:
def __init__(self, file: Path): def __init__(self, file: Path):
self._data: dict = {} self._data: dict = {}
self._simple_data: dict = {}
self._admin_level_data = [] self._admin_level_data = []
self._simple_file = Path() / "configs" / "config.yaml"
if file: if file:
file.parent.mkdir(exist_ok=True, parents=True) file.parent.mkdir(exist_ok=True, parents=True)
self.file = file self.file = file
_yaml = YAML()
if file.exists(): if file.exists():
_yaml = YAML()
with open(file, "r", encoding="utf8") as f: with open(file, "r", encoding="utf8") as f:
self._data = _yaml.load(f) self._data = _yaml.load(f)
if not self._data:
self.file.unlink()
raise ValueError(
"配置文件为空!\n"
"***********************************************************\n"
"****** 配置文件 plugins2config.yaml 为空,已删除,请重启 ******\n"
"***********************************************************"
)
if self._simple_file.exists():
try:
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
except ScannerError as e:
raise ScannerError(
f"{e}\n**********************************************\n"
f"****** 可能为config.yaml配置文件填写不规范 ******\n"
f"**********************************************"
)
def add_plugin_config( def add_plugin_config(
self, self,
@@ -92,26 +71,17 @@ class ConfigsManager:
""" """
if module in self._data.keys(): if module in self._data.keys():
del self._data[module] del self._data[module]
self.save()
def set_config(self, module: str, key: str, value: Any, auto_save: bool = False, save_simple_data: bool = True): def set_config(self, module: str, key: str, value: str):
""" """
设置配置值 设置配置值
:param module: 模块名 :param module: 模块名
:param key: 配置名称 :param key: 配置名称
:param value: 值 :param value: 值
:param auto_save: 自动保存
:param save_simple_data: 保存至config.yaml
""" """
if module in self._data.keys(): if module in self._data.keys():
if ( if self._data[module].get(key) is not None:
self._data[module].get(key) is not None
and self._data[module][key] != value
):
self._data[module][key]["value"] = value self._data[module][key]["value"] = value
self._simple_data[module][key] = value
if auto_save:
self.save(save_simple_data=save_simple_data)
def set_help(self, module: str, key: str, help_: str): def set_help(self, module: str, key: str, help_: str):
""" """
@@ -123,9 +93,8 @@ class ConfigsManager:
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["help"] = help_ self._data[module][key]["help"] = help_
self.save()
def set_default_value(self, module: str, key: str, value: Any): def set_default_value(self, module: str, key: str, value: str):
""" """
设置配置默认值 设置配置默认值
:param module: 模块名 :param module: 模块名
@@ -135,11 +104,8 @@ class ConfigsManager:
if module in self._data.keys(): if module in self._data.keys():
if self._data[module].get(key) is not None: if self._data[module].get(key) is not None:
self._data[module][key]["default_value"] = value self._data[module][key]["default_value"] = value
self.save()
def get_config( def get_config(self, module: str, key: str, default: Optional[Any] = None) -> Optional[Any]:
self, module: str, key: str, default: Optional[Any] = None
) -> Optional[Any]:
""" """
获取指定配置值 获取指定配置值
:param module: 模块名 :param module: 模块名
@@ -176,40 +142,17 @@ class ConfigsManager:
if key in self._data.keys(): if key in self._data.keys():
return self._data[key] return self._data[key]
def save(self, path: Union[str, Path] = None, save_simple_data: bool = False): def save(self, path: Union[str, Path] = None):
""" """
保存数据 保存数据
:param path: 路径 :param path: 路径
:param save_simple_data: 同时保存至config.yaml
""" """
if save_simple_data:
with open(self._simple_file, "w", encoding="utf8") as f:
yaml.dump(
self._simple_data,
f,
indent=2,
Dumper=yaml.RoundTripDumper,
allow_unicode=True,
)
path = path if path else self.file path = path if path else self.file
with open(path, "w", encoding="utf8") as f: with open(path, "w", encoding="utf8") as f:
yaml.dump( yaml.dump(
self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
) )
def reload(self):
"""
重新加载配置文件
"""
_yaml = YAML()
if self._simple_file.exists():
with open(self._simple_file, "r", encoding="utf8") as f:
self._simple_data = _yaml.load(f)
for key in self._simple_data.keys():
for k in self._simple_data[key].keys():
self._data[key][k]["value"] = self._simple_data[key][k]
self.save()
def get_admin_level_data(self): def get_admin_level_data(self):
""" """
获取管理插件等级 获取管理插件等级
+157 -1452
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 KiB

After

Width:  |  Height:  |  Size: 694 KiB

+93 -78
View File
@@ -1,5 +1,4 @@
from services.db_context import db from services.db_context import db
from typing import Dict
from typing import Optional, List from typing import Optional, List
from services.log import logger from services.log import logger
@@ -8,32 +7,33 @@ class BagUser(db.Model):
__tablename__ = "bag_users" __tablename__ = "bag_users"
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False) belonging_group = db.Column(db.BigInteger(), nullable=False)
gold = db.Column(db.Integer(), default=100) gold = db.Column(db.Integer(), default=100)
props = db.Column(db.TEXT(), nullable=False, default="") # 旧道具字段(废弃) props = db.Column(db.TEXT(), nullable=False, default="")
spend_total_gold = db.Column(db.Integer(), default=0) spend_total_gold = db.Column(db.Integer(), default=0)
get_total_gold = db.Column(db.Integer(), default=0) get_total_gold = db.Column(db.Integer(), default=0)
get_today_gold = db.Column(db.Integer(), default=0) get_today_gold = db.Column(db.Integer(), default=0)
spend_today_gold = db.Column(db.Integer(), default=0) spend_today_gold = db.Column(db.Integer(), default=0)
property = db.Column(db.JSON(), nullable=False, default={}) # 新道具字段
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "group_id", unique=True) _idx1 = db.Index("bag_group_users_idx1", "user_qq", "belonging_group", unique=True)
@classmethod @classmethod
async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str: async def get_my_total_gold(cls, user_qq: int, belonging_group: int) -> str:
""" """
说明: 说明:
获取金币概况 获取金币概况
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if not user: if not user:
user = await cls.create( user = await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
) )
return ( return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}" f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
@@ -42,56 +42,62 @@ class BagUser(db.Model):
) )
@classmethod @classmethod
async def get_gold(cls, user_qq: int, group_id: int) -> int: async def get_gold(cls, user_qq: int, belonging_group: int) -> int:
""" """
说明: 说明:
获取当前金币 获取当前金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if user: if user:
return user.gold return user.gold
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
) )
return 100 return 100
@classmethod @classmethod
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]: async def get_props(cls, user_qq: int, belonging_group: int) -> str:
""" """
说明: 说明:
获取当前道具 获取当前道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
user = await query.gino.first() user = await query.gino.first()
if user: if user:
return user.property return user.props
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
) )
return {} return ""
@classmethod @classmethod
async def add_gold(cls, user_qq: int, group_id: int, num: int): async def add_gold(cls, user_qq: int, belonging_group: int, num: int):
""" """
说明: 说明:
增加金币 增加金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
:param num: 金币数量 :param num: 金币数量
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
if user: if user:
@@ -103,23 +109,25 @@ class BagUser(db.Model):
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
gold=100 + num, gold=100 + num,
get_total_gold=num, get_total_gold=num,
get_today_gold=num, get_today_gold=num,
) )
@classmethod @classmethod
async def spend_gold(cls, user_qq: int, group_id: int, num: int): async def spend_gold(cls, user_qq: int, belonging_group: int, num: int):
""" """
说明: 说明:
花费金币 花费金币
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
:param num: 金币数量 :param num: 金币数量
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
if user: if user:
@@ -131,96 +139,103 @@ class BagUser(db.Model):
else: else:
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
gold=100 - num, gold=100 - num,
spend_total_gold=num, spend_total_gold=num,
spend_today_gold=num, spend_today_gold=num,
) )
@classmethod @classmethod
async def add_property(cls, user_qq: int, group_id: int, name: str): async def add_props(cls, user_qq: int, belonging_group: int, name: str):
""" """
说明: 说明:
增加道具 增加道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
:param name: 道具名称 :param name: 道具名称
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
if user: if user:
p = user.property await user.update(props=user.props + f"{name},").apply()
if p.get(name) is None:
p[name] = 1
else:
p[name] += 1
await user.update(property=p).apply()
else: else:
await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1}) await cls.create(
user_qq=user_qq, belonging_group=belonging_group, props=f"{name},"
)
@classmethod @classmethod
async def delete_property( async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool:
cls, user_qq: int, group_id: int, name: str, num: int = 1
) -> bool:
""" """
说明: 说明:
使用/删除 道具 使用道具
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 所在群号 :param belonging_group: 所在群号
:param name: 道具名称 :param name: 道具名称
:param num: 使用个数
""" """
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
if user: if user:
property_ = user.property rst = ""
if name in property_: props = user.props
if property_.get(name) == num: if props.find(name) != -1:
del property_[name] props = props.split(",")
else: try:
property_[name] -= num index = props.index(name)
await user.update(property=property_).apply() except ValueError:
return False
props = props[:index] + props[index + 1 :]
for p in props:
if p != "":
rst += p + ","
await user.update(props=rst).apply()
return True return True
return False else:
return False
else:
return False
@classmethod @classmethod
async def buy_property( async def buy_props(
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int cls, user_qq: int, belonging_group: int, goods: "GoodsInfo", goods_num: int
) -> bool: ) -> bool:
""" """
说明: 说明:
购买道具 购买道具
参数: 参数:
:param user_qq: 用户qq :param user_qq: 用户qq
:param group_id: 所在群聊 :param belonging_group: 所在群聊
:param goods: 商品 :param goods: 商品
:param goods_num: 商品数量 :param goods_num: 商品数量
""" """
try: try:
# 折扣后金币 # 折扣后金币
spend_gold = goods.goods_discount * goods.goods_price * goods_num spend_gold = goods.goods_discount * goods.goods_price * goods_num
await BagUser.spend_gold(user_qq, group_id, spend_gold) await BagUser.spend_gold(user_qq, belonging_group, spend_gold)
for _ in range(goods_num): for _ in range(goods_num):
await BagUser.add_property(user_qq, group_id, goods.goods_name) await BagUser.add_props(user_qq, belonging_group, goods.goods_name)
return True return True
except Exception as e: except Exception as e:
logger.error(f"buy_property 发生错误 {type(e)}:{e}") logger.error(f"buy_props 发生错误 {type(e)}:{e}")
return False return False
@classmethod @classmethod
async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]: async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]:
""" """
说明: 说明:
获取所有用户数据 获取所有用户数据
参数: 参数:
:param group_id: 群号 :param group_id: 群号
""" """
if not group_id: if not group_id:
query = await cls.query.gino.all() query = await cls.query.gino.all()
else: else:
query = await cls.query.where((cls.group_id == group_id)).gino.all() query = await cls.query.where((cls.belonging_group == group_id)).gino.all()
return query return query
+12 -12
View File
@@ -15,9 +15,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def check_ban_level(cls, user_qq: int, level: int) -> bool: async def check_ban_level(cls, user_qq: int, level: int) -> bool:
""" """
说明: 说明:
检测ban掉目标的用户与unban用户的权限等级大小 检测ban掉目标的用户与unban用户的权限等级大小
参数: 参数:
:param user_qq: unban用户的qq号 :param user_qq: unban用户的qq号
:param level: ban掉目标用户的权限等级 :param level: ban掉目标用户的权限等级
""" """
@@ -31,9 +31,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def check_ban_time(cls, user_qq: int) -> str: async def check_ban_time(cls, user_qq: int) -> str:
""" """
说明: 说明:
检测用户被ban时长 检测用户被ban时长
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
""" """
query = cls.query.where((cls.user_qq == user_qq)) query = cls.query.where((cls.user_qq == user_qq))
@@ -49,9 +49,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def is_ban(cls, user_qq: int) -> bool: async def is_ban(cls, user_qq: int) -> bool:
""" """
说明: 说明:
判断用户是否被ban 判断用户是否被ban
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
""" """
if await cls.check_ban_time(user_qq): if await cls.check_ban_time(user_qq):
@@ -63,9 +63,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def is_super_ban(cls, user_qq: int) -> bool: async def is_super_ban(cls, user_qq: int) -> bool:
""" """
说明: 说明:
判断用户是否被ban 判断用户是否被ban
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
""" """
user = await cls.query.where((cls.user_qq == user_qq)).gino.first() user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
@@ -77,9 +77,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool: async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
""" """
说明: 说明:
ban掉目标用户 ban掉目标用户
参数: 参数:
:param user_qq: 目标用户qq号 :param user_qq: 目标用户qq号
:param ban_level: 使用ban命令用户的权限 :param ban_level: 使用ban命令用户的权限
:param duration: ban时长,秒 :param duration: ban时长,秒
@@ -104,9 +104,9 @@ class BanUser(db.Model):
@classmethod @classmethod
async def unban(cls, user_qq: int) -> bool: async def unban(cls, user_qq: int) -> bool:
""" """
说明: 说明:
unban用户 unban用户
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
""" """
query = cls.query.where((cls.user_qq == user_qq)) query = cls.query.where((cls.user_qq == user_qq))
-214
View File
@@ -1,214 +0,0 @@
from datetime import datetime, timedelta
from typing import List, Literal, Optional, Tuple, Union
from services.db_context import db
class ChatHistory(db.Model):
__tablename__ = "chat_history"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger())
text = db.Column(db.Text())
plain_text = db.Column(db.Text())
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str, plain_text: str):
await cls.create(
user_qq=user_qq, group_id=group_id, text=text, plain_text=plain_text, create_time=datetime.now()
)
@classmethod
async def get_user_msg(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取用户消息
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return await cls._get_msg(uid, None, "user", msg_type, days).gino.all()
@classmethod
async def get_group_user_msg(
cls,
uid: int,
gid: int,
limit: int = 10,
date_scope: Tuple[datetime, datetime] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊指定用户聊天记录
参数:
:param uid: qq
:param gid: 群号
:param limit: 获取数量
:param date_scope: 日期范围,默认None为全搜索
"""
return (
await cls._get_msg(uid, gid, "group", days=date_scope)
.limit(limit)
.gino.all()
)
@classmethod
async def get_group_user_msg_count(cls, uid: int, gid: int) -> Optional[int]:
"""
说明:
查询群聊指定用户的聊天记录数量
参数:
:param uid: qq
:param gid: 群号
"""
if x := await db.first(
db.text(
f"SELECT COUNT(id) as sum FROM public.chat_history WHERE user_qq = {uid} AND group_id = {gid}"
)
):
return x[0]
return None
@classmethod
async def get_group_msg_rank(
cls,
gid: int,
limit: int = 10,
order: str = "DESC",
date_scope: Optional[Tuple[datetime, datetime]] = None,
) -> Optional[Tuple[int, int]]:
"""
说明:
获取排行数据
参数:
:param gid: 群号
:param limit: 获取数量
:param order: 排序类型,desc,des
:param date_scope: 日期范围
"""
sql = f"SELECT user_qq, COUNT(id) as sum FROM public.chat_history WHERE group_id = {gid} "
if date_scope:
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
return await db.all(db.text(sql))
@classmethod
async def get_group_first_msg_datetime(cls, gid: int) -> Optional[datetime]:
"""
说明:
获取群第一条记录消息时间
参数:
:param gid:
"""
if (
msg := await cls.query.where(cls.group_id == gid)
.order_by(cls.create_time)
.gino.first()
):
return msg.create_time
return None
@classmethod
async def get_user_msg_count(
cls,
uid: int,
msg_type: Optional[Literal["private", "group"]],
days: Optional[int] = None,
) -> int:
"""
说明:
获取用户消息数量
参数:
:param uid: 用户qq
:param msg_type: 消息类型,私聊或群聊
:param days: 限制日期
"""
return (
await cls._get_msg(uid, None, "user", msg_type, days, True).gino.first()
)[0]
@classmethod
async def get_group_msg(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return await cls._get_msg(None, gid, "group", None, days).gino.all()
@classmethod
async def get_group_msg_count(
cls,
gid: int,
days: Optional[int] = None,
) -> List["ChatHistory"]:
"""
说明:
获取群聊消息数量
参数:
:param gid: 用户qq
:param days: 限制日期
"""
return (await cls._get_msg(None, gid, "group", None, days, True).gino.first())[
0
]
@classmethod
def _get_msg(
cls,
uid: Optional[int],
gid: Optional[int],
type_: Literal["user", "group"],
msg_type: Optional[Literal["private", "group"]] = None,
days: Optional[Union[int, Tuple[datetime, datetime]]] = None,
is_select_count: bool = False,
):
"""
说明:
获取消息查询query
参数:
:param uid: 用户qq
:param gid: 群号
:param type_: 类型,私聊或群聊
:param msg_type: 消息类型,用户或群聊
:param days: 限制日期
"""
if is_select_count:
setattr(ChatHistory, "count", db.func.count(cls.id).label("count"))
query = cls.select("count")
else:
query = cls.query
if type_ == "user":
query = query.where(cls.user_qq == uid)
if msg_type == "private":
query = query.where(cls.group_id == None)
elif msg_type == "group":
query = query.where(cls.group_id != None)
else:
query = query.where(cls.group_id == gid)
if uid:
query = query.where(cls.user_qq == uid)
if days:
if isinstance(days, int):
query = query.where(
cls.create_time >= datetime.now() - timedelta(days=days)
)
elif isinstance(days, tuple):
query = query.where(cls.create_time >= days[0]).where(
cls.create_time <= days[1]
)
return query
+10 -10
View File
@@ -15,9 +15,9 @@ class FriendUser(db.Model):
@classmethod @classmethod
async def get_user_name(cls, user_id: int) -> str: async def get_user_name(cls, user_id: int) -> str:
""" """
说明: 说明:
获取好友用户名称 获取好友用户名称
参数: 参数:
:param user_id: qq号 :param user_id: qq号
""" """
query = cls.query.where(cls.user_id == user_id) query = cls.query.where(cls.user_id == user_id)
@@ -30,9 +30,9 @@ class FriendUser(db.Model):
@classmethod @classmethod
async def add_friend_info(cls, user_id: int, user_name: str) -> bool: async def add_friend_info(cls, user_id: int, user_name: str) -> bool:
""" """
说明: 说明:
添加好友信息 添加好友信息
参数: 参数:
:param user_id: qq号 :param user_id: qq号
:param user_name: 用户名称 :param user_name: 用户名称
""" """
@@ -55,9 +55,9 @@ class FriendUser(db.Model):
@classmethod @classmethod
async def delete_friend_info(cls, user_id: int) -> bool: async def delete_friend_info(cls, user_id: int) -> bool:
""" """
说明: 说明:
删除好友信息 删除好友信息
参数: 参数:
:param user_id: qq号 :param user_id: qq号
""" """
try: try:
@@ -72,9 +72,9 @@ class FriendUser(db.Model):
@classmethod @classmethod
async def get_friend_nickname(cls, user_id: int) -> str: async def get_friend_nickname(cls, user_id: int) -> str:
""" """
说明: 说明:
获取用户昵称 获取用户昵称
参数: 参数:
:param user_id: qq号 :param user_id: qq号
""" """
query = cls.query.where(cls.user_id == user_id) query = cls.query.where(cls.user_id == user_id)
@@ -92,9 +92,9 @@ class FriendUser(db.Model):
@classmethod @classmethod
async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool: async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool:
""" """
说明: 说明:
设置用户昵称 设置用户昵称
参数: 参数:
:param user_id: qq号 :param user_id: qq号
:param nickname: 昵称 :param nickname: 昵称
""" """
+37 -103
View File
@@ -1,5 +1,5 @@
from services.db_context import db from services.db_context import db
from typing import Optional, List, Tuple from typing import Optional, List
from services.log import logger from services.log import logger
@@ -12,33 +12,27 @@ class GoodsInfo(db.Model):
goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述 goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述
goods_discount = db.Column(db.Numeric(scale=3, asdecimal=False), default=1) # 打折 goods_discount = db.Column(db.Numeric(scale=3, asdecimal=False), default=1) # 打折
goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时 goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时
daily_limit = db.Column(db.Integer(), nullable=False, default=0) # 每日购买限制
daily_purchase_limit = db.Column(
db.JSON(), nullable=False, default={}
) # 每日购买限制数据存储
_idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True) _idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True)
@classmethod @classmethod
async def add_goods( async def add_goods(
cls, cls,
goods_name: str, goods_name: str,
goods_price: int, goods_price: int,
goods_description: str, goods_description: str,
goods_discount: float = 1, goods_discount: float = 1,
goods_limit_time: int = 0, goods_limit_time: int = 0,
daily_limit: int = 0,
) -> bool: ) -> bool:
""" """
说明: 说明:
添加商品 添加商品
参数: 参数:
:param goods_name: 商品名称 :param goods_name: 商品名称
:param goods_price: 商品价格 :param goods_price: 商品价格
:param goods_description: 商品简介 :param goods_description: 商品简介
:param goods_discount: 商品折扣 :param goods_discount: 商品折扣
:param goods_limit_time: 商品限时 :param goods_limit_time: 商品限时
:param daily_limit: 每日购买限制
""" """
try: try:
if not await cls.get_goods_info(goods_name): if not await cls.get_goods_info(goods_name):
@@ -48,7 +42,6 @@ class GoodsInfo(db.Model):
goods_description=goods_description, goods_description=goods_description,
goods_discount=goods_discount, goods_discount=goods_discount,
goods_limit_time=goods_limit_time, goods_limit_time=goods_limit_time,
daily_limit=daily_limit,
) )
return True return True
except Exception as e: except Exception as e:
@@ -58,15 +51,15 @@ class GoodsInfo(db.Model):
@classmethod @classmethod
async def delete_goods(cls, goods_name: str) -> bool: async def delete_goods(cls, goods_name: str) -> bool:
""" """
说明: 说明:
删除商品 删除商品
参数: 参数:
:param goods_name: 商品名称 :param goods_name: 商品名称
""" """
query = ( query = (
await cls.query.where(cls.goods_name == goods_name) await cls.query.where(cls.goods_name == goods_name)
.with_for_update() .with_for_update()
.gino.first() .gino.first()
) )
if not query: if not query:
return False return False
@@ -75,59 +68,59 @@ class GoodsInfo(db.Model):
@classmethod @classmethod
async def update_goods( async def update_goods(
cls, cls,
goods_name: str, goods_name: str,
goods_price: Optional[int] = None, goods_price: Optional[int] = None,
goods_description: Optional[str] = None, goods_description: Optional[str] = None,
goods_discount: Optional[float] = None, goods_discount: Optional[float] = None,
goods_limit_time: Optional[int] = None, goods_limit_time: Optional[int] = None,
daily_limit: Optional[int] = None
) -> bool: ) -> bool:
""" """
说明: 说明:
更新商品信息 更新商品信息
参数: 参数:
:param goods_name: 商品名称 :param goods_name: 商品名称
:param goods_price: 商品价格 :param goods_price: 商品价格
:param goods_description: 商品简介 :param goods_description: 商品简介
:param goods_discount: 商品折扣 :param goods_discount: 商品折扣
:param goods_limit_time: 商品限时时间 :param goods_limit_time: 商品限时时间
:param daily_limit: 每日次数限制
""" """
try: try:
query = ( query = (
await cls.query.where(cls.goods_name == goods_name) await cls.query.where(cls.goods_name == goods_name)
.with_for_update() .with_for_update()
.gino.first() .gino.first()
) )
if not query: if not query:
return False return False
await query.update( if goods_price:
goods_price=goods_price or query.goods_price, await query.update(goods_price=goods_price).apply()
goods_description=goods_description or query.goods_description, if goods_description:
goods_discount=goods_discount or query.goods_discount, await query.update(goods_description=goods_description).apply()
goods_limit_time=goods_limit_time if goods_limit_time is not None else query.goods_limit_time, if goods_discount:
daily_limit=daily_limit if daily_limit is not None else query.daily_limit, await query.update(goods_discount=goods_discount).apply()
).apply() if goods_limit_time:
await query.update(goods_limit_time=goods_limit_time).apply()
return True return True
except Exception as e: except Exception as e:
logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}") logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}")
return False return False
@classmethod @classmethod
async def get_goods_info(cls, goods_name: str) -> "GoodsInfo": async def get_goods_info(cls, goods_name: str) -> "GoodsInfo":
""" """
说明: 说明:
获取商品对象 获取商品对象
参数: 参数:
:param goods_name: 商品名称 :param goods_name: 商品名称
""" """
return await cls.query.where(cls.goods_name == goods_name).gino.first() query = await cls.query.where(cls.goods_name == goods_name).gino.first()
return query
@classmethod @classmethod
async def get_all_goods(cls) -> List["GoodsInfo"]: async def get_all_goods(cls) -> List["GoodsInfo"]:
""" """
说明: 说明:
获得全部有序商品对象 获得全部有序商品对象
""" """
query = await cls.query.gino.all() query = await cls.query.gino.all()
@@ -138,62 +131,3 @@ class GoodsInfo(db.Model):
goods_lst.append([x for x in query if x.id == min_id][0]) goods_lst.append([x for x in query if x.id == min_id][0])
id_lst.remove(min_id) id_lst.remove(min_id)
return goods_lst return goods_lst
@classmethod
async def add_user_daily_purchase(
cls, goods: "GoodsInfo", user_id: int, group_id: int, 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.update(daily_purchase_limit=goods.daily_purchase_limit).apply()
@classmethod
async def check_user_daily_purchase(
cls, goods: "GoodsInfo", user_id: int, group_id: int, 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
async def reset_daily_purchase(cls):
"""
重置每次次数限制
"""
await cls.update.values(daily_purchase_limit={}).gino.status()
+7 -7
View File
@@ -17,9 +17,9 @@ class GroupInfo(db.Model):
@classmethod @classmethod
async def get_group_info(cls, group_id: int) -> "GroupInfo": async def get_group_info(cls, group_id: int) -> "GroupInfo":
""" """
说明: 说明:
获取群信息 获取群信息
参数: 参数:
:param group_id: 群号 :param group_id: 群号
""" """
query = cls.query.where(cls.group_id == group_id) query = cls.query.where(cls.group_id == group_id)
@@ -35,9 +35,9 @@ class GroupInfo(db.Model):
group_flag: Optional[int] = None, group_flag: Optional[int] = None,
) -> bool: ) -> bool:
""" """
说明: 说明:
添加群信息 添加群信息
参数: 参数:
:param group_id: 群号 :param group_id: 群号
:param group_name: 群名称 :param group_name: 群名称
:param max_member_count: 群员最大数量 :param max_member_count: 群员最大数量
@@ -74,9 +74,9 @@ class GroupInfo(db.Model):
@classmethod @classmethod
async def delete_group_info(cls, group_id: int): async def delete_group_info(cls, group_id: int):
""" """
说明: 说明:
删除群信息 删除群信息
参数: 参数:
:param group_id: 群号 :param group_id: 群号
""" """
await cls.delete.where(cls.group_id == group_id).gino.status() await cls.delete.where(cls.group_id == group_id).gino.status()
@@ -84,7 +84,7 @@ class GroupInfo(db.Model):
@classmethod @classmethod
async def get_all_group(cls) -> List["GroupInfo"]: async def get_all_group(cls) -> List["GroupInfo"]:
""" """
说明: 说明:
获取所有群对象 获取所有群对象
""" """
query = await cls.query.gino.all() query = await cls.query.gino.all()
+42 -44
View File
@@ -10,41 +10,41 @@ class GroupInfoUser(db.Model):
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
user_name = db.Column(db.Unicode(), nullable=False) user_name = db.Column(db.Unicode(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False) belonging_group = db.Column(db.BigInteger(), nullable=False)
user_join_time = db.Column(db.DateTime(), nullable=False) user_join_time = db.Column(db.DateTime(), nullable=False)
nickname = db.Column(db.Unicode()) nickname = db.Column(db.Unicode())
uid = db.Column(db.BigInteger()) uid = db.Column(db.BigInteger())
_idx1 = db.Index("info_group_users_idx1", "user_qq", "group_id", unique=True) _idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True)
@classmethod @classmethod
async def add_member_info( async def add_member_info(
cls, cls,
user_qq: int, user_qq: int,
group_id: int, belonging_group: int,
user_name: str, user_name: str,
user_join_time: datetime, user_join_time: datetime,
uid: Optional[int] = None, uid: Optional[int] = None,
) -> bool: ) -> bool:
""" """
说明: 说明:
添加群内用户信息 添加群内用户信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param belonging_group: 群号
:param user_name: 用户名称 :param user_name: 用户名称
:param user_join_time: 入群时间 :param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成) :param uid: 用户唯一 id(自动生成)
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
try: try:
if not await query.gino.first(): if not await query.gino.first():
await cls.create( await cls.create(
user_qq=user_qq, user_qq=user_qq,
user_name=user_name, user_name=user_name,
group_id=group_id, belonging_group=belonging_group,
user_join_time=user_join_time, user_join_time=user_join_time,
uid=uid uid=uid
) )
@@ -54,31 +54,31 @@ class GroupInfoUser(db.Model):
@classmethod @classmethod
async def get_member_info( async def get_member_info(
cls, user_qq: int, group_id: int cls, user_qq: int, belonging_group: int
) -> "GroupInfoUser": ) -> "GroupInfoUser":
""" """
说明: 说明:
查询群员信息 查询群员信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param belonging_group: 群号
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
return await query.gino.first() return await query.gino.first()
@classmethod @classmethod
async def delete_member_info(cls, user_qq: int, group_id: int) -> bool: async def delete_member_info(cls, user_qq: int, belonging_group: int) -> bool:
""" """
说明: 说明:
删除群员信息 删除群员信息
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param belonging_group: 群号
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
@@ -87,40 +87,40 @@ class GroupInfoUser(db.Model):
return True return True
else: else:
await cls.delete.where( await cls.delete.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
).gino.status() ).gino.status()
return True return True
except Exception: except Exception:
return False return False
@classmethod @classmethod
async def get_group_member_id_list(cls, group_id: int) -> List[int]: async def get_group_member_id_list(cls, belonging_group: int) -> List[int]:
""" """
说明: 说明:
获取该群所有用户qq 获取该群所有用户qq
参数: 参数:
:param group_id: 群号 :param belonging_group: 群号
""" """
member_list = [] member_list = []
query = cls.query.where((cls.group_id == group_id)) query = cls.query.where((cls.belonging_group == belonging_group))
for user in await query.gino.all(): for user in await query.gino.all():
member_list.append(user.user_qq) member_list.append(user.user_qq)
return member_list return member_list
@classmethod @classmethod
async def set_group_member_nickname( async def set_group_member_nickname(
cls, user_qq: int, group_id: int, nickname: str cls, user_qq: int, belonging_group: int, nickname: str
) -> bool: ) -> bool:
""" """
说明: 说明:
设置群员在该群内的昵称 设置群员在该群内的昵称
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param belonging_group: 群号
:param nickname: 昵称 :param nickname: 昵称
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
user = await query.with_for_update().gino.first() user = await query.with_for_update().gino.first()
if user: if user:
@@ -131,27 +131,25 @@ class GroupInfoUser(db.Model):
@classmethod @classmethod
async def get_user_all_group(cls, user_qq: int) -> List[int]: async def get_user_all_group(cls, user_qq: int) -> List[int]:
""" """
说明: 获取该用户所在的所有群聊
获取该用户所在的所有群聊 :param user_qq: 用户qq
参数:
:param user_qq: 用户qq
""" """
query = await cls.query.where(cls.user_qq == user_qq).gino.all() query = await cls.query.where(cls.user_qq == user_qq).gino.all()
if query: if query:
query = [x.group_id for x in query] query = [x.belonging_group for x in query]
return query return query
@classmethod @classmethod
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str: async def get_group_member_nickname(cls, user_qq: int, belonging_group: int) -> str:
""" """
说明: 说明:
获取用户在该群的昵称 获取用户在该群的昵称
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param belonging_group: 群号
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
user = await query.gino.first() user = await query.gino.first()
if user: if user:
@@ -165,12 +163,12 @@ class GroupInfoUser(db.Model):
return "" return ""
@classmethod @classmethod
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[str]: async def get_group_member_uid(cls, user_qq: int, belonging_group: int) -> Optional[str]:
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
user = await query.gino.first() user = await query.gino.first()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.group_id == 114514)).with_for_update() _max_uid = cls.query.where((cls.user_qq == 114514) & (cls.belonging_group == 114514)).with_for_update()
_max_uid_user = await _max_uid.gino.first() _max_uid_user = await _max_uid.gino.first()
_max_uid = _max_uid_user.uid _max_uid = _max_uid_user.uid
if not user or not user.uid: if not user or not user.uid:
@@ -180,9 +178,9 @@ class GroupInfoUser(db.Model):
return x.uid return x.uid
else: else:
if not user: if not user:
await GroupInfoUser.add_member_info(user_qq, group_id, '', datetime.min) await GroupInfoUser.add_member_info(user_qq, belonging_group, '', datetime.min)
user = await cls.query.where( user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
).gino.first() ).gino.first()
await user.update( await user.update(
uid=_max_uid + 1, uid=_max_uid + 1,
+20 -25
View File
@@ -1,5 +1,3 @@
from asyncpg import UniqueViolationError
from services.db_context import db from services.db_context import db
@@ -17,9 +15,9 @@ class LevelUser(db.Model):
@classmethod @classmethod
async def get_user_level(cls, user_qq: int, group_id: int) -> int: async def get_user_level(cls, user_qq: int, group_id: int) -> int:
""" """
说明: 说明:
获取用户在群内的等级 获取用户在群内的等级
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param group_id: 群号
""" """
@@ -35,9 +33,9 @@ class LevelUser(db.Model):
cls, user_qq: int, group_id: int, level: int, group_flag: int = 0 cls, user_qq: int, group_id: int, level: int, group_flag: int = 0
) -> bool: ) -> bool:
""" """
说明: 说明:
设置用户在群内的权限 设置用户在群内的权限
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param group_id: 群号
:param level: 权限等级 :param level: 权限等级
@@ -46,27 +44,24 @@ class LevelUser(db.Model):
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
try: if user is None:
if not user: await cls.create(
await cls.create( user_qq=user_qq,
user_qq=user_qq, group_id=group_id,
group_id=group_id, user_level=level,
user_level=level, group_flag=group_flag,
group_flag=group_flag, )
) return True
return True else:
else: await user.update(user_level=level, group_flag=group_flag).apply()
await user.update(user_level=level, group_flag=group_flag).apply()
return False
except UniqueViolationError:
return False return False
@classmethod @classmethod
async def delete_level(cls, user_qq: int, group_id: int) -> bool: async def delete_level(cls, user_qq: int, group_id: int) -> bool:
""" """
说明: 说明:
删除用户权限 删除用户权限
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param group_id: 群号
""" """
@@ -82,9 +77,9 @@ class LevelUser(db.Model):
@classmethod @classmethod
async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool: async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool:
""" """
说明: 说明:
检查用户权限等级是否大于 level 检查用户权限等级是否大于 level
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param group_id: 群号
:param level: 权限等级 :param level: 权限等级
@@ -112,9 +107,9 @@ class LevelUser(db.Model):
@classmethod @classmethod
async def is_group_flag(cls, user_qq: int, group_id: int) -> bool: async def is_group_flag(cls, user_qq: int, group_id: int) -> bool:
""" """
说明: 说明:
检测是否会被自动更新刷新权限 检测是否会被自动更新刷新权限
参数: 参数:
:param user_qq: qq号 :param user_qq: qq号
:param group_id: 群号 :param group_id: 群号
""" """
+14 -13
View File
@@ -8,7 +8,8 @@ class SignGroupUser(db.Model):
id = db.Column(db.Integer(), primary_key=True) id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False) belonging_group = db.Column(db.BigInteger(), nullable=False)
checkin_count = db.Column(db.Integer(), nullable=False) checkin_count = db.Column(db.Integer(), nullable=False)
checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False) checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False)
impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False) impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False)
@@ -19,29 +20,29 @@ class SignGroupUser(db.Model):
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0 db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
) )
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "group_id", unique=True) _idx1 = db.Index("sign_group_users_idx1", "user_qq", "belonging_group", unique=True)
@classmethod @classmethod
async def ensure( async def ensure(
cls, user_qq: int, group_id: int, for_update: bool = False cls, user_qq: int, belonging_group: int, for_update: bool = False
) -> "SignGroupUser": ) -> "SignGroupUser":
""" """
说明: 说明:
获取签到用户 获取签到用户
参数: 参数:
:param user_qq: 用户qq :param user_qq: 用户qq
:param group_id: 所在群聊 :param belonging_group: 所在群聊
:param for_update: 是否存在修改数据 :param for_update: 是否存在修改数据
""" """
query = cls.query.where( query = cls.query.where(
(cls.user_qq == user_qq) & (cls.group_id == group_id) (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
) )
if for_update: if for_update:
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
return user or await cls.create( return user or await cls.create(
user_qq=user_qq, user_qq=user_qq,
group_id=group_id, belonging_group=belonging_group,
checkin_count=0, checkin_count=0,
checkin_time_last=datetime.min, # 从未签到过 checkin_time_last=datetime.min, # 从未签到过
impression=0, impression=0,
@@ -78,22 +79,22 @@ class SignGroupUser(db.Model):
).apply() ).apply()
@classmethod @classmethod
async def get_all_impression(cls, group_id: int) -> "list, list, list": async def get_all_impression(cls, belonging_group: int) -> "list, list, list":
""" """
说明: 说明:
获取该群所有用户 id 及对应 好感度 获取该群所有用户 id 及对应 好感度
参数: 参数:
:param group_id: 群号 :param belonging_group: 群号
""" """
impression_list = [] impression_list = []
user_qq_list = [] user_qq_list = []
user_group = [] user_group = []
if group_id: if belonging_group:
query = cls.query.where(cls.group_id == group_id) query = cls.query.where(cls.belonging_group == belonging_group)
else: else:
query = cls.query query = cls.query
for user in await query.gino.all(): for user in await query.gino.all():
impression_list.append(user.impression) impression_list.append(user.impression)
user_qq_list.append(user.user_qq) user_qq_list.append(user.user_qq)
user_group.append(user.group_id) user_group.append(user.belonging_group)
return user_qq_list, impression_list, user_group return user_qq_list, impression_list, user_group
+3 -1
View File
@@ -1,4 +1,6 @@
from nonebot import on_regex from nonebot import on_regex
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.rule import to_me from nonebot.rule import to_me
from pathlib import Path from pathlib import Path
@@ -27,7 +29,7 @@ about = on_regex("^关于$", priority=5, block=True, rule=to_me())
@about.handle() @about.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
ver_file = Path() / '__version__' ver_file = Path() / '__version__'
version = None version = None
if ver_file.exists(): if ver_file.exists():
+10 -9
View File
@@ -1,14 +1,15 @@
import random
from nonebot import on_keyword
import os
from utils.message_builder import image from utils.message_builder import image
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from nonebot import on_command from nonebot import on_command
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.adapters.onebot.v11 import GroupMessageEvent from nonebot.typing import T_State
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from nonebot.adapters.cqhttp.permission import GROUP
from utils.utils import FreqLimiter from utils.utils import FreqLimiter
from configs.config import NICKNAME from configs.config import NICKNAME
import random
from nonebot import on_keyword
import os
__zx_plugin_name__ = "基本设置 [Hidden]" __zx_plugin_name__ = "基本设置 [Hidden]"
@@ -24,12 +25,12 @@ config_play_game = on_keyword({"打游戏"}, permission=GROUP, priority=1, block
@config_play_game.handle() @config_play_game.handle()
async def _(event: GroupMessageEvent): async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if not _flmt.check(event.group_id): if not _flmt.check(event.group_id):
return return
_flmt.start_cd(event.group_id) _flmt.start_cd(event.group_id)
await config_play_game.finish( await config_play_game.finish(
image(random.choice(os.listdir(IMAGE_PATH / "dayouxi")), "dayouxi") image(random.choice(os.listdir(IMAGE_PATH + "dayouxi/")), "dayouxi")
) )
@@ -39,7 +40,7 @@ self_introduction = on_command(
@self_introduction.handle() @self_introduction.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
if NICKNAME.find('真寻') != -1: if NICKNAME.find('真寻') != -1:
result = ( result = (
"我叫绪山真寻\n" "我叫绪山真寻\n"
@@ -56,6 +57,6 @@ my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True)
@my_wife.handle() @my_wife.handle()
async def _(): async def _(bot: Bot, event: MessageEvent, state: T_State):
await my_wife.finish(image("laopo.jpg", "other")) await my_wife.finish(image("laopo.jpg", "other"))
+23 -19
View File
@@ -1,15 +1,17 @@
from nonebot import on_message from nonebot import on_message
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.cqhttp import (
Bot, Bot,
GroupMessageEvent, GroupMessageEvent,
Message, Message,
MessageEvent, MessageEvent,
) )
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State
from models.friend_user import FriendUser from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from services.log import logger from services.log import logger
from utils.utils import get_message_img, get_message_text from utils.utils import get_message_text, get_message_imgs
from .data_source import get_chat_result, hello, no_result from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
@@ -19,35 +21,40 @@ usage:
与{NICKNAME}普普通通的对话吧! 与{NICKNAME}普普通通的对话吧!
""" """
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = 'HibiKier'
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"cmd": ["Ai", "ai", "AI", "aI"], "cmd": ["Ai", "ai", "AI", "aI"],
} }
__plugin_configs__ = { __plugin_configs__ = {
"TL_KEY": {"value": [], "help": "图灵Key"}, "TL_KEY": {
"ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False}, "value": [],
"TEXT_FILTER": { "help": "图灵Key"
"value": ["鸡", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
}, },
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False
}
} }
Config.add_plugin_config( Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token" "alapi",
"ALAPI_TOKEN",
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
) )
ai = on_message(rule=to_me(), priority=998) ai = on_message(rule=to_me(), priority=8)
@ai.handle() @ai.handle()
async def _(bot: Bot, event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()) msg = get_message_text(event.json())
img = get_message_img(event.json()) imgs = get_message_imgs(event.json())
if "CQ:xml" in str(event.get_message()): if "CQ:xml" in str(event.get_message()):
return return
# 打招呼 # 打招呼
if (not msg and not img) or msg in [ if (not msg and not imgs) or msg in [
"你好啊", "你好啊",
"你好", "你好",
"在吗", "在吗",
@@ -58,7 +65,7 @@ async def _(bot: Bot, event: MessageEvent):
"在", "在",
]: ]:
await ai.finish(hello()) await ai.finish(hello())
img = img[0] if img else "" img = imgs[0] if imgs else ""
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname( nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id event.user_id, event.group_id
@@ -67,7 +74,7 @@ async def _(bot: Bot, event: MessageEvent):
nickname = await FriendUser.get_friend_nickname(event.user_id) nickname = await FriendUser.get_friend_nickname(event.user_id)
if not nickname: if not nickname:
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
nickname = event.sender.card or event.sender.nickname nickname = event.sender.card if event.sender.card else event.sender.nickname
else: else:
nickname = event.sender.nickname nickname = event.sender.nickname
result = await get_chat_result(msg, img, event.user_id, nickname) result = await get_chat_result(msg, img, event.user_id, nickname)
@@ -76,9 +83,6 @@ async def _(bot: Bot, event: MessageEvent):
f"问题:{msg} ---- 回答:{result}" f"问题:{msg} ---- 回答:{result}"
) )
if result: if result:
result = str(result)
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result)) await ai.finish(Message(result))
else: else:
await ai.finish(no_result()) await ai.finish(no_result())
+33 -37
View File
@@ -20,7 +20,7 @@ check_url = "https://v2.alapi.cn/api/censor/text"
index = 0 index = 0
anime_data = json.load(open(DATA_PATH / "anime.json", "r", encoding="utf8")) anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8"))
async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str: async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str:
@@ -57,7 +57,7 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if random.random() < 0.2: if random.random() < 0.2:
if nickname.find("大人") == -1: if nickname.find("大人") == -1:
nickname += "大~人~" nickname += "大~人~"
rst = str(rst).replace("小主人", nickname).replace("小朋友", nickname) rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst) ai_message_manager.add_result(user_id, rst)
return rst return rst
@@ -126,39 +126,35 @@ async def xie_ai(text: str) -> str:
""" """
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}") res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
content = "" content = ""
try: data = json.loads(res.text)
data = json.loads(res.text) if data["result"] == 0:
if data["result"] == 0: content = data["content"]
content = data["content"] if "菲菲" in content:
if "菲菲" in content: content = content.replace("菲菲", NICKNAME)
content = content.replace("菲菲", NICKNAME) if "艳儿" in content:
if "艳儿" in content: content = content.replace("艳儿", NICKNAME)
content = content.replace("艳儿", NICKNAME) if "公众号" in content:
if "公众号" in content: content = ""
content = "" if "{br}" in content:
if "{br}" in content: content = content.replace("{br}", "\n")
content = content.replace("{br}", "\n") if "提示" in content:
if "提示" in content: content = content[: content.find("提示")]
content = content[: content.find("提示")] if "淘宝" in content:
if "淘宝" in content or "taobao.com" in content: return ""
return "" while True:
while True: r = re.search("{face:(.*)}", content)
r = re.search("{face:(.*)}", content) if r:
if r: id_ = r.group(1)
id_ = r.group(1) content = content.replace(
content = content.replace( "{" + f"face:{id_}" + "}", str(face(int(id_)))
"{" + f"face:{id_}" + "}", str(face(int(id_))) )
) else:
else: break
break return (
return ( content
content if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") else await check_text(content)
else await check_text(content) )
)
except Exception as e:
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
return ""
def hello() -> str: def hello() -> str:
@@ -174,7 +170,7 @@ def hello() -> str:
"呼呼,叫俺干嘛", "呼呼,叫俺干嘛",
) )
) )
img = random.choice(os.listdir(IMAGE_PATH / "zai")) img = random.choice(os.listdir(IMAGE_PATH + "zai/"))
if img[-4:] == ".gif": if img[-4:] == ".gif":
result += image(img, "zai") result += image(img, "zai")
else: else:
@@ -197,7 +193,7 @@ def no_result() -> str:
"我!不!知!道!", "我!不!知!道!",
] ]
) )
+ image(random.choice(os.listdir(IMAGE_PATH / "noresult")), "noresult") + image(random.choice(os.listdir(IMAGE_PATH + "noresult/")), "noresult")
) )
-26
View File
@@ -1,26 +0,0 @@
from typing import Optional, Tuple, Union
from configs.config import Config
from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict, str], int]:
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
if data["code"] == 101:
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
+7 -6
View File
@@ -1,6 +1,7 @@
from nonebot import on_regex from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from ._data_source import get_data from nonebot.typing import T_State
from .data_source import get_data
from services.log import logger from services.log import logger
__zx_plugin_name__ = "网易云热评" __zx_plugin_name__ = "网易云热评"
@@ -22,8 +23,8 @@ __plugin_settings__ = {
} }
comments_163 = on_regex( comments_163 = on_command(
"^(网易云热评|网易云评论|到点了|12点了)$", priority=5, block=True "网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True
) )
@@ -31,7 +32,7 @@ comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle() @comments_163.handle()
async def _(event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(comments_163_url) data, code = await get_data(comments_163_url)
if code != 200: if code != 200:
await comments_163.finish(data, at_sender=True) await comments_163.finish(data, at_sender=True)
+6 -5
View File
@@ -1,8 +1,9 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from utils.message_builder import image from utils.message_builder import image
from nonebot.params import CommandArg from utils.utils import get_message_text
from ._data_source import get_data from .data_source import get_data
from services.log import logger from services.log import logger
__zx_plugin_name__ = "b封面" __zx_plugin_name__ = "b封面"
@@ -31,8 +32,8 @@ cover_url = "https://v2.alapi.cn/api/bilibili/cover"
@cover.handle() @cover.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip() msg = get_message_text(event.json())
params = {"c": msg} params = {"c": msg}
data, code = await get_data(cover_url, params) data, code = await get_data(cover_url, params)
if code != 200: if code != 200:
+49
View File
@@ -0,0 +1,49 @@
from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional
from configs.config import Config
from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+8 -5
View File
@@ -1,7 +1,10 @@
from nonebot import on_regex from nonebot import on_command
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from ._data_source import get_data from nonebot.typing import T_State
from utils.http_utils import AsyncHttpx
from configs.config import Config
from .data_source import get_data
__zx_plugin_name__ = "鸡汤" __zx_plugin_name__ = "鸡汤"
@@ -25,11 +28,11 @@ __plugin_settings__ = {
url = "https://v2.alapi.cn/api/soul" url = "https://v2.alapi.cn/api/soul"
jitang = on_regex("^毒?鸡汤$", priority=5, block=True) jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True)
@jitang.handle() @jitang.handle()
async def _(event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
try: try:
data, code = await get_data(url) data, code = await get_data(url)
if code != 200: if code != 200:
+4 -3
View File
@@ -1,7 +1,8 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger from services.log import logger
from ._data_source import get_data from .data_source import get_data
__zx_plugin_name__ = "古诗" __zx_plugin_name__ = "古诗"
__plugin_usage__ = """usage: __plugin_usage__ = """usage:
@@ -26,7 +27,7 @@ poetry_url = "https://v2.alapi.cn/api/shici"
@poetry.handle() @poetry.handle()
async def _(event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(poetry_url) data, code = await get_data(poetry_url)
if code != 200: if code != 200:
await poetry.finish(data, at_sender=True) await poetry.finish(data, at_sender=True)
+71
View File
@@ -0,0 +1,71 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from .data_source import get_data, gen_wbtop_pic
from utils.utils import get_message_text, is_number
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncPlaywright
import asyncio
__zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """
usage:
在QQ上吃个瓜
指令:
微博热搜:发送实时热搜
微博热搜 [id]:截图该热搜页面
示例:微博热搜 5
""".strip()
__plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["微博热搜"],
}
wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = "https://v2.alapi.cn/api/new/wbtop"
wbtop_data = []
@wbtop.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
global wbtop_data
msg = get_message_text(event.json())
if not wbtop_data or not msg:
data, code = await get_data(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data["data"]
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]["url"]
try:
await wbtop.send("开始截取数据...")
img = await AsyncPlaywright.screenshot(
url,
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
"#pl_feedlist_index",
sleep=5
)
await wbtop.send(img)
except Exception as e:
logger.error(f"微博热搜截图出错... {type(e)}: {e}")
await wbtop.send("发生了一些错误.....")
+52 -92
View File
@@ -1,9 +1,6 @@
from nonebot import on_command, on_regex from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent, Message from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from utils.image_utils import text2image
from utils.message_builder import image
from .data_source import ( from .data_source import (
add_live_sub, add_live_sub,
delete_sub, delete_sub,
@@ -12,16 +9,14 @@ from .data_source import (
get_media_id, get_media_id,
get_sub_status, get_sub_status,
SubManager, SubManager,
BilibiliSub, BilibiliSub
) )
from models.level_user import LevelUser from models.level_user import LevelUser
from utils.manager import group_manager
from configs.config import Config from configs.config import Config
from utils.utils import is_number, scheduler, get_bot from utils.utils import get_message_text, is_number, scheduler, get_bot
from typing import Optional, Tuple, Any from typing import Optional
from services.log import logger from services.log import logger
from nonebot import Driver from nonebot import Driver
from nonebot.params import CommandArg, ArgStr, RegexGroup
import nonebot import nonebot
__zx_plugin_name__ = "B站订阅" __zx_plugin_name__ = "B站订阅"
@@ -42,7 +37,7 @@ usage:
__plugin_des__ = "非常便利的B站订阅通知" __plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"] __plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier & NumberSir" __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
@@ -54,22 +49,12 @@ __plugin_configs__ = {
"value": 5, "value": 5,
"help": "群内bilibili订阅需要管理的权限", "help": "群内bilibili订阅需要管理的权限",
"default_value": 5, "default_value": 5,
}, }
"LIVE_MSG_AT_ALL": {
"value": False,
"help": "直播提醒是否AT全体(仅在真寻是管理员时生效)",
"default_value": False,
},
"UP_MSG_AT_ALL": {
"value": False,
"help": "UP动态投稿提醒是否AT全体(仅在真寻是管理员时生效)",
"default_value": False,
},
} }
add_sub = on_command("添加订阅", priority=5, block=True) add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_regex(r"^删除订阅(\d+)$", priority=5, block=True) del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_regex("^查看订阅$", priority=5, block=True) show_sub_info = on_command("查看订阅", priority=5, block=True)
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
@@ -83,9 +68,18 @@ async def _():
sub_manager = SubManager() sub_manager = SubManager()
@add_sub.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
season_data = state["season_data"]
msg = get_message_text(event.json())
if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
await add_sub.reject("Id必须为数字且在范围内!请重新输入...")
state["id"] = season_data[int(msg) - 1]["media_id"]
@add_sub.handle() @add_sub.handle()
async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = arg.extract_plain_text().strip().split() msg = get_message_text(event.json()).split()
if len(msg) < 2: if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...") await add_sub.finish("参数不完全,请查看订阅帮助...")
sub_type = msg[0] sub_type = msg[0]
@@ -126,22 +120,11 @@ async def _(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
state["id"] = int(id_) state["id"] = int(id_)
@add_sub.got("sub_type")
@add_sub.got("sub_user")
@add_sub.got("id") @add_sub.got("id")
async def _( async def _(bot: Bot, event: MessageEvent, state: T_State):
event: MessageEvent, sub_type = state["sub_type"]
state: T_State, sub_user = state["sub_user"]
id_: str = ArgStr("id"), id_ = state["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"]
id_ = int(id_)
if sub_type in ["主播", "直播"]: if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user)) await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]: elif sub_type.lower() in ["up", "用户"]:
@@ -158,10 +141,12 @@ async def _(
@del_sub.handle() @del_sub.handle()
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()): async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = reg_group[0] msg = get_message_text(event.json())
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
id_ = ( id_ = (
f"{event.group_id}" f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent) if isinstance(event, GroupMessageEvent)
else f"{event.user_id}" else f"{event.user_id}"
) )
@@ -177,11 +162,12 @@ async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
@show_sub_info.handle() @show_sub_info.handle()
async def _(event: MessageEvent): async def _(bot: Bot, event: MessageEvent, state: T_State):
if isinstance(event, GroupMessageEvent): id_ = (
id_ = f"{event.group_id}" f"{event.user_id}:{event.group_id}"
else: if isinstance(event, GroupMessageEvent)
id_ = f"{event.user_id}" else f"{event.user_id}"
)
data = await BilibiliSub.get_sub_data(id_) data = await BilibiliSub.get_sub_data(id_)
live_rst = "" live_rst = ""
up_rst = "" up_rst = ""
@@ -195,7 +181,6 @@ async def _(event: MessageEvent):
up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n" up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n"
if x.sub_type == "season": if x.sub_type == "season":
season_rst += ( season_rst += (
f"\t番剧id:{x.sub_id}\n"
f"\t番名:{x.season_name}\n" f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n" f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n" f"------------------\n"
@@ -204,18 +189,8 @@ async def _(event: MessageEvent):
up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst: if not live_rst and not up_rst and not season_rst:
live_rst = ( live_rst = "您目前没有任何订阅..."
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..." await show_sub_info.send(live_rst + up_rst + season_rst)
)
await show_sub_info.send(
image(
b64=(
await text2image(
live_rst + up_rst + season_rst, padding=10, color="#f9f6f2"
)
).pic2bs4()
)
)
# 推送 # 推送
@@ -227,18 +202,18 @@ async def _():
bot = get_bot() bot = get_bot()
sub = None sub = None
if bot: if bot:
# try: try:
await sub_manager.reload_sub_data() await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data() sub = await sub_manager.random_sub_data()
if sub: if sub:
logger.debug(f"Bilibili订阅开始检测:{sub.sub_id}") logger.info(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type) rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot) await send_sub_msg(rst, sub, bot)
# except Exception as e: if sub.sub_type == "live":
# logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}") rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot): async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
@@ -248,28 +223,13 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
:param sub: BilibiliSub :param sub: BilibiliSub
:param bot: Bot :param bot: Bot
""" """
temp_group = []
if rst: if rst:
for x in sub.sub_users.split(",")[:-1]: for x in sub.sub_users.split(",")[:-1]:
try: try:
if ":" in x and x.split(":")[1] not in temp_group: if ":" in x:
group_id = int(x.split(":")[1]) await bot.send_group_msg(
temp_group.append(group_id) group_id=int(x.split(":")[1]), message=Message(rst)
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: else:
await bot.send_private_msg(user_id=int(x), message=Message(rst)) await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e: except Exception as e:
+88 -129
View File
@@ -1,18 +1,14 @@
from bilireq.exceptions import ResponseCodeError from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.manager import resources_manager from utils.manager import resources_manager
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from utils.utils import get_bot
from .model import BilibiliSub from .model import BilibiliSub
from bilireq.live import get_room_info_by_id from bilibili_api.live import LiveRoom
from .utils import get_meta from bilibili_api import bangumi
from utils.message_builder import image from utils.message_builder import image
from bilireq.user import get_user_info from bilibili_api.user import User
from bilireq import dynamic from bilibili_api import user
from .utils import get_videos from typing import Optional
from typing import Optional, Tuple from pathlib import Path
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from datetime import datetime from datetime import datetime
from utils.browser import get_browser from utils.browser import get_browser
@@ -24,7 +20,7 @@ import random
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2" bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = IMAGE_PATH / "bilibili_sub" / "dynamic" dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True) dynamic_path.mkdir(exist_ok=True, parents=True)
@@ -39,35 +35,36 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
:return: :return:
""" """
try: try:
try: async with db.transaction():
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法""" try:
live_info = await get_room_info_by_id(live_id) live = LiveRoom(live_id)
except ResponseCodeError: live_info = (await live.get_room_info())["room_info"]
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确" except ResponseCodeException:
uid = live_info["uid"] return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
room_id = live_info["room_id"] uid = live_info["uid"]
short_id = live_info["short_id"] room_id = live_info["room_id"]
title = live_info["title"] short_id = live_info["short_id"]
live_status = live_info["live_status"] title = live_info["title"]
if await BilibiliSub.add_bilibili_sub( live_status = live_info["live_status"]
room_id, if await BilibiliSub.add_bilibili_sub(
"live", room_id,
sub_user, "live",
uid=uid, sub_user,
live_short_id=short_id, uid=uid,
live_status=live_status, live_short_id=short_id,
): live_status=live_status,
await _get_up_status(room_id) ):
uname = (await BilibiliSub.get_sub(room_id)).uname await _get_up_status(live_id)
return ( uname = (await BilibiliSub.get_sub(live_id)).uname
"已成功订阅主播:\n" return (
f"\ttitle:{title}\n" "已成功订阅主播:\n"
f"\tname: {uname}\n" f"\ttitle:{title}\n"
f"\tlive_id:{room_id}\n" f"\tname: {uname}\n"
f"\tuid:{uid}" f"\tlive_id:{live_id}\n"
f"\tuid:{uid}"
) )
else: else:
return "添加订阅失败..." return "添加订阅失败..."
except Exception as e: except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}") logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..." return "添加订阅失败..."
@@ -82,18 +79,16 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
try: try:
async with db.transaction(): async with db.transaction():
try: try:
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法""" u = user.User(uid)
user_info = await get_user_info(uid) user_info = await u.get_user_info()
except ResponseCodeError: except ResponseCodeException:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确" return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"] uname = user_info["name"]
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法""" dynamic_info = await u.get_dynamics(0)
dynamic_info = await dynamic.get_user_dynamics(uid)
dynamic_upload_time = 0 dynamic_upload_time = 0
if dynamic_info.get("cards"): if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法""" video_info = await u.get_videos()
video_info = await get_videos(uid)
latest_video_created = 0 latest_video_created = 0
if video_info["list"].get("vlist"): if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"] latest_video_created = video_info["list"]["vlist"][0]["created"]
@@ -123,9 +118,8 @@ async def add_season_sub(media_id: int, sub_user: str) -> str:
try: try:
async with db.transaction(): async with db.transaction():
try: try:
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法""" season_info = await bangumi.get_meta(media_id)
season_info = await get_meta(media_id) except ResponseCodeException:
except ResponseCodeError:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确" return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"] season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"] season_current_episode = season_info["media"]["new_ep"]["index"]
@@ -171,9 +165,7 @@ async def get_media_id(keyword: str) -> dict:
for _ in range(3): for _ in range(3):
try: try:
_season_data = {} _season_data = {}
response = await AsyncHttpx.get( response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5)
bilibili_search_url, params=params, timeout=5
)
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
if data.get("data"): if data.get("data"):
@@ -199,6 +191,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
获取订阅状态 获取订阅状态
:param id_: 订阅 id :param id_: 订阅 id
:param sub_type: 订阅类型 :param sub_type: 订阅类型
:return:
""" """
try: try:
if sub_type == "live": if sub_type == "live":
@@ -207,10 +200,8 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
return await _get_up_status(id_) return await _get_up_status(id_)
elif sub_type == "season": elif sub_type == "season":
return await _get_season_status(id_) return await _get_season_status(id_)
except ResponseCodeError: except ResponseCodeException:
logger.error(f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试...") return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
return None
# return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e: # except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}") # logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....." # return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -221,18 +212,17 @@ async def _get_live_status(id_: int) -> Optional[str]:
获取直播订阅状态 获取直播订阅状态
:param id_: 直播间 id :param id_: 直播间 id
""" """
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法""" live = LiveRoom(id_)
live_info = await get_room_info_by_id(id_) live_info = (await live.get_room_info())["room_info"]
title = live_info["title"] title = live_info["title"]
room_id = live_info["room_id"] room_id = live_info["room_id"]
live_status = live_info["live_status"] live_status = live_info["live_status"]
cover = live_info["user_cover"] cover = live_info["cover"]
sub = await BilibiliSub.get_sub(id_) sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status: if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status) await BilibiliSub.update_sub_info(id_, live_status=live_status)
if sub.live_status == 0 and live_status == 1: if sub.live_status == 0 and live_status == 1:
return ( return (
f""
f"{image(cover)}\n" f"{image(cover)}\n"
f"{sub.uname} 开播啦!\n" f"{sub.uname} 开播啦!\n"
f"标题:{title}\n" f"标题:{title}\n"
@@ -244,35 +234,28 @@ async def _get_live_status(id_: int) -> Optional[str]:
async def _get_up_status(id_: int) -> Optional[str]: async def _get_up_status(id_: int) -> Optional[str]:
""" """
获取用户投稿状态 获取用户投稿状态
:param id_: 订阅 id :param id_: 用户 id
:return: :return:
""" """
_user = await BilibiliSub.get_sub(id_) _user = await BilibiliSub.get_sub(id_)
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法""" u = user.User(_user.uid)
user_info = await get_user_info(_user.uid) user_info = await u.get_user_info()
uname = user_info["name"] uname = user_info["name"]
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法""" video_info = await u.get_videos()
video_info = await get_videos(_user.uid)
latest_video_created = 0 latest_video_created = 0
video = None video = None
dividing_line = "\n-------------\n"
if _user.uname != uname: if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname) await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time, link = await get_user_dynamic(_user.uid, _user) dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
if video_info["list"].get("vlist"): if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0] video = video_info["list"]["vlist"][0]
latest_video_created = video["created"] latest_video_created = video["created"]
rst = "" rst = ""
if dynamic_img: if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time) await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n{link}" rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if ( if _user.latest_video_created < latest_video_created and video:
latest_video_created rst = rst + "-------------\n" if rst else rst
and _user.latest_video_created
and video
and _user.latest_video_created < latest_video_created
):
rst = rst + dividing_line if rst else rst
await BilibiliSub.update_sub_info( await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created id_, latest_video_created=latest_video_created
) )
@@ -283,7 +266,7 @@ async def _get_up_status(id_: int) -> Optional[str]:
f'Bvid:{video["bvid"]}\n' f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}' f'直链:https://www.bilibili.com/video/{video["bvid"]}'
) )
rst = None if rst == dividing_line else rst rst = None if rst == "-------------\n" else rst
return rst return rst
@@ -292,8 +275,7 @@ async def _get_season_status(id_) -> Optional[str]:
获取 番剧 更新状态 获取 番剧 更新状态
:param id_: 番剧 id :param id_: 番剧 id
""" """
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法""" season_info = await bangumi.get_meta(id_)
season_info = await get_meta(id_)
title = season_info["media"]["title"] title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode _idx = (await BilibiliSub.get_sub(id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"] new_ep = season_info["media"]["new_ep"]["index"]
@@ -310,73 +292,50 @@ async def _get_season_status(id_) -> Optional[str]:
async def get_user_dynamic( async def get_user_dynamic(
uid: int, local_user: BilibiliSub u: User, local_user: BilibiliSub
) -> Tuple[Optional[MessageSegment], int, str]: ) -> "Optional[MessageSegment], int":
""" """
获取用户动态 获取用户动态
:param uid: 用户uid :param u: 用户类
:param local_user: 数据库存储的用户数据 :param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间 :return: 最新动态截图与时间
""" """
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法""" dynamic_info = await u.get_dynamics(0)
dynamic_info = await dynamic.get_user_dynamics(uid)
browser = await get_browser() browser = await get_browser()
if dynamic_info.get("cards") and browser: if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
dynamic_id = dynamic_info["cards"][0]["desc"]["dynamic_id"]
if local_user.dynamic_upload_time < dynamic_upload_time: if local_user.dynamic_upload_time < dynamic_upload_time:
context = await browser.new_context() page = await browser.new_page()
page = await context.new_page() await page.goto(
try: f"https://space.bilibili.com/{local_user.uid}/dynamic",
await page.goto( wait_until="networkidle",
f"https://t.bilibili.com/{dynamic_id}", timeout=10000,
wait_until="networkidle", )
timeout=10000, await page.set_viewport_size({"width": 2560, "height": 1080})
) # 删除置顶
# await page.set_viewport_size({"width": 2560, "height": 1080, "timeout": 10000*20}) # timeout: 200s await page.evaluate(
# 删除置顶
# await page.evaluate(
# """
# xs = document.getElementsByClassName('bili-dyn-item__tag');
# for (x of xs) {
# x.parentNode.parentNode.remove();
# }
# """
# )
# async with page.expect_popup() as popup_info:
# await page.locator(".bili-rich-text__content").click()
# details_page = await popup_info.value
await page.set_viewport_size(
{"width": 2560, "height": 1080, "timeout": 10000 * 20}
)
await page.wait_for_selector(".panel-area")
await page.evaluate(
"""
xs = document.getElementById('internationalHeader');
xs.remove();
xs = document.getElementsByClassName('panel-area')
xs[0].remove();
""" """
) xs = document.getElementsByClassName('first-card-with-title');
card = page.locator(".detail-card") for (x of xs) {
await card.wait_for() x.remove();
await card.screenshot( }
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", """
) )
except Exception as e: card = await page.query_selector(".card")
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}") # 截图并保存
finally: await card.screenshot(
await context.close() path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
await page.close() timeout=100000,
)
await page.close()
return ( return (
image( image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg", f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
"bilibili_sub/dynamic", "bilibili_sub/dynamic",
), ),
dynamic_upload_time, dynamic_upload_time,
f"https://t.bilibili.com/{dynamic_id}"
) )
return None, 0, '' return None, None
class SubManager: class SubManager:
+46 -45
View File
@@ -47,9 +47,9 @@ class BilibiliSub(db.Model):
season_update_time: Optional[datetime] = None, season_update_time: Optional[datetime] = None,
) -> bool: ) -> bool:
""" """
说明: 说明:
添加订阅 添加订阅
参数: 参数:
:param sub_id: 订阅名称,房间号,番剧号等 :param sub_id: 订阅名称,房间号,番剧号等
:param sub_type: 订阅类型 :param sub_type: 订阅类型
:param sub_user: 订阅此条目的用户 :param sub_user: 订阅此条目的用户
@@ -65,43 +65,44 @@ class BilibiliSub(db.Model):
:param season_update_time: 番剧更新时间 :param season_update_time: 番剧更新时间
""" """
try: try:
query = ( async with db.transaction():
await cls.query.where(cls.sub_id == sub_id) query = (
.with_for_update() await cls.query.where(cls.sub_id == sub_id)
.gino.first() .with_for_update()
) .gino.first()
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
) )
await sub.update( sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
live_short_id=live_short_id if query:
if live_short_id if sub_user not in query.sub_users:
else sub.live_short_id, sub_users = query.sub_users + sub_user
live_status=live_status if live_status else sub.live_status, await query.update(sub_users=sub_users).apply()
dynamic_upload_time=dynamic_upload_time else:
if dynamic_upload_time sub = await cls.create(
else sub.dynamic_upload_time, sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
uid=uid if uid else sub.uid, )
uname=uname if uname else sub.uname, await sub.update(
latest_video_created=latest_video_created live_short_id=live_short_id
if latest_video_created if live_short_id
else sub.latest_video_created, else sub.live_short_id,
season_update_time=season_update_time live_status=live_status if live_status else sub.live_status,
if season_update_time dynamic_upload_time=dynamic_upload_time
else sub.season_update_time, if dynamic_upload_time
season_current_episode=season_current_episode else sub.dynamic_upload_time,
if season_current_episode uid=uid if uid else sub.uid,
else sub.season_current_episode, uname=uname if uname else sub.uname,
season_id=season_id if season_id else sub.season_id, latest_video_created=latest_video_created
season_name=season_name if season_name else sub.season_name, if latest_video_created
).apply() else sub.latest_video_created,
return True season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
except Exception as e: except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}") logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False return False
@@ -109,9 +110,9 @@ class BilibiliSub(db.Model):
@classmethod @classmethod
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool: async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool:
""" """
说明: 说明:
删除订阅 删除订阅
参数: 参数:
:param sub_id: 订阅名称 :param sub_id: 订阅名称
:param sub_user: 删除此条目的用户 :param sub_user: 删除此条目的用户
""" """
@@ -139,9 +140,9 @@ class BilibiliSub(db.Model):
@classmethod @classmethod
async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]: async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]:
""" """
说明: 说明:
获取订阅对象 获取订阅对象
参数: 参数:
:param sub_id: 订阅 id :param sub_id: 订阅 id
""" """
return await cls.query.where(cls.sub_id == sub_id).gino.first() return await cls.query.where(cls.sub_id == sub_id).gino.first()
@@ -172,9 +173,9 @@ class BilibiliSub(db.Model):
season_update_time: Optional[datetime] = None, season_update_time: Optional[datetime] = None,
) -> bool: ) -> bool:
""" """
说明: 说明:
更新订阅信息 更新订阅信息
参数: 参数:
:param sub_id: 订阅名称,房间号,番剧号等 :param sub_id: 订阅名称,房间号,番剧号等
:param live_short_id: 直接短 id :param live_short_id: 直接短 id
:param live_status: 主播开播状态 :param live_status: 主播开播状态
@@ -231,7 +232,7 @@ class BilibiliSub(db.Model):
cls, cls,
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]": ) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
""" """
说明: 说明:
分类获取所有数据 分类获取所有数据
""" """
live_data = [] live_data = []
+6 -49
View File
@@ -1,12 +1,12 @@
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
# from bilibili_api import user from pathlib import Path
from bilireq.user import get_user_info from bilibili_api import user
from io import BytesIO from io import BytesIO
BORDER_PATH = IMAGE_PATH / "border" BORDER_PATH = Path(IMAGE_PATH) / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True) BORDER_PATH.mkdir(parents=True, exist_ok=True)
@@ -29,8 +29,8 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
:param des: 直播简介 :param des: 直播简介
:return: :return:
""" """
u = user.User(uid)
user_info = await get_user_info(uid) user_info = await u.get_user_info()
name = user_info["name"] name = user_info["name"]
sex = user_info["sex"] sex = user_info["sex"]
face = user_info["face"] face = user_info["face"]
@@ -38,6 +38,7 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
ava = BuildImage(100, 100, background=BytesIO(await get_pic(face))) ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover))) cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image( def _create_live_des_image(
@@ -68,47 +69,3 @@ def _create_live_des_image(
border_img = BuildImage(1772, 2657, background=border) border_img = BuildImage(1772, 2657, background=border)
bk = BuildImage(1772, 2657, font_size=30) bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width") bk.paste(cover, (0, 100), center_type="by_width")
async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
"""
根据番剧 ID 获取番剧元数据信息,
作为bilibili_api和bilireq的替代品。
如果bilireq.bangumi更新了,可以转为调用bilireq.bangumi的get_meta方法,两者完全一致。
"""
from bilireq.utils import get
BASE_URL = "https://api.bilibili.com"
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",
*, auth=None, reqtype="both", **kwargs
):
"""
获取用户投该视频信息
作为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 get
BASE_URL = "https://api.bilibili.com"
url = f"{BASE_URL}/x/space/arc/search"
params = {
"mid": uid,
"ps": 30,
"tid": tid,
"pn": pn,
"keyword": keyword,
"order": order
}
return await get(url, params=params, auth=auth, reqtype=reqtype, **kwargs)
-253
View File
@@ -1,253 +0,0 @@
from nonebot.adapters.onebot.v11 import (
Event,
MessageEvent,
GroupMessageEvent,
Message,
Bot,
)
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from utils.image_utils import BuildImage
from utils.manager import group_manager
from utils.utils import get_message_text, is_number
from nonebot.params import RegexGroup, CommandArg
from .utils import black_word_manager
from nonebot import on_command, on_message, on_regex
from configs.config import Config, NICKNAME
from nonebot.permission import SUPERUSER
from .data_source import show_black_text_image, set_user_punish
from services.log import logger
from models.ban_user import BanUser
from datetime import datetime
from utils.message_builder import image
from .model import BlackWord
from typing import Tuple, Any
__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] [id] [punish_level]
示例:记录名单
示例:记录名单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
)
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],
)
Config.add_plugin_config(
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True
)
# 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,
)
Config.add_plugin_config(
"black_word",
"BAN_3_DURATION",
7,
help_="Union[int, List[int, int]]Ban时长(天),三级惩罚,可以为指定数字或指定列表区间(随机),例如 [7, 30]",
default_value=360,
)
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,
)
Config.add_plugin_config(
"black_word",
"ADD_PUNISH_LEVEL_TO_COUNT",
3,
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
default_value=3,
)
Config.add_plugin_config(
"black_word",
"ALAPI_CHECK_FLAG",
False,
help_="当未检测到已收录的敏感词时,开启ALAPI文本检测并将疑似文本发送给超级用户",
default_value=False,
)
Config.add_plugin_config(
"black_word",
"CONTAIN_BLACK_STOP_PROPAGATION",
True,
help_="当文本包含任意敏感词时,停止向下级插件传递,即不触发ai",
default_value=True,
)
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,
):
if (
isinstance(event, MessageEvent)
and event.is_tome()
and matcher.plugin_name == "black_word"
and not await BanUser.is_ban(event.user_id)
and str(event.user_id) not in bot.config.superusers
and not get_message_text(event.json()).startswith("原神绑定")
):
# 屏蔽群权限-1的群
if isinstance(event, GroupMessageEvent) and group_manager.get_group_level(event.group_id) < 0:
return
user_id = event.user_id
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
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,
int(user_id.split(":")[1]) if user_id else None,
int(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 = int(msg[0])
id_ = int(msg[1])
punish_level = int(msg[2])
rst = await set_user_punish(uid, id_, punish_level)
await set_punish.send(rst)
logger.info(
f"USER {event.user_id} 设置惩罚 uid:{uid} id_:{id_} punish_level:{punish_level} --> {rst}"
)
-118
View File
@@ -1,118 +0,0 @@
from nonebot.adapters.onebot.v11 import Bot
from utils.image_utils import BuildImage, text2image
from services.log import logger
from typing import Optional
from datetime import datetime
from .model import BlackWord
from .utils import _get_punish, Config
async def show_black_text_image(
bot: Bot,
user: Optional[int],
group_id: Optional[int],
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, 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=x.group_id, user_id=x.user_qq
)
)["card"]
else:
user_name = [
u["nickname"] for u in friend_str if u["user_id"] == x.user_qq
][0]
except Exception as e:
logger.warning(
f"show_black_text_image 获取 USER {x.user_qq} user_name 失败 {type(e)}:{e}"
)
user_name = x.user_qq
id_str += f"{i}\n"
uname_str += f"{user_name}\n"
uid_str += f"{x.user_qq}\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: int, 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或敏感词"
-148
View File
@@ -1,148 +0,0 @@
from services.db_context import db
from typing import Optional, List
from datetime import datetime, timedelta
class BlackWord(db.Model):
__tablename__ = "black_word"
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
group_id = db.Column(db.BigInteger())
plant_text = db.Column(db.String())
black_word = db.Column(db.String())
punish = db.Column(db.String(), default="")
punish_level = db.Column(db.Integer())
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod
async def add_user_black_word(
cls,
user_qq: int,
group_id: Optional[int],
black_word: str,
plant_text: str,
punish_level: int,
):
"""
说明:
添加用户发送的敏感词
参数:
:param user_qq: 用户id
:param group_id: 群号
:param black_word: 黑名单词汇
:param plant_text: 消息文本
:param punish_level: 惩罚等级
"""
await cls.create(
user_qq=user_qq,
group_id=group_id,
plant_text=plant_text,
black_word=black_word,
punish_level=punish_level,
create_time=datetime.now(),
)
@classmethod
async def set_user_punish(
cls,
user_qq: int,
punish: str,
black_word: Optional[str] = None,
id_: Optional[int] = None,
) -> bool:
"""
说明:
设置处罚
参数:
:param user_qq: 用户id
:param punish: 处罚
:param black_word: 黑名单词汇
:param id_: 记录下标
"""
user = None
if (not black_word and not id_) or not punish:
return False
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
if black_word:
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
elif id_:
user_list = await query.gino.all()
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
return False
user = user_list[id_]
if not user:
return False
await user.update(punish=cls.punish + punish + " ").apply()
return True
@classmethod
async def get_user_count(
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
) -> int:
"""
说明:
获取用户规定周期内的犯事次数
参数:
:param user_qq: 用户qq
:param days: 周期天数
:param punish_level: 惩罚等级
"""
setattr(BlackWord, "count", db.func.count(cls.id).label("count"))
query = cls.select("count").where(
(cls.user_qq == user_qq)
& (cls.punish_level != -1)
& (cls.create_time > datetime.now() - timedelta(days=days))
)
if punish_level is not None:
query = query.where(cls.punish_level == punish_level)
return (await query.gino.first())[0]
@classmethod
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
"""
说明:
获取用户最近一次的惩罚记录等级
参数:
:param user_qq: 用户qq
:param days: 周期天数
"""
if (
query := await cls.query.where(cls.user_qq == user_qq)
.where(cls.create_time > datetime.now() - timedelta(days=days))
.order_by(cls.id.desc())
.gino.first()
):
return query.punish_level
return None
@classmethod
async def get_black_data(
cls,
user_qq: Optional[int],
group_id: Optional[int],
date: Optional[datetime],
date_type: str = "=",
) -> List["BlackWord"]:
"""
说明:
通过指定条件查询数据
参数:
:param user_qq: 用户qq
:param group_id: 群号
:param date: 日期
:param date_type: 日期查询类型
"""
query = cls.query
if user_qq:
query = query.where(cls.user_qq == user_qq)
if group_id:
query = query.where(cls.group_id == group_id)
if date:
if date_type == "=":
query = query.where(cls.create_time == date)
elif date_type == ">":
query = query.where(cls.create_time > date)
elif date_type == "<":
query = query.where(cls.create_time < date)
return await query.gino.all()
-322
View File
@@ -1,322 +0,0 @@
from utils.utils import cn2py, get_bot
from configs.path_config import DATA_PATH
from typing import Optional, Union, Tuple
from .model import BlackWord
from configs.config import Config
from pathlib import Path
from services.log import logger
from models.ban_user import BanUser
from nonebot.adapters.onebot.v11 import ActionFailed
from models.group_member_info import GroupInfoUser
from utils.http_utils import AsyncHttpx
import random
try:
import ujson as json
except ModuleNotFoundError:
import json
class BlackWordManager:
"""
敏感词管理( 拒绝恶意
"""
def __init__(self, word_file: Path, py_file: Path):
self._word_list = {
"1": [],
"2": [],
"3": [],
"4": ["sb", "nmsl", "mdzz", "2b", "jb", "操", "废物", "憨憨", "cnm", "rnm"],
"5": [],
}
self._py_list = {
"1": [],
"2": [],
"3": [],
"4": [
"shabi",
"wocaonima",
"sima",
"sabi",
"zhizhang",
"naocan",
"caonima",
"rinima",
"simadongxi",
"simawanyi",
"hanbi",
"hanpi",
"laji",
"fw"
],
"5": [],
}
word_file.parent.mkdir(parents=True, exist_ok=True)
if word_file.exists():
# 清空默认配置
with open(word_file, "r", encoding="utf8") as f:
self._word_list = json.load(f)
else:
with open(word_file, "w", encoding="utf8") as f:
json.dump(
self._word_list,
f,
ensure_ascii=False,
indent=4,
)
if py_file.exists():
# 清空默认配置
with open(py_file, "r", encoding="utf8") as f:
self._py_list = json.load(f)
else:
with open(py_file, "w", encoding="utf8") as f:
json.dump(
self._py_list,
f,
ensure_ascii=False,
indent=4,
)
async def check(
self, user_id: int, group_id: Optional[int], message: str
) -> Optional[Union[str, bool]]:
"""
检查是否包含黑名单词汇
:param user_id: 用户id
:param group_id: 群号
:param message: 消息
"""
if data := self._check(message):
if data[0]:
await _add_user_black_word(
user_id, group_id, data[0], message, int(data[1])
)
return True
if Config.get_config(
"black_word", "ALAPI_CHECK_FLAG"
) and not await check_text(message):
await send_msg(
0, None, f"USER {user_id} GROUP {group_id} ALAPI 疑似检测:{message}"
)
return False
def _check(self, message: str) -> Tuple[Optional[str], int]:
"""
检测文本是否违规
:param message: 检测消息
"""
# 移除空格
message = message.replace(" ", "")
py_msg = cn2py(message).lower()
# 完全匹配
for x in [self._word_list, self._py_list]:
for level in x:
if message in x[level] or py_msg in x[level]:
return message if message in x[level] else py_msg, level
# 模糊匹配
for x in [self._word_list, self._py_list]:
for level in x:
for m in x[level]:
if m in message or m in py_msg:
return m, -1
return None, 0
async def _add_user_black_word(
user_id: int,
group_id: Optional[int],
black_word: str,
message: str,
punish_level: int,
):
"""
添加敏感词数据
:param user_id: 用户id
:param group_id: 群号
:param black_word: 触发的黑名单词汇
:param message: 原始文本
:param punish_level: 惩罚等级
"""
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
# 周期内超过次数直接提升惩罚
if Config.get_config(
"black_word", "AUTO_ADD_PUNISH_LEVEL"
) and user_count > Config.get_config("black_word", "ADD_PUNISH_LEVEL_TO_COUNT"):
punish_level -= 1
await BlackWord.add_user_black_word(
user_id, group_id, black_word, message, punish_level
)
logger.info(
f"已将 USER {user_id} GROUP {group_id} 添加至黑名单词汇记录 Black_word:{black_word} Plant_text:{message}"
)
# 自动惩罚
if Config.get_config("black_word", "AUTO_PUNISH") and punish_level != -1:
await _punish_handle(user_id, group_id, punish_level, black_word)
async def _punish_handle(
user_id: int, group_id: Optional[int], punish_level: int, black_word: str
):
"""
惩罚措施,级别越低惩罚越严
:param user_id: 用户id
:param group_id: 群号
:param black_word: 触发的黑名单词汇
"""
logger.info(f"BlackWord USER {user_id} 触发 {punish_level} 级惩罚...")
# 周期天数
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
# 用户周期内触发punish_level级惩罚的次数
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
# 获取最近一次的惩罚等级,将在此基础上增加
punish_level = await BlackWord.get_user_punish_level(user_id, cycle_days) or punish_level
# 容忍次数:List[int]
tolerate_count = Config.get_config("black_word", "TOLERATE_COUNT")
if not tolerate_count or len(tolerate_count) < 5:
tolerate_count = [5, 2, 2, 2, 2]
if punish_level == 1 and user_count > tolerate_count[punish_level - 1]:
# 永久ban
await _get_punish(1, user_id, group_id)
await BlackWord.set_user_punish(user_id, "永久ban 删除好友", black_word)
elif punish_level == 2 and user_count > tolerate_count[punish_level - 1]:
# 删除好友
await _get_punish(2, user_id, group_id)
await BlackWord.set_user_punish(user_id, "删除好友", black_word)
elif punish_level == 3 and user_count > tolerate_count[punish_level - 1]:
# 永久ban
ban_day = await _get_punish(3, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"ban {ban_day} 天", black_word)
elif punish_level == 4 and user_count > tolerate_count[punish_level - 1]:
# ban指定时长
ban_time = await _get_punish(4, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"ban {ban_time} 分钟", black_word)
elif punish_level == 5 and user_count > tolerate_count[punish_level - 1]:
# 口头警告
warning_result = await _get_punish(5, user_id, group_id)
await BlackWord.set_user_punish(user_id, f"口头警告:{warning_result}", black_word)
else:
await BlackWord.set_user_punish(user_id, f"提示!", black_word)
await send_msg(
user_id,
group_id,
f"BlackWordChecker:该条发言已被记录,目前你在{cycle_days}天内的发表{punish_level}级"
f"言论记录次数为:{user_count}次,请注意你的发言\n"
f"* 如果你不清楚惩罚机制,请发送“惩罚机制” *",
)
async def _get_punish(
id_: int, user_id: int, group_id: Optional[int] = None
) -> Optional[Union[int, str]]:
"""
通过id_获取惩罚
:param id_: id
:param user_id: 用户id
:param group_id: 群号
"""
bot = get_bot()
# 忽略的群聊
# _ignore_group = Config.get_config("black_word", "IGNORE_GROUP")
# 处罚 id 4 ban 时间:int,List[int]
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION")
# 处罚 id 4 ban 时间:int,List[int]
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION")
# 口头警告内容
warning_result = Config.get_config("black_word", "WARNING_RESULT")
try:
uname = (await GroupInfoUser.get_member_info(user_id, group_id)).user_name
except AttributeError:
uname = user_id
# 永久ban
if id_ == 1:
if str(user_id) not in bot.config.superusers:
await BanUser.ban(user_id, 10, 99999999)
await send_msg(user_id, group_id, f"BlackWordChecker 永久ban USER {uname}({user_id})")
logger.info(f"BlackWord 永久封禁 USER {user_id}...")
# 删除好友(有的话
elif id_ == 2:
if str(user_id) not in bot.config.superusers:
try:
await bot.delete_friend(user_id=user_id)
await send_msg(
user_id, group_id, f"BlackWordChecker 删除好友 USER {uname}({user_id})"
)
logger.info(f"BlackWord 删除好友 {user_id}...")
except ActionFailed:
pass
# 封禁用户指定时间,默认7天
elif id_ == 3:
if isinstance(ban_3_duration, list):
ban_3_duration = random.randint(ban_3_duration[0], ban_3_duration[1])
await BanUser.ban(user_id, 9, ban_4_duration * 60 * 60 * 24)
await send_msg(
user_id,
group_id,
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_3_duration} 天处罚。",
)
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_3_duration} 天...")
return ban_3_duration
# 封禁用户指定时间,默认360分钟
elif id_ == 4:
if isinstance(ban_4_duration, list):
ban_4_duration = random.randint(ban_4_duration[0], ban_4_duration[1])
await BanUser.ban(user_id, 9, ban_4_duration * 60)
await send_msg(
user_id,
group_id,
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_4_duration} 分钟处罚。",
)
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_4_duration} 分钟...")
return ban_4_duration
# 口头警告
elif id_ == 5:
if group_id:
await bot.send_group_msg(group_id=group_id, message=warning_result)
else:
await bot.send_private_msg(user_id=user_id, message=warning_result)
logger.info(f"BlackWord 口头警告 USER {user_id}")
return warning_result
return None
async def send_msg(user_id: int, group_id: Optional[int], message: str):
"""
发送消息
:param user_id: user_id
:param group_id: group_id
:param message: message
"""
bot = get_bot()
if not user_id:
user_id = int(list(bot.config.superusers)[0])
if group_id:
await bot.send_group_msg(group_id=group_id, message=message)
else:
await bot.send_private_msg(user_id=user_id, message=message)
async def check_text(text: str) -> bool:
"""
ALAPI文本检测,检测输入违规
:param text: 回复
"""
if not Config.get_config("alapi", "ALAPI_TOKEN"):
return True
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
try:
data = (
await AsyncHttpx.get(
"https://v2.alapi.cn/api/censor/text", timeout=4, params=params
)
).json()
if data["code"] == 200:
return data["data"]["conclusion_type"] == 2
except Exception as e:
logger.error(f"检测违规文本错误...{type(e)}:{e}")
return True
black_word_manager = BlackWordManager(DATA_PATH / "black_word" / "black_word.json", DATA_PATH / "black_word" / "black_py.json")

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